From 93b718af3f4823f7f6382d81ab9909b6f2e6a14a Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Thu, 25 Jun 2020 20:19:26 -0400 Subject: [PATCH 01/15] Changes to CMakeLists.txt to build Manta on macOS. --- .gitignore | 12 +++++++++ CMakeLists.txt | 72 +++++++++++++++++++++++++------------------------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 9382ee6d..e7fa89d6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,15 @@ waveletNoiseTileDouble.bin build doc tensorflow/mantaGen/datasets + +# macos +.DS_Store + +# vscode +\.vscode + +# Python +venv +__pycache__ +.ipynb_checkpoints +*.ipynb diff --git a/CMakeLists.txt b/CMakeLists.txt index 642e720a..fbfd039c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,8 +33,8 @@ endif() if(APPLE) if(NOT CMAKE_PREFIX_PATH) - set(CMAKE_PREFIX_PATH "/usr/local/opt/qt5/") # mac/homebrew (version independent) - #set(CMAKE_PREFIX_PATH "/home/myname/qt/5.5/clang_64") # other... + #set(CMAKE_PREFIX_PATH "/usr/local/opt/qt5/") # mac/homebrew (version independent) + set(CMAKE_PREFIX_PATH "/Users/Cesar/Qt/5.15.0/clang_64/") endif() endif() @@ -437,41 +437,41 @@ endif(DOXYGEN_FOUND) # Python # note - if configuration fails, comment out the auto block below, and manually set the following two paths: -#set(PYTHON_INCLUDE_DIRS "PYTHON_PATH/include") -#set(PYTHON_LIBRARIES "PYTHON_PATH/libs/pythonX.Y.lib/so") +set(PYTHON_INCLUDE_DIRS "/Users/Cesar/anaconda3/include/python3.7m/") +set(PYTHON_LIBRARIES "/Users/Cesar/anaconda3/lib/libpython3.7m.dylib") # auto block -if (WIN32) - # convenience override for windows, typically the auto find doesnt work, so fall back to WIN_PYTHON_PATH - find_package(PythonLibs ${PYTHON_VER_ID} QUIET) - if(NOT PYTHONLIBS_FOUND) - set(PYTHON_INCLUDE_DIRS "${WIN_PYTHON_PATH}/include") - set(PYTHON_LIBRARIES "${WIN_PYTHON_PATH}/libs/python${PYTHON_VER_ID}.lib") - endif() -else() - # try to configure from python itself first - execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('INCLUDEPY'))" OUTPUT_VARIABLE PYTHON_INCLUDE_DIRS) - execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('LIBDIR'))" OUTPUT_VARIABLE PYTHON_LIB1) - if(APPLE) - # LDLIBRARY doesnt seem to work for Macs... - set(PYTHON_LIB2 "../Python") - else() - execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('LDLIBRARY'))" OUTPUT_VARIABLE PYTHON_LIB2) - endif() - set(PYTHON_LIBRARIES "${PYTHON_LIB1}/${PYTHON_LIB2}") - if(EXISTS "${PYTHON_INCLUDE_DIRS}" AND EXISTS "${PYTHON_LIBRARIES}") - message("Found python configuration") - else() - # otherwise, fall back to cmake find - if(NOT PYTHON_VERSION) - find_package(PythonLibs REQUIRED) # use any - else() - find_package(PythonLibs ${PYTHON_VER_ID} EXACT QUIET) # fixed version - endif() - if(NOT PYTHONLIBS_FOUND) - message(FATAL_ERROR "No python found, please manually set PYTHON_VERSION for cmake, or PYTHON_ paths in CMakeLists.txt") - endif() - endif() -endif() +# if (WIN32) +# # convenience override for windows, typically the auto find doesnt work, so fall back to WIN_PYTHON_PATH +# find_package(PythonLibs ${PYTHON_VER_ID} QUIET) +# if(NOT PYTHONLIBS_FOUND) +# set(PYTHON_INCLUDE_DIRS "${WIN_PYTHON_PATH}/include") +# set(PYTHON_LIBRARIES "${WIN_PYTHON_PATH}/libs/python${PYTHON_VER_ID}.lib") +# endif() +# else() +# # try to configure from python itself first +# execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('INCLUDEPY'))" OUTPUT_VARIABLE PYTHON_INCLUDE_DIRS) +# execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('LIBDIR'))" OUTPUT_VARIABLE PYTHON_LIB1) +# if(APPLE) +# # LDLIBRARY doesnt seem to work for Macs... +# set(PYTHON_LIB2 "../Python") +# else() +# execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('LDLIBRARY'))" OUTPUT_VARIABLE PYTHON_LIB2) +# endif() +# set(PYTHON_LIBRARIES "${PYTHON_LIB1}/${PYTHON_LIB2}") +# if(EXISTS "${PYTHON_INCLUDE_DIRS}" AND EXISTS "${PYTHON_LIBRARIES}") +# message("Found python configuration") +# else() +# # otherwise, fall back to cmake find +# if(NOT PYTHON_VERSION) +# find_package(PythonLibs REQUIRED) # use any +# else() +# find_package(PythonLibs ${PYTHON_VER_ID} EXACT QUIET) # fixed version +# endif() +# if(NOT PYTHONLIBS_FOUND) +# message(FATAL_ERROR "No python found, please manually set PYTHON_VERSION for cmake, or PYTHON_ paths in CMakeLists.txt") +# endif() +# endif() +# endif() # end auto block, python config done list(APPEND INCLUDE_PATHS ${PYTHON_INCLUDE_DIRS}) list(APPEND F_LIBS ${PYTHON_LIBRARIES}) From f11cd64800a65e4029ae3b02322bbe96e7b21463 Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Fri, 26 Jun 2020 02:01:15 -0400 Subject: [PATCH 02/15] Able to export the positions of the particles in a BasicParticleSystem. --- .gitignore | 2 +- CMakeLists.txt | 5 +- scenes/cell-growth/export-particles.py | 77 + scenes/cell-growth/points.ipynb | 140 ++ .../cell-growth/results/particles-frame-0.txt | 1405 +++++++++++++++++ .../cell-growth/results/particles-frame-1.txt | 1405 +++++++++++++++++ .../results/particles-frame-10.txt | 1405 +++++++++++++++++ .../cell-growth/results/particles-frame-2.txt | 1405 +++++++++++++++++ .../cell-growth/results/particles-frame-3.txt | 1405 +++++++++++++++++ .../cell-growth/results/particles-frame-4.txt | 1405 +++++++++++++++++ .../cell-growth/results/particles-frame-5.txt | 1405 +++++++++++++++++ .../cell-growth/results/particles-frame-6.txt | 1405 +++++++++++++++++ .../cell-growth/results/particles-frame-7.txt | 1405 +++++++++++++++++ .../cell-growth/results/particles-frame-8.txt | 1405 +++++++++++++++++ .../cell-growth/results/particles-frame-9.txt | 1405 +++++++++++++++++ scenes/cell-growth/view-particles.ipynb | 111 ++ 16 files changed, 15787 insertions(+), 3 deletions(-) create mode 100644 scenes/cell-growth/export-particles.py create mode 100644 scenes/cell-growth/points.ipynb create mode 100644 scenes/cell-growth/results/particles-frame-0.txt create mode 100644 scenes/cell-growth/results/particles-frame-1.txt create mode 100644 scenes/cell-growth/results/particles-frame-10.txt create mode 100644 scenes/cell-growth/results/particles-frame-2.txt create mode 100644 scenes/cell-growth/results/particles-frame-3.txt create mode 100644 scenes/cell-growth/results/particles-frame-4.txt create mode 100644 scenes/cell-growth/results/particles-frame-5.txt create mode 100644 scenes/cell-growth/results/particles-frame-6.txt create mode 100644 scenes/cell-growth/results/particles-frame-7.txt create mode 100644 scenes/cell-growth/results/particles-frame-8.txt create mode 100644 scenes/cell-growth/results/particles-frame-9.txt create mode 100644 scenes/cell-growth/view-particles.ipynb diff --git a/.gitignore b/.gitignore index e7fa89d6..ca6fa304 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,4 @@ tensorflow/mantaGen/datasets venv __pycache__ .ipynb_checkpoints -*.ipynb +# *.ipynb diff --git a/CMakeLists.txt b/CMakeLists.txt index fbfd039c..7458c7e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,8 +33,9 @@ endif() if(APPLE) if(NOT CMAKE_PREFIX_PATH) - #set(CMAKE_PREFIX_PATH "/usr/local/opt/qt5/") # mac/homebrew (version independent) - set(CMAKE_PREFIX_PATH "/Users/Cesar/Qt/5.15.0/clang_64/") + set(CMAKE_PREFIX_PATH "/usr/local/opt/qt5/") # mac/homebrew (version independent) + #set(CMAKE_PREFIX_PATH "/home/myname/qt/5.5/clang_64") # other... + #set(CMAKE_PREFIX_PATH "/Users/Cesar/Qt/5.15.0/clang_64/") endif() endif() diff --git a/scenes/cell-growth/export-particles.py b/scenes/cell-growth/export-particles.py new file mode 100644 index 00000000..014cdd51 --- /dev/null +++ b/scenes/cell-growth/export-particles.py @@ -0,0 +1,77 @@ +# +# NUMPY file format test +# +from manta import * + +# solver params +dim = 2 +particleNumber = 2 +res = 64 +gs = vec3(res,res,res) +if (dim==2): + gs.z=1 + particleNumber = 3 # use more particles in 2d + +s = Solver(name='main', gridSize = gs, dim=dim) +s.timestep = 0.5 + +# prepare grids and particles +flags = s.create(FlagGrid) +flags2 = s.create(FlagGrid) +vel = s.create(MACGrid) +vel2 = s.create(MACGrid) +velOld = s.create(MACGrid) +pressure = s.create(RealGrid) +pressure2 = s.create(RealGrid) +tmpVec3 = s.create(VecGrid) +pp = s.create(BasicParticleSystem) +# add velocity data to particles +pVel = pp.create(PdataVec3) +# apic part +mass = s.create(MACGrid) +pCx = pp.create(PdataVec3) +pCy = pp.create(PdataVec3) +pCz = pp.create(PdataVec3) + +# scene setup +flags.initDomain(boundaryWidth=0) +# enable one of the following +# fluidbox = Box(parent=s, p0=gs*vec3(0,0,0), p1=gs*vec3(0.4,0.6,1)) # breaking dam +fluidbox = Box(parent=s, p0=gs*vec3(0.4,0.72,0.4), p1=gs*vec3(0.6,0.92,0.6)) # centered falling block +phiInit = fluidbox.computeLevelset() +flags.updateFromLevelset(phiInit) +# phiInit is not needed from now on! + +# note, there's no resamplig here, so we need _LOTS_ of particles... +sampleFlagsWithParticles(flags=flags, parts=pp, discretization=particleNumber, randomness=0.2) + +#main loop +for t in range(20): + mantaMsg('\nFrame %i, simulation time %f' % (s.frame, s.timeTotal)) + + # APIC + pp.advectInGrid(flags=flags, vel=vel, integrationMode=IntRK4, deleteInObstacle=False) + apicMapPartsToMAC(flags=flags, vel=vel, parts=pp, partVel=pVel, cpx=pCx, cpy=pCy, cpz=pCz, mass=mass) + extrapolateMACFromWeight(vel=vel , distance=2, weight=tmpVec3) + markFluidCells(parts=pp, flags=flags) + + addGravity(flags=flags, vel=vel, gravity=(0,-0.002,0)) + + # pressure solve + setWallBcs(flags=flags, vel=vel) + solvePressure(flags=flags, vel=vel, pressure=pressure) + setWallBcs(flags=flags, vel=vel) + + # we dont have any levelset, ie no extrapolation, so make sure the velocities are valid + extrapolateMACSimple(flags=flags, vel=vel) + + # APIC velocity update + apicMapMACGridToParts(partVel=pVel, cpx=pCx, cpy=pCy, cpz=pCz, parts=pp, vel=vel, flags=flags) + + s.step() + + file_name = 'results/particles-frame-%d.txt' % s.frame + pp.save(file_name) + + + diff --git a/scenes/cell-growth/points.ipynb b/scenes/cell-growth/points.ipynb new file mode 100644 index 00000000..190faeca --- /dev/null +++ b/scenes/cell-growth/points.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "Plot(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camera=[2, -3, 0.2, 0.0, 0…", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "22fb85219bd643b395894b32d40222ec" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": "Output()", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "d274bcf3a0c948ad8a8fd935edcfa6cc" + } + }, + "metadata": {} + } + ], + "source": [ + "import k3d\n", + "import numpy as np\n", + "\n", + "points_number = 500\n", + "positions = 50 * np.random.random_sample((points_number,3)) - 25\n", + "colors = np.random.randint(0, 0xFFFFFF, points_number)\n", + "\n", + "plot = k3d.plot()\n", + "points = k3d.points(positions.astype(np.float32), colors.astype(np.uint32), point_size=3.0, shader='flat')\n", + "plot += points\n", + "plot.display()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "plot.camera_auto_fit = False" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "points.shader = '3d'" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "points.shader = '3dSpecular'" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "points.shader = 'mesh'" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "points.mesh_detail = 5" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "tmp = points.colors\n", + "points.colors = []" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "points.colors = tmp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.7.6 64-bit ('anaconda3': conda)", + "language": "python", + "name": "python37664bitanaconda3condab615bb15fcd4464bb2000e7d9e446dfd" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6-final" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/scenes/cell-growth/results/particles-frame-0.txt b/scenes/cell-growth/results/particles-frame-0.txt new file mode 100644 index 00000000..996fde26 --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-0.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+46.122944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+46.224190,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+46.196522,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+46.452816,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+46.541092,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+46.452572,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+46.800152,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+46.888466,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+46.853466,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+46.207771,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+46.176586,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+46.226772,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+46.441101,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+46.473625,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+46.477360,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+46.892422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+46.792229,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+46.857471,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+46.206802,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+46.169518,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+46.206985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+46.450970,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+46.500599,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+46.442928,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+46.795616,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+46.858963,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+46.834446,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+46.197285,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+46.202263,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+46.150383,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+46.493103,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+46.549870,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+46.486687,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+46.824116,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+46.816715,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+46.787060,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+46.172230,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+46.194016,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+46.123440,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+46.491394,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+46.534767,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+46.483269,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+46.899994,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+46.831276,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+46.818508,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+46.117393,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+46.112106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+46.180454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+46.435593,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+46.493061,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+46.446278,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+46.826477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+46.776901,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+46.792454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+46.233032,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+46.153206,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+46.139019,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+46.541412,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+46.535339,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+46.507298,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+46.875801,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+46.811890,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+46.851540,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+46.107635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+46.193913,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+46.224499,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+46.463787,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+46.465145,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+46.443615,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+46.890518,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+46.786201,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+46.853771,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+46.156303,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+46.134682,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+46.138504,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+46.444492,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+46.434052,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+46.457832,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+46.848034,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+46.823112,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+46.837921,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+46.125908,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+46.140656,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+46.122890,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+46.561375,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+46.436268,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+46.542309,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+46.847363,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+46.801311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+46.817127,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+46.171368,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+46.214226,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+46.225922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+46.530819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+46.447945,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+46.477554,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+46.880821,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+46.866276,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+46.778492,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+46.186802,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+46.201874,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+46.171841,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+46.533688,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+46.496178,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+46.474491,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+46.845936,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+46.882908,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+46.773361,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+47.130924,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+47.198879,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+47.144337,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+47.492691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+47.536110,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+47.463451,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+47.790478,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+47.847279,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+47.767593,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+47.180180,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+47.141026,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+47.103714,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+47.493225,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+47.499550,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+47.545895,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+47.816639,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+47.855515,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+47.825241,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+47.107990,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+47.143990,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+47.193726,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+47.551411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+47.466866,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+47.439705,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+47.864902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+47.794430,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+47.783440,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+47.138252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+47.117508,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+47.117512,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+47.456318,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+47.540882,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+47.447998,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+47.825691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+47.859486,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+47.840477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+47.175446,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+47.112045,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+47.231552,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+47.487579,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+47.480812,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+47.464241,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+47.799389,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+47.892891,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+47.860435,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+47.122341,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+47.116993,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+47.138935,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+47.494602,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+47.562840,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+47.541824,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+47.881767,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+47.822147,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+47.773201,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+47.127670,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+47.147614,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+47.123856,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+47.457779,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+47.438080,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+47.552200,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+47.859417,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+47.867279,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+47.851830,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+47.151440,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+47.115253,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+47.110172,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+47.553574,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+47.557205,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+47.458061,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+47.861832,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+47.802292,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+47.862774,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+47.138985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+47.206238,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+47.158653,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+47.454506,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+47.441410,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+47.565647,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+47.776230,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+47.769707,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+47.821865,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+47.127430,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+47.115665,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+47.226070,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+47.540215,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+47.502472,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+47.536961,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+47.800453,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+47.817509,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+47.835381,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+47.213562,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+47.151264,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+47.107273,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+47.498653,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+47.478733,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+47.494473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+47.818954,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+47.877613,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+47.791481,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+47.132786,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+47.203644,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+47.163212,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+47.526203,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+47.530907,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+47.468208,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+47.832623,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+47.821846,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+47.794727,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+48.162819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+48.122746,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+48.184875,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+48.524178,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+48.503078,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+48.558643,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+48.885143,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+48.770775,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+48.792240,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+48.152676,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+48.223042,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+48.226700,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+48.500191,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+48.443211,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+48.478241,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+48.846165,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+48.876045,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+48.795280,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+48.136925,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+48.120323,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+48.142944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+48.552448,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+48.459564,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+48.434582,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+48.772369,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+48.778793,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+48.828930,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+48.124149,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+48.203358,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+48.203484,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+48.456120,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+48.563362,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+48.514709,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+48.878979,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+48.787392,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+48.843460,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+48.130749,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+48.217804,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+48.188919,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+48.545727,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+48.525551,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+48.454155,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+48.830444,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+48.842342,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+48.860344,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+48.130043,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+48.169102,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+48.109261,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+48.493717,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+48.448051,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+48.489204,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+48.821278,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+48.836338,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+48.784985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+48.183819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+48.124477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+48.127293,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+48.472088,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+48.487774,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+48.554245,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+48.811420,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+48.898449,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+48.878315,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+48.215515,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+48.100548,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+48.232243,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+48.547329,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+48.445316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+48.521065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+48.815155,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+48.810654,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+48.849613,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+48.114967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+48.143124,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+48.166748,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+48.556465,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+48.548050,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+48.475307,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+48.858185,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+48.854488,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+48.884144,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+48.230209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+48.145618,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+48.167213,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+48.566494,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+48.446346,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+48.513115,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+48.801189,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+48.868793,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+48.769199,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+48.166424,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+48.100346,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+48.122284,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+48.478394,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+48.550964,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+48.523506,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+48.813107,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+48.840477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+48.821457,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+48.138535,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+48.105461,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+48.217754,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+48.442669,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+48.564213,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+48.519207,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+48.817734,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+48.813370,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+48.825165,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+49.202103,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+49.163143,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+49.166683,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+49.552029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+49.524918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+49.442142,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+49.867897,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+49.779678,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+49.824280,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+49.230736,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+49.212597,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+49.115387,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+49.492374,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+49.475067,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+49.541771,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+49.854855,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+49.767208,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+49.773506,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+49.141655,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+49.166550,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+49.143246,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+49.510719,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+49.506069,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+49.508682,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+49.860325,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+49.877026,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+49.816914,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+49.155785,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+49.205273,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+49.189606,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+49.499485,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+49.530827,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+49.517147,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+49.769176,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+49.818939,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+49.878613,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+49.203922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+49.157734,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+49.226635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+49.498215,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+49.514057,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+49.436665,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+49.777390,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+49.881252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+49.803135,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+49.157795,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+49.136421,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+49.182316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+49.565594,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+49.450089,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+49.478622,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+49.775696,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+49.857548,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+49.784420,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+49.139820,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+49.179340,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+49.136276,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+49.498322,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+49.438992,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+49.488842,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+49.838089,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+49.810341,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+49.825653,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+49.169926,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+49.117641,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+49.117680,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+49.533028,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+49.493832,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+49.544617,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+49.781712,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+49.889004,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+49.820114,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+49.122581,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+49.188370,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+49.199558,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+49.507679,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+49.446754,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+49.543892,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+49.895473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+49.867825,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+49.873478,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+49.223022,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+49.191330,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+49.183571,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+49.551300,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+49.550819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+49.490288,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+49.811741,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+49.813148,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+49.859333,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+49.172478,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+49.221176,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+49.131027,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+49.446529,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+49.478481,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+49.494972,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+49.829544,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+49.774754,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+49.862598,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+49.209225,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+49.199562,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+49.133720,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+49.505276,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+49.552635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+49.507278,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+49.789017,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+49.859245,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+49.782764,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+50.183064,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+50.145409,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+50.209473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+50.482197,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+50.491390,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+50.451611,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+50.843063,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+50.836662,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+50.793930,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+50.155064,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+50.130524,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+50.217449,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+50.530888,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+50.489056,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+50.475971,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+50.842472,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+50.826546,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+50.879635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+50.182354,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+50.165077,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+50.113247,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+50.540634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+50.517036,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+50.564110,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+50.795979,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+50.792553,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+50.869400,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+50.122650,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+50.137768,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+50.198353,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+50.565006,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+50.455639,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+50.516422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+50.786854,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+50.835384,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+50.814068,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+50.138065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+50.211956,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+50.232567,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+50.530842,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+50.478767,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+50.565922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+50.784203,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+50.772270,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+50.840473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+50.137760,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+50.190605,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+50.217079,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+50.469555,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+50.521660,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+50.493416,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+50.837616,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+50.840675,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+50.785957,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+50.114864,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+50.162525,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+50.171364,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+50.513115,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+50.456764,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+50.563717,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+50.787773,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+50.862270,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+50.804058,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+50.180367,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+50.231037,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+50.168205,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+50.471008,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+50.555965,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+50.477463,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+50.893330,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+50.883392,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+50.798264,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+50.115837,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+50.157944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+50.105915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+50.478657,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+50.458664,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+50.511162,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+50.791592,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+50.806126,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+50.876019,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+50.144432,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+50.178291,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+50.157726,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+50.506977,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+50.495487,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+50.469410,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+50.803497,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+50.846725,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+50.857590,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+50.186737,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+50.101227,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+50.127178,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+50.486153,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+50.508110,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+50.488964,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+50.846489,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+50.788963,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+50.875538,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+50.194801,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+50.198669,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+50.122669,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+50.451824,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+50.505611,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+50.433949,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+50.831543,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+50.886799,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+50.827473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+51.212479,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+51.119820,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+51.229984,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+51.456913,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+51.441242,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+51.474503,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+51.842892,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+51.884521,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+51.806511,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+51.127316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+51.231087,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+51.148312,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+51.484535,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+51.434856,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+51.437729,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+51.812798,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+51.796360,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+51.866627,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+51.169094,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+51.187752,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+51.193810,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+51.508575,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+51.520473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+51.454010,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+51.783710,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+51.809311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+51.863316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+51.110603,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+51.174797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+51.152206,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+51.441967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+51.485523,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+51.553894,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+51.778976,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+51.771351,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+51.798801,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+51.107647,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+51.120171,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+51.133881,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+51.489902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+51.457382,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+51.443806,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+51.769897,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+51.788017,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+51.813477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+51.152462,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+51.209915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+51.230831,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+51.433800,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+51.525654,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+51.543316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+51.888504,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+51.891106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+51.893661,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+51.114311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+51.111439,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+51.186913,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+51.467495,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+51.484039,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+51.496227,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+51.809231,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+51.882591,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+51.833691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+51.157902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+51.101677,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+51.189293,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+51.488350,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+51.437691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+51.527332,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+51.847668,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+51.770237,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+51.812683,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+51.216698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+51.178844,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+51.176270,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+51.529053,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+51.442665,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+51.445690,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+51.804413,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+51.840858,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+51.829212,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+51.213093,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+51.221287,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+51.134514,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+51.437584,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+51.449322,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+51.464119,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+51.776512,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+51.788975,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+51.839237,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+51.222633,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+51.149940,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+51.209564,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+51.536930,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+51.554569,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+51.539631,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+51.774082,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+51.884289,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+51.875710,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+51.212509,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+51.225719,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+51.171730,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+51.536446,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+51.529957,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+51.459774,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+51.801308,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+51.885639,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+51.868095,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+52.201496,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+52.191963,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+52.116505,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+52.555477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+52.443497,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+52.487549,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+52.845779,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+52.878605,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+52.852318,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+52.173634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+52.181454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+52.186192,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+52.489048,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+52.494987,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+52.478252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+52.785732,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+52.771797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+52.822681,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+52.154320,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+52.170040,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+52.106773,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+52.474590,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+52.515087,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+52.559731,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+52.801979,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+52.785904,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+52.860226,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+52.126991,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+52.203888,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+52.216824,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+52.484264,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+52.487984,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+52.538609,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+52.855507,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+52.782753,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+52.830940,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+52.168411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+52.103199,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+52.198643,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+52.560841,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+52.502464,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+52.560619,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+52.801659,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+52.873013,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+52.809872,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+52.196171,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+52.129631,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+52.207539,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+52.446686,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+52.528252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+52.524548,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+52.881935,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+52.865334,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+52.810555,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+52.111561,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+52.174706,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+52.145203,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+52.563484,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+52.542103,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+52.516998,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+52.867985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+52.871143,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+52.852425,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+52.145416,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+52.145905,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+52.171700,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+52.479488,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+52.446079,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+52.463570,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+52.861298,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+52.819847,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+52.863720,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+52.135635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+52.197441,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+52.129967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+52.536972,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+52.523094,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+52.556656,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+52.872810,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+52.833977,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+52.878311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+52.172272,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+52.124672,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+52.167149,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+52.442657,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+52.503147,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+52.518681,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+52.828312,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+52.780231,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+52.836727,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+52.226742,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+52.118454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+52.133194,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+52.521770,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+52.451973,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+52.534588,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+52.795372,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+52.786148,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+52.823990,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+52.101601,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+52.109486,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+52.119999,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+52.531242,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+52.434635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+52.531921,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+52.801849,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+52.806797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+52.776337,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+53.231323,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+53.102844,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+53.232510,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+53.523552,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+53.519878,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+53.478519,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+53.846645,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+53.877876,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+53.774490,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+53.188206,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+53.165871,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+53.200825,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+53.497845,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+53.484875,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+53.477612,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+53.865944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+53.787380,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+53.827698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+53.217834,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+53.124306,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+53.153557,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+53.500267,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+53.511383,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+53.490192,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+53.812859,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+53.826622,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+53.815773,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+53.141602,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+53.227032,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+53.131214,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+53.500061,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+53.518360,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+53.468651,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+53.775486,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+53.869102,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+53.773415,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+53.111042,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+53.182709,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+53.167740,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+53.545967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+53.445335,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+53.506187,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+53.861546,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+53.815121,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+53.896729,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+53.126698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+53.202583,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+53.195152,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+53.488686,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+53.455563,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+53.503414,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+53.789848,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+53.872005,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+53.779457,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+53.137691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+53.231148,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+53.193291,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+53.506828,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+53.462196,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+53.436188,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+53.845043,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+53.834209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+53.814804,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+53.173927,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+53.191292,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+53.217632,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+53.469772,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+53.564049,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+53.491676,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+53.862946,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+53.779354,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+53.885117,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+53.153065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+53.222469,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+53.168270,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+53.554363,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+53.485577,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+53.532558,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+53.806896,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+53.890411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+53.848854,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+53.186768,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+53.213520,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+53.140560,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+53.455799,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+53.507538,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+53.552536,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+53.776527,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+53.859222,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+53.818336,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+53.124413,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+53.193928,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+53.183037,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+53.546967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+53.549915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+53.495792,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+53.852169,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+53.850498,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+53.876457,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+53.202755,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+53.162029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+53.118725,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+53.454281,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+53.465240,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+53.497383,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+53.798267,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+53.797962,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+53.849411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+54.126938,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+54.195629,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+54.117855,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+54.478477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+54.502048,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+54.501354,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+54.813816,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+54.815281,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+54.871952,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+54.106087,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+54.135098,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+54.132587,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+54.516033,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+54.557106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+54.467541,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+54.841846,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+54.773533,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+54.830524,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+54.152237,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+54.225052,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+54.204712,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+54.503342,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+54.545261,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+54.557003,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+54.804756,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+54.794430,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+54.771698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+54.224411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+54.127312,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+54.115250,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+54.473492,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+54.527569,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+54.507908,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+54.813065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+54.898220,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+54.853012,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+54.121845,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+54.144268,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+54.174557,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+54.526585,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+54.473900,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+54.518005,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+54.847099,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+54.802029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+54.793514,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+54.143875,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+54.134258,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+54.213333,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+54.550507,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+54.459484,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+54.449825,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+54.801537,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+54.784985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+54.769222,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+54.196976,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+54.178967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+54.124821,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+54.516373,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+54.520206,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+54.496212,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+54.818062,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+54.787605,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+54.824600,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+54.167709,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+54.110859,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+54.113586,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+54.556965,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+54.468117,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+54.434826,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+54.882919,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+54.770611,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+54.864567,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+54.119244,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+54.192802,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+54.170151,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+54.525578,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+54.461563,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+54.506996,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+54.794220,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+54.832947,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+54.888836,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+54.120201,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+54.136829,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+54.195499,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+54.527649,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+54.545208,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+54.531567,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+54.791336,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+54.821518,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+54.879963,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+54.226608,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+54.196995,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+54.186924,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+54.502499,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+54.565315,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+54.489071,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+54.801395,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+54.787659,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+54.847473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+54.110634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+54.158367,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+54.170006,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+54.523399,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+54.442406,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+54.519779,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+54.774071,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+54.868561,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+54.871189,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+55.190952,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+55.191593,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+55.128365,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+55.486843,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+55.458435,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+55.507492,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+55.885456,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+55.770092,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+55.802567,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+55.104282,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+55.113491,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+55.224407,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+55.438301,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+55.526138,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+55.451904,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+55.873898,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+55.862995,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+55.845978,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+55.209858,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+55.194374,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+55.207191,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+55.470947,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+55.439796,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+55.470013,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+55.863129,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+55.866837,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+55.819530,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+55.173359,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+55.145458,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+55.122345,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+55.511765,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+55.463112,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+55.453209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+55.808571,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+55.847153,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+55.814240,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+55.111885,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+55.114353,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+55.152729,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+55.512165,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+55.503857,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+55.498714,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+55.872570,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+55.854534,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+55.792839,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+55.111542,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+55.200775,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+55.182709,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+55.534115,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+55.511532,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+55.476105,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+55.882973,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+55.867233,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+55.820869,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+55.189129,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+55.114006,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+55.162334,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+55.507458,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+55.461285,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+55.471241,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+55.802723,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+55.865044,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+55.867474,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+55.215721,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+55.216476,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+55.209549,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+55.463707,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+55.529041,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+55.466209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+55.812546,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+55.773117,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+55.820534,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+55.216385,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+55.175903,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+55.176449,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+55.445618,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+55.546024,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+55.537422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+55.779995,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+55.890335,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+55.884468,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+55.208412,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+55.158298,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+55.120869,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+55.526211,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+55.565315,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+55.467636,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+55.871838,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+55.838737,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+55.825562,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+55.233238,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+55.196354,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+55.150902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+55.558887,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+55.552406,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+55.555889,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+55.813553,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+55.767593,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+55.846710,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+55.145969,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+55.180443,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+55.106800,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+55.456005,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+55.532539,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+55.494896,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+55.863243,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+55.848995,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+55.891785,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+56.166611,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+56.128429,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+56.153812,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+56.439682,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+56.450569,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+56.483124,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+56.886478,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+56.787045,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+56.854164,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+56.110634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+56.231628,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+56.104076,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+56.496975,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+56.522827,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+56.487797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+56.873226,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+56.808449,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+56.877930,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+56.147968,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+56.183830,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+56.208572,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+56.534000,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+56.560619,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+56.505642,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+56.772556,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+56.861454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+56.878922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+56.129944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+56.172470,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+56.212318,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+56.480698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+56.446789,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+56.524704,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+56.853203,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+56.872452,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+56.769371,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+56.140759,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+56.184422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+56.187916,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+56.446209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+56.472984,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+56.444946,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+56.884541,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+56.859509,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+56.867672,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+56.139740,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+56.121563,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+56.142151,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+56.510376,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+56.552013,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+56.475464,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+56.790001,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+56.792843,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+56.879387,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+56.225372,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+56.146965,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+56.191460,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+56.559982,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+56.541065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+56.437698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+56.793186,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+56.829498,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+56.852291,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+56.223495,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+56.208427,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+56.145802,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+56.513569,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+56.498714,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+56.551296,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+56.792900,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+56.856880,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+56.792488,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+56.109081,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+56.181210,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+56.167465,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+56.474892,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+56.507000,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+56.452843,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+56.778893,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+56.883106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+56.777096,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+56.122902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+56.181347,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+56.128967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+56.512341,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+56.472843,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+56.501034,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+56.770374,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+56.857468,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+56.821381,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+56.139446,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+56.189053,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+56.144966,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+56.462734,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+56.566551,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+56.529903,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+56.788654,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+56.776852,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+56.887314,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+56.132915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+56.138378,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+56.109215,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+56.492805,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+56.565498,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+56.441422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+56.801918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+56.875187,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+56.868740,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+57.189922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+57.172085,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+57.109512,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+57.448681,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+57.472218,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+57.458500,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+57.803734,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+57.786671,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+57.892162,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+57.206448,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+57.120010,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+57.122158,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+57.495293,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+57.515797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+57.553867,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+57.898094,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+57.894115,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+57.844490,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+57.135479,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+57.143444,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+57.147362,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+57.535423,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+57.455029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+57.529865,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+57.856236,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+57.848442,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+57.772129,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+57.184532,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+57.211372,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+57.138298,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+57.521832,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+57.536442,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+57.445480,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+57.798264,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+57.790676,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+57.766834,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+57.193054,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+57.208549,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+57.217495,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+57.522171,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+57.458897,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+57.552727,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+57.766903,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+57.899296,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+57.877918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+57.220924,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+57.212345,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+57.204071,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+57.563358,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+57.560333,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+57.530914,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+57.847191,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+57.882515,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+57.811054,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+57.106525,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+57.110214,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+57.150852,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+57.514660,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+57.460915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+57.483627,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+57.832489,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+57.828568,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+57.771801,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+57.218460,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+57.212059,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+57.203487,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+57.560867,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+57.554993,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+57.479267,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+57.837948,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+57.863430,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+57.875935,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+57.118004,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+57.187019,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+57.169624,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+57.514862,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+57.453201,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+57.524899,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+57.837742,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+57.879536,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+57.867123,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+57.147808,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+57.140812,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+57.108582,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+57.507435,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+57.556232,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+57.565395,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+57.790199,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+57.795254,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+57.818684,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+57.218060,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+57.163280,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+57.188015,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+57.556530,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+57.554985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+57.529675,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+57.774570,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+57.899395,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+57.785309,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+57.203285,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+57.153355,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+57.181633,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+57.524937,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+57.563744,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+57.491711,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+57.884106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+57.887638,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+57.891529,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+58.226059,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+58.192905,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+58.187679,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+58.493248,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+58.528530,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+58.519363,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+58.850498,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+58.826462,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+58.867550,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+58.210979,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+58.157246,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+58.164181,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+58.535320,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+58.549522,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+58.456642,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+58.854588,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+58.824310,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+58.795280,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+58.143219,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+58.107224,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+58.114929,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+58.507881,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+58.477539,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+58.455616,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+58.829342,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+58.827419,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+58.875053,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+58.160706,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+58.130379,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+58.172634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+58.528450,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+58.468506,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+58.456547,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+58.814224,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+58.865337,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+58.798653,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+58.159588,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+58.103909,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+58.158566,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+58.474819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+58.492092,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+58.515903,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+58.866993,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+58.864311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+58.868778,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+58.179878,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+58.214497,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+58.229233,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+58.554516,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+58.447510,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+58.454838,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+58.775299,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+58.879093,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+58.820732,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+58.213928,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+58.118797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+58.162918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+58.433388,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+58.450535,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+58.512180,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+58.881042,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+58.872295,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+58.883274,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+58.149509,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+58.194252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+58.169857,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+58.541706,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+58.515488,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+58.470482,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+58.792538,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+58.832081,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+58.819603,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+58.180283,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+58.118992,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+58.160069,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+58.537674,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+58.546883,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+58.455029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+58.856205,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+58.840061,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+58.829350,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+58.210114,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+58.196522,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+58.155510,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+58.552269,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+58.492599,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+58.506622,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+58.850330,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+58.877251,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+58.835426,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+58.112740,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+58.193851,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+58.205441,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+58.454918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+58.478939,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+58.512196,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+58.823212,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+58.783703,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+58.840286,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+58.220165,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+58.189861,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+58.162613,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+58.443882,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+58.525185,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+58.449135,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+58.865883,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+58.855579,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+58.884434,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-1.txt b/scenes/cell-growth/results/particles-frame-1.txt new file mode 100644 index 00000000..3e6fa063 --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-1.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+46.026943,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+46.128189,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+46.100521,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+46.356815,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+46.445091,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+46.356571,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+46.704151,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+46.792465,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+46.757465,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+46.111771,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+46.080585,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+46.130772,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+46.345100,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+46.377625,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+46.381359,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+46.796421,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+46.696228,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+46.761471,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+46.110802,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+46.073517,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+46.110985,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+46.354969,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+46.404598,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+46.346928,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+46.699615,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+46.762962,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+46.738445,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+46.101284,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+46.106262,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+46.054382,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+46.397102,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+46.453869,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+46.390686,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+46.728115,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+46.720715,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+46.691059,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+46.076229,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+46.098015,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+46.027439,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+46.395393,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+46.438766,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+46.387268,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+46.803993,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+46.735275,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+46.722507,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+46.021393,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+46.016106,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+46.084454,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+46.339592,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+46.397060,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+46.350277,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+46.730476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+46.680901,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+46.696453,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+46.137032,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+46.057205,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+46.043018,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+46.445412,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+46.439339,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+46.411297,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+46.779800,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+46.715889,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+46.755539,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+46.011635,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+46.097912,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+46.128498,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+46.367786,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+46.369144,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+46.347614,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+46.794518,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+46.690201,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+46.757771,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+46.060303,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+46.038681,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+46.042503,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+46.348492,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+46.338051,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+46.361832,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+46.752033,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+46.727112,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+46.741920,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+46.029907,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+46.044655,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+46.026890,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+46.465374,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+46.340267,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+46.446308,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+46.751362,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+46.705311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+46.721127,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+46.075367,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+46.118225,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+46.129921,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+46.434818,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+46.351944,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+46.381554,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+46.784821,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+46.770275,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+46.682491,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+46.090801,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+46.105873,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+46.075840,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+46.437687,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+46.400177,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+46.378490,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+46.749935,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+46.786907,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+46.677361,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+47.034924,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+47.102879,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+47.048336,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+47.396690,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+47.440109,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+47.367451,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+47.694477,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+47.751278,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+47.671593,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+47.084179,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+47.045025,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+47.007713,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+47.397224,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+47.403549,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+47.449894,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+47.720638,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+47.759514,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+47.729240,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+47.011990,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+47.047989,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+47.097725,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+47.455410,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+47.370865,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+47.343704,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+47.768902,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+47.698429,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+47.687439,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+47.042252,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+47.021507,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+47.021511,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+47.360317,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+47.444881,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+47.351997,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+47.729691,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+47.763485,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+47.744476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+47.079445,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+47.016045,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+47.135551,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+47.391579,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+47.384811,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+47.368240,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+47.703388,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+47.796890,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+47.764435,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+47.026340,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+47.020992,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+47.042934,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+47.398602,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+47.466839,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+47.445824,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+47.785767,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+47.726147,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+47.677200,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+47.031670,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+47.051613,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+47.027855,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+47.361778,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+47.342079,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+47.456200,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+47.763416,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+47.771278,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+47.755829,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+47.055439,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+47.019253,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+47.014172,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+47.457573,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+47.461205,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+47.362061,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+47.765831,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+47.706291,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+47.766773,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+47.042984,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+47.110237,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+47.062653,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+47.358505,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+47.345409,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+47.469646,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+47.680229,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+47.673706,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+47.725864,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+47.031429,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+47.019665,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+47.130070,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+47.444214,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+47.406471,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+47.440960,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+47.704453,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+47.721508,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+47.739380,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+47.117561,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+47.055264,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+47.011272,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+47.402653,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+47.382732,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+47.398472,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+47.722954,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+47.781612,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+47.695480,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+47.036785,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+47.107643,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+47.067211,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+47.430202,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+47.434906,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+47.372208,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+47.736622,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+47.725845,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+47.698727,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+48.066818,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+48.026745,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+48.088875,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+48.428177,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+48.407078,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+48.462643,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+48.789143,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+48.674774,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+48.696239,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+48.056675,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+48.127041,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+48.130699,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+48.404190,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+48.347210,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+48.382240,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+48.750164,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+48.780045,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+48.699280,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+48.040924,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+48.024323,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+48.046944,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+48.456448,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+48.363564,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+48.338581,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+48.676369,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+48.682793,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+48.732929,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+48.028149,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+48.107357,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+48.107483,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+48.360119,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+48.467361,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+48.418709,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+48.782978,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+48.691391,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+48.747459,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+48.034748,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+48.121803,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+48.092918,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+48.449726,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+48.429550,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+48.358154,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+48.734444,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+48.746342,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+48.764343,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+48.034042,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+48.073101,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+48.013260,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+48.397717,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+48.352051,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+48.393204,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+48.725277,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+48.740337,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+48.688984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+48.087818,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+48.028477,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+48.031292,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+48.376087,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+48.391773,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+48.458244,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+48.715420,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+48.802448,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+48.782314,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+48.119514,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+48.004547,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+48.136242,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+48.451328,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+48.349316,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+48.425064,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+48.719154,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+48.714653,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+48.753613,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+48.018967,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+48.047123,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+48.070747,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+48.460464,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+48.452049,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+48.379307,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+48.762184,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+48.758488,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+48.788143,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+48.134209,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+48.049618,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+48.071213,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+48.470493,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+48.350346,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+48.417114,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+48.705189,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+48.772793,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+48.673199,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+48.070423,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+48.004345,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+48.026283,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+48.382393,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+48.454964,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+48.427505,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+48.717106,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+48.744476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+48.725456,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+48.042534,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+48.009460,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+48.121754,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+48.346668,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+48.468212,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+48.423206,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+48.721733,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+48.717369,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+48.729164,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+49.106102,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+49.067142,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+49.070683,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+49.456028,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+49.428917,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+49.346142,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+49.771896,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+49.683678,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+49.728279,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+49.134735,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+49.116596,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+49.019386,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+49.396374,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+49.379066,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+49.445770,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+49.758854,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+49.671207,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+49.677505,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+49.045654,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+49.070549,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+49.047245,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+49.414719,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+49.410069,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+49.412682,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+49.764324,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+49.781025,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+49.720913,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+49.059784,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+49.109272,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+49.093605,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+49.403484,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+49.434826,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+49.421146,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+49.673176,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+49.722939,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+49.782612,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+49.107922,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+49.061733,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+49.130634,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+49.402214,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+49.418056,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+49.340664,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+49.681389,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+49.785252,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+49.707134,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+49.061794,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+49.040421,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+49.086315,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+49.469593,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+49.354088,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+49.382622,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+49.679695,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+49.761547,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+49.688419,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+49.043819,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+49.083340,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+49.040276,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+49.402321,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+49.342991,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+49.392841,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+49.742088,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+49.714340,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+49.729652,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+49.073925,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+49.021641,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+49.021679,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+49.437027,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+49.397831,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+49.448616,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+49.685711,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+49.793003,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+49.724113,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+49.026581,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+49.092369,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+49.103558,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+49.411678,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+49.350754,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+49.447891,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+49.799473,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+49.771824,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+49.777477,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+49.127022,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+49.095329,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+49.087570,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+49.455299,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+49.454819,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+49.394287,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+49.715740,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+49.717148,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+49.763332,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+49.076477,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+49.125175,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+49.035027,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+49.350529,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+49.382481,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+49.398972,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+49.733543,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+49.678753,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+49.766598,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+49.113224,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+49.103561,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+49.037720,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+49.409275,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+49.456635,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+49.411278,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+49.693016,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+49.763245,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+49.686764,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+50.087063,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+50.049408,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+50.113472,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+50.386196,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+50.395390,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+50.355610,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+50.747063,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+50.740662,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+50.697929,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+50.059063,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+50.034523,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+50.121449,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+50.434887,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+50.393055,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+50.379971,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+50.746471,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+50.730545,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+50.783634,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+50.086353,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+50.069077,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+50.017246,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+50.444633,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+50.421036,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+50.468109,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+50.699978,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+50.696552,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+50.773399,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+50.026649,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+50.041767,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+50.102352,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+50.469006,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+50.359638,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+50.420422,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+50.690853,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+50.739384,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+50.718067,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+50.042065,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+50.115955,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+50.136566,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+50.434841,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+50.382767,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+50.469921,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+50.688202,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+50.676270,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+50.744473,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+50.041759,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+50.094604,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+50.121078,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+50.373554,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+50.425659,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+50.397415,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+50.741615,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+50.744675,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+50.689957,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+50.018864,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+50.066525,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+50.075363,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+50.417114,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+50.360764,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+50.467716,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+50.691772,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+50.766270,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+50.708057,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+50.084366,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+50.135036,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+50.072205,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+50.375008,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+50.459965,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+50.381462,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+50.797329,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+50.787392,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+50.702263,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+50.019836,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+50.061943,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+50.009914,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+50.382656,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+50.362663,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+50.415161,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+50.695591,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+50.710125,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+50.780018,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+50.048431,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+50.082291,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+50.061726,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+50.410976,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+50.399487,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+50.373409,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+50.707497,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+50.750725,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+50.761589,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+50.090736,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+50.005226,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+50.031178,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+50.390152,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+50.412109,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+50.392963,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+50.750488,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+50.692963,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+50.779537,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+50.098801,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+50.102669,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+50.026669,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+50.355824,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+50.409611,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+50.337948,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+50.735542,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+50.790798,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+50.731472,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+51.116478,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+51.023819,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+51.133984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+51.360912,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+51.345242,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+51.378502,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+51.746891,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+51.788521,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+51.710510,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+51.031315,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+51.135086,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+51.052311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+51.388535,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+51.338856,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+51.341728,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+51.716797,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+51.700359,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+51.770626,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+51.073093,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+51.091751,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+51.097809,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+51.412575,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+51.424473,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+51.358009,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+51.687710,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+51.713310,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+51.767315,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+51.014603,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+51.078796,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+51.056206,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+51.345966,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+51.389523,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+51.457893,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+51.682976,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+51.675350,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+51.702801,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+51.011646,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+51.024170,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+51.037880,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+51.393902,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+51.361382,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+51.347805,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+51.673897,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+51.692017,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+51.717476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+51.056461,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+51.113914,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+51.134830,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+51.337799,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+51.429653,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+51.447315,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+51.792503,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+51.795105,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+51.797661,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+51.018311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+51.015438,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+51.090912,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+51.371494,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+51.388039,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+51.400227,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+51.713230,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+51.786591,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+51.737690,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+51.061901,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+51.005676,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+51.093292,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+51.392349,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+51.341690,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+51.431332,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+51.751667,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+51.674236,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+51.716682,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+51.120697,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+51.082844,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+51.080269,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+51.433052,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+51.346664,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+51.349689,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+51.708412,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+51.744858,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+51.733212,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+51.117092,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+51.125286,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+51.038513,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+51.341583,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+51.353321,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+51.368118,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+51.680511,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+51.692974,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+51.743237,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+51.126633,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+51.053940,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+51.113564,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+51.440929,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+51.458569,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+51.443630,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+51.678082,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+51.788288,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+51.779709,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+51.116508,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+51.129719,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+51.075729,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+51.440445,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+51.433956,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+51.363773,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+51.705307,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+51.789639,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+51.772095,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+52.105495,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+52.095963,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+52.020504,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+52.459476,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+52.347496,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+52.391548,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+52.749779,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+52.782604,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+52.756317,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+52.077633,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+52.085453,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+52.090191,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+52.393047,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+52.398987,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+52.382252,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+52.689732,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+52.675797,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+52.726681,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+52.058319,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+52.074039,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+52.010773,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+52.378590,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+52.419086,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+52.463730,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+52.705978,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+52.689903,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+52.764225,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+52.030991,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+52.107887,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+52.120823,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+52.388264,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+52.391983,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+52.442608,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+52.759506,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+52.686752,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+52.734940,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+52.072411,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+52.007198,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+52.102642,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+52.464840,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+52.406464,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+52.464619,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+52.705658,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+52.777012,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+52.713871,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+52.100170,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+52.033630,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+52.111538,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+52.350685,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+52.432251,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+52.428547,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+52.785934,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+52.769333,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+52.714554,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+52.015560,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+52.078705,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+52.049202,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+52.467484,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+52.446102,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+52.420998,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+52.771984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+52.775143,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+52.756424,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+52.049416,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+52.049904,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+52.075699,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+52.383488,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+52.350079,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+52.367569,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+52.765297,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+52.723846,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+52.767719,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+52.039635,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+52.101440,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+52.033966,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+52.440971,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+52.427094,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+52.460655,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+52.776810,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+52.737976,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+52.782310,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+52.076271,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+52.028671,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+52.071148,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+52.346657,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+52.407146,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+52.422680,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+52.732311,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+52.684231,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+52.740726,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+52.130741,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+52.022453,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+52.037193,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+52.425770,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+52.355972,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+52.438587,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+52.699371,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+52.690147,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+52.727989,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+52.005600,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+52.013485,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+52.023998,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+52.435242,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+52.338634,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+52.435921,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+52.705849,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+52.710796,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+52.680336,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+53.135323,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+53.006844,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+53.136509,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+53.427551,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+53.423878,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+53.382519,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+53.750645,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+53.781876,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+53.678490,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+53.092205,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+53.069870,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+53.104824,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+53.401844,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+53.388874,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+53.381611,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+53.769943,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+53.691380,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+53.731697,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+53.121834,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+53.028305,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+53.057556,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+53.404266,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+53.415382,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+53.394192,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+53.716858,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+53.730621,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+53.719772,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+53.045601,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+53.131031,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+53.035213,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+53.404060,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+53.422359,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+53.372650,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+53.679485,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+53.773102,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+53.677414,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+53.015041,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+53.086708,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+53.071739,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+53.449966,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+53.349335,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+53.410187,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+53.765545,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+53.719120,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+53.800728,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+53.030697,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+53.106583,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+53.099152,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+53.392685,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+53.359562,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+53.407413,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+53.693848,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+53.776005,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+53.683456,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+53.041691,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+53.135147,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+53.097290,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+53.410828,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+53.366196,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+53.340187,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+53.749043,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+53.738209,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+53.718803,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+53.077927,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+53.095291,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+53.121632,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+53.373772,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+53.468048,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+53.395676,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+53.766945,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+53.683353,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+53.789116,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+53.057064,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+53.126469,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+53.072269,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+53.458363,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+53.389576,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+53.436558,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+53.710896,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+53.794411,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+53.752853,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+53.090767,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+53.117519,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+53.044559,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+53.359798,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+53.411537,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+53.456535,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+53.680527,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+53.763222,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+53.722336,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+53.028412,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+53.097927,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+53.087036,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+53.450966,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+53.453915,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+53.399792,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+53.756168,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+53.754498,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+53.780457,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+53.106754,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+53.066029,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+53.022724,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+53.358280,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+53.369240,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+53.401382,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+53.702267,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+53.701962,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+53.753410,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+54.030937,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+54.099628,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+54.021854,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+54.382477,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+54.406048,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+54.405354,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+54.717815,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+54.719280,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+54.775951,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+54.010086,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+54.039097,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+54.036587,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+54.420033,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+54.461105,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+54.371540,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+54.745846,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+54.677532,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+54.734524,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+54.056236,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+54.129051,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+54.108711,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+54.407341,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+54.449261,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+54.461002,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+54.708755,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+54.698429,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+54.675697,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+54.128410,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+54.031311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+54.019249,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+54.377491,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+54.431568,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+54.411907,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+54.717064,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+54.802219,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+54.757011,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+54.025845,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+54.048267,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+54.078556,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+54.430584,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+54.377899,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+54.422005,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+54.751099,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+54.706028,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+54.697514,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+54.047874,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+54.038258,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+54.117332,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+54.454506,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+54.363483,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+54.353825,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+54.705536,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+54.688984,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+54.673222,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+54.100975,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+54.082966,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+54.028820,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+54.420372,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+54.424206,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+54.400211,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+54.722061,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+54.691605,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+54.728600,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+54.071709,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+54.014858,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+54.017586,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+54.460964,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+54.372116,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+54.338825,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+54.786919,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+54.674610,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+54.768566,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+54.023243,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+54.096802,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+54.074150,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+54.429577,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+54.365562,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+54.410995,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+54.698219,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+54.736946,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+54.792835,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+54.024200,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+54.040829,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+54.099499,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+54.431648,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+54.449207,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+54.435566,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+54.695335,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+54.725517,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+54.783962,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+54.130608,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+54.100994,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+54.090923,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+54.406498,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+54.469315,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+54.393070,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+54.705395,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+54.691658,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+54.751472,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+54.014633,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+54.062366,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+54.074005,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+54.427399,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+54.346405,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+54.423779,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+54.678070,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+54.772560,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+54.775188,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+55.094952,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+55.095592,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+55.032364,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+55.390842,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+55.362434,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+55.411491,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+55.789455,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+55.674091,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+55.706566,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+55.008282,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+55.017490,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+55.128407,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+55.342300,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+55.430138,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+55.355904,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+55.777897,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+55.766994,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+55.749977,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+55.113857,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+55.098373,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+55.111191,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+55.374947,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+55.343796,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+55.374012,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+55.767128,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+55.770836,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+55.723530,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+55.077358,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+55.049458,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+55.026344,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+55.415764,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+55.367111,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+55.357208,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+55.712570,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+55.751152,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+55.718239,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+55.015884,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+55.018353,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+55.056728,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+55.416164,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+55.407856,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+55.402714,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+55.776569,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+55.758533,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+55.696838,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+55.015541,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+55.104774,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+55.086708,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+55.438114,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+55.415531,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+55.380104,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+55.786972,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+55.771233,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+55.724869,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+55.093128,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+55.018005,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+55.066334,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+55.411457,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+55.365284,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+55.375240,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+55.706722,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+55.769043,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+55.771473,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+55.119720,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+55.120476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+55.113548,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+55.367706,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+55.433041,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+55.370209,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+55.716545,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+55.677116,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+55.724533,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+55.120384,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+55.079903,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+55.080448,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+55.349617,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+55.450024,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+55.441422,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+55.683994,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+55.794334,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+55.788467,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+55.112411,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+55.062298,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+55.024868,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+55.430210,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+55.469315,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+55.371635,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+55.775837,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+55.742737,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+55.729561,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+55.137238,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+55.100353,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+55.054901,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+55.462887,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+55.456406,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+55.459888,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+55.717552,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+55.671593,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+55.750710,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+55.049969,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+55.084442,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+55.010799,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+55.360004,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+55.436539,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+55.398895,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+55.767242,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+55.752995,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+55.795784,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+56.070610,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+56.032429,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+56.057812,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+56.343681,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+56.354568,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+56.387123,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+56.790478,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+56.691044,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+56.758163,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+56.014633,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+56.135628,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+56.008076,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+56.400974,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+56.426826,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+56.391796,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+56.777225,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+56.712448,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+56.781929,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+56.051968,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+56.087830,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+56.112572,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+56.438000,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+56.464619,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+56.409641,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+56.676556,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+56.765453,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+56.782921,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+56.033943,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+56.076469,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+56.116318,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+56.384697,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+56.350788,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+56.428703,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+56.757202,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+56.776451,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+56.673370,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+56.044758,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+56.088421,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+56.091915,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+56.350208,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+56.376984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+56.348946,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+56.788540,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+56.763508,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+56.771671,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+56.043739,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+56.025562,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+56.046150,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+56.414375,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+56.456013,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+56.379463,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+56.694000,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+56.696842,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+56.783386,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+56.129372,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+56.050964,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+56.095459,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+56.463982,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+56.445065,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+56.341698,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+56.697186,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+56.733498,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+56.756290,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+56.127495,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+56.112427,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+56.049801,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+56.417568,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+56.402714,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+56.455296,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+56.696899,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+56.760880,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+56.696487,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+56.013081,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+56.085209,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+56.071465,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+56.378891,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+56.410999,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+56.356842,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+56.682892,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+56.787106,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+56.681095,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+56.026901,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+56.085346,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+56.032967,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+56.416340,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+56.376842,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+56.405033,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+56.674374,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+56.761467,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+56.725380,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+56.043446,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+56.093052,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+56.048965,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+56.366734,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+56.470551,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+56.433903,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+56.692654,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+56.680851,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+56.791313,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+56.036915,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+56.042377,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+56.013214,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+56.396805,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+56.469498,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+56.345421,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+56.705917,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+56.779186,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+56.772739,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+57.093922,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+57.076084,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+57.013512,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+57.352680,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+57.376217,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+57.362499,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+57.707733,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+57.690670,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+57.796162,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+57.110447,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+57.024010,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+57.026157,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+57.399292,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+57.419796,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+57.457867,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+57.802094,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+57.798115,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+57.748489,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+57.039478,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+57.047443,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+57.051361,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+57.439423,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+57.359028,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+57.433865,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+57.760235,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+57.752441,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+57.676128,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+57.088531,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+57.115372,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+57.042297,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+57.425831,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+57.440441,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+57.349480,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+57.702263,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+57.694675,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+57.670834,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+57.097054,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+57.112549,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+57.121494,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+57.426170,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+57.362896,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+57.456726,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+57.670902,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+57.803295,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+57.781918,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+57.124924,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+57.116344,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+57.108070,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+57.467358,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+57.464333,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+57.434914,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+57.751190,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+57.786514,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+57.715054,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+57.010525,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+57.014214,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+57.054852,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+57.418659,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+57.364914,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+57.387627,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+57.736488,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+57.732567,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+57.675800,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+57.122459,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+57.116058,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+57.107487,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+57.464867,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+57.458992,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+57.383266,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+57.741947,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+57.767429,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+57.779934,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+57.022003,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+57.091019,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+57.073624,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+57.418861,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+57.357201,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+57.428898,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+57.741741,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+57.783535,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+57.771122,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+57.051807,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+57.044811,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+57.012581,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+57.411434,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+57.460232,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+57.469395,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+57.694199,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+57.699253,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+57.722683,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+57.122059,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+57.067280,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+57.092014,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+57.460529,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+57.458984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+57.433674,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+57.678570,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+57.803394,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+57.689308,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+57.107285,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+57.057354,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+57.085632,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+57.428936,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+57.467743,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+57.395710,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+57.788105,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+57.791637,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+57.795528,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+58.130058,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+58.096905,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+58.091679,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+58.397247,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+58.432529,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+58.423363,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+58.754498,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+58.730461,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+58.771549,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+58.114979,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+58.061245,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+58.068180,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+58.439320,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+58.453522,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+58.360641,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+58.758587,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+58.728310,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+58.699280,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+58.047218,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+58.011223,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+58.018929,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+58.411880,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+58.381538,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+58.359615,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+58.733341,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+58.731419,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+58.779053,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+58.064705,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+58.034378,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+58.076633,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+58.432449,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+58.372505,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+58.360546,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+58.718224,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+58.769337,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+58.702652,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+58.063587,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+58.007908,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+58.062565,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+58.378819,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+58.396091,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+58.419903,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+58.770992,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+58.768311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+58.772778,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+58.083878,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+58.118496,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+58.133232,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+58.458515,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+58.351509,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+58.358837,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+58.679298,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+58.783092,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+58.724731,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+58.117928,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+58.022797,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+58.066917,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+58.337387,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+58.354534,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+58.416180,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+58.785042,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+58.776295,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+58.787273,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+58.053509,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+58.098251,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+58.073856,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+58.445705,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+58.419487,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+58.374481,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+58.696537,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+58.736080,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+58.723602,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+58.084282,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+58.022991,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+58.064068,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+58.441673,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+58.450882,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+58.359028,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+58.760204,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+58.744061,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+58.733349,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+58.114113,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+58.100521,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+58.059509,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+58.456268,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+58.396599,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+58.410622,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+58.754330,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+58.781250,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+58.739426,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+58.016739,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+58.097851,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+58.109440,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+58.358917,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+58.382938,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+58.416195,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+58.727211,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+58.687702,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+58.744286,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+58.124165,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+58.093861,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+58.066612,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+58.347881,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+58.429184,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+58.353134,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+58.769882,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+58.759579,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+58.788433,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-10.txt b/scenes/cell-growth/results/particles-frame-10.txt new file mode 100644 index 00000000..1473635e --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-10.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+40.042942,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+40.144188,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+40.116520,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+40.372814,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+40.461090,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+40.372570,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+40.720150,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+40.808464,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+40.773464,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+40.127769,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+40.096584,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+40.146770,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+40.361099,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+40.393623,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+40.397358,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+40.812420,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+40.712227,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+40.777470,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+40.126801,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+40.089516,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+40.126984,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+40.370968,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+40.420597,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+40.362926,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+40.715614,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+40.778961,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+40.754444,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+40.117283,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+40.122261,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+40.070381,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+40.413101,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+40.469868,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+40.406685,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+40.744114,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+40.736713,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+40.707058,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+40.092228,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+40.114014,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+40.043438,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+40.411392,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+40.454765,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+40.403267,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+40.819992,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+40.751274,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+40.738506,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+40.037392,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+40.032104,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+40.100452,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+40.355591,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+40.413059,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+40.366276,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+40.746475,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+40.696899,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+40.712452,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+40.153030,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+40.073204,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+40.059017,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+40.461411,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+40.455338,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+40.427296,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+40.795799,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+40.731888,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+40.771538,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+40.027634,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+40.113911,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+40.144497,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+40.383785,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+40.385143,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+40.363613,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+40.810516,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+40.706200,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+40.773769,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+40.076302,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+40.054680,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+40.058502,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+40.364491,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+40.354050,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+40.377831,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+40.768032,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+40.743111,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+40.757919,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+40.045906,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+40.060654,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+40.042889,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+40.481373,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+40.356266,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+40.462307,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+40.767361,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+40.721310,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+40.737125,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+40.091366,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+40.134224,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+40.145920,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+40.450817,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+40.367943,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+40.397552,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+40.800819,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+40.786274,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+40.698490,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+40.106800,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+40.121872,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+40.091839,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+40.453686,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+40.416176,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+40.394489,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+40.765934,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+40.802906,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+40.693359,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+41.050922,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+41.118877,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+41.064335,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+41.412689,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+41.456108,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+41.383450,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+41.710476,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+41.767277,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+41.687592,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+41.100178,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+41.061024,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+41.023712,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+41.413223,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+41.419548,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+41.465893,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+41.736641,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+41.775513,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+41.745239,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+41.027988,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+41.063988,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+41.113724,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+41.471409,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+41.386864,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+41.359703,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+41.784901,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+41.714428,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+41.703438,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+41.058250,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+41.037506,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+41.037510,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+41.376316,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+41.460880,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+41.367996,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+41.745689,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+41.779484,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+41.760475,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+41.095444,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+41.032043,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+41.151550,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+41.407578,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+41.400810,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+41.384239,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+41.719387,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+41.812889,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+41.780434,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+41.042339,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+41.036991,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+41.058933,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+41.414600,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+41.482838,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+41.461823,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+41.801765,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+41.742146,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+41.693199,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+41.047668,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+41.067612,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+41.043854,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+41.377777,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+41.358078,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+41.472198,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+41.779415,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+41.787277,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+41.771828,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+41.071438,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+41.035252,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+41.030170,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+41.473572,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+41.477203,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+41.378059,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+41.781830,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+41.722290,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+41.782772,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+41.058983,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+41.126236,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+41.078651,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+41.374504,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+41.361408,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+41.485645,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+41.696228,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+41.689705,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+41.741863,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+41.047428,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+41.035664,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+41.146069,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+41.460213,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+41.422470,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+41.456959,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+41.720451,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+41.737507,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+41.755379,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+41.133560,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+41.071262,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+41.027271,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+41.418652,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+41.398731,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+41.414471,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+41.738953,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+41.797611,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+41.711479,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+41.052784,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+41.123642,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+41.083210,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+41.446201,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+41.450905,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+41.388206,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+41.752621,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+41.741844,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+41.714725,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+42.082817,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+42.042747,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+42.104874,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+42.444176,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+42.423080,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+42.478645,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+42.805145,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+42.690773,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+42.712242,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+42.072678,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+42.143044,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+42.146698,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+42.420189,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+42.363209,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+42.398239,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+42.766163,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+42.796043,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+42.715279,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+42.056923,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+42.040321,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+42.062943,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+42.472446,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+42.379562,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+42.354580,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+42.692368,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+42.698792,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+42.748928,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+42.044147,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+42.123356,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+42.123482,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+42.376118,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+42.483360,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+42.434708,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+42.798977,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+42.707390,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+42.763458,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+42.050747,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+42.137802,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+42.108917,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+42.465725,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+42.445549,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+42.374153,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+42.750443,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+42.762341,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+42.780342,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+42.050041,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+42.089100,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+42.029259,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+42.413715,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+42.368050,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+42.409203,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+42.741276,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+42.756336,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+42.704983,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+42.103817,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+42.044476,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+42.047291,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+42.392086,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+42.407772,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+42.474243,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+42.731419,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+42.818447,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+42.798313,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+42.135513,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+42.020546,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+42.152241,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+42.467327,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+42.365314,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+42.441063,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+42.735153,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+42.730652,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+42.769611,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+42.034966,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+42.063122,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+42.086746,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+42.476463,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+42.468048,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+42.395306,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+42.778183,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+42.774487,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+42.804142,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+42.150208,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+42.065617,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+42.087212,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+42.486492,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+42.366344,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+42.433113,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+42.721188,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+42.788792,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+42.689198,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+42.086422,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+42.020344,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+42.042282,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+42.398392,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+42.470963,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+42.443504,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+42.733105,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+42.760475,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+42.741455,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+42.058533,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+42.025459,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+42.137753,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+42.362667,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+42.484211,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+42.439205,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+42.737732,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+42.733368,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+42.745163,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+43.122101,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+43.083145,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+43.086681,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+43.472031,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+43.444916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+43.362144,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+43.787895,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+43.699677,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+43.744278,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+43.150734,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+43.132595,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+43.035385,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+43.412373,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+43.395065,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+43.461769,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+43.774853,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+43.687206,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+43.693504,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+43.061653,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+43.086548,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+43.063244,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+43.430717,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+43.426067,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+43.428680,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+43.780323,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+43.797024,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+43.736912,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+43.075783,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+43.125271,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+43.109604,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+43.419483,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+43.450825,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+43.437145,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+43.689175,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+43.738934,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+43.798611,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+43.123920,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+43.077732,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+43.146633,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+43.418213,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+43.434055,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+43.356663,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+43.697388,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+43.801250,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+43.723133,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+43.077793,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+43.056419,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+43.102314,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+43.485592,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+43.370087,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+43.398621,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+43.695694,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+43.777546,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+43.704418,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+43.059818,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+43.099339,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+43.056274,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+43.418320,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+43.358990,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+43.408840,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000001,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+43.758087,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+43.730339,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+43.745651,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000001,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+43.089924,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+43.037640,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+43.037678,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+43.453026,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+43.413830,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+43.464615,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+43.701710,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+43.809002,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+43.740112,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+43.042580,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+43.108368,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+43.119556,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+43.427677,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+43.366753,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+43.463890,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+43.815472,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+43.787823,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+43.793476,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+43.143021,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+43.111328,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+43.103569,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+43.471298,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+43.470818,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+43.410286,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+43.731739,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+43.733147,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+43.779331,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+43.092476,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+43.141174,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+43.051025,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+43.366528,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+43.398479,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+43.414970,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+43.749542,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+43.694752,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+43.782597,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+43.129223,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+43.119560,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+43.053719,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+43.425274,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+43.472633,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+43.427277,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+43.709015,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+43.779243,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+43.702763,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+44.103062,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+44.065407,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+44.129471,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+44.402195,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+44.411388,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+44.371609,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+44.763062,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+44.756660,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+44.713928,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+44.075062,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+44.050522,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+44.137447,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+44.450886,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+44.409054,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+44.395969,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+44.762470,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+44.746544,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+44.799633,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+44.102352,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+44.085075,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+44.033245,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+44.460632,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+44.437035,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+44.484108,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+44.715977,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+44.712551,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+44.789398,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+44.042648,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+44.057766,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+44.118351,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+44.485004,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+44.375637,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+44.436420,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+44.706852,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+44.755383,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+44.734066,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+44.058064,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+44.131954,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+44.152565,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+44.450840,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+44.398766,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+44.485920,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+44.704201,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+44.692268,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+44.760471,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+44.057758,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+44.110603,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+44.137077,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+44.389553,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+44.441658,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+44.413414,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+44.757614,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+44.760674,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+44.705956,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+44.034863,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+44.082523,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+44.091362,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+44.433113,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+44.376762,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+44.483715,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+44.707771,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+44.782272,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+44.724056,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+44.100365,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000001,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+44.151035,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+44.088203,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+44.391006,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+44.475964,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+44.397461,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+44.813328,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+44.803391,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+44.718262,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+44.035835,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+44.077942,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+44.025913,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+44.398655,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+44.378662,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+44.431160,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+44.711590,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+44.726124,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+44.796017,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+44.064430,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+44.098289,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+44.077724,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+44.426975,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+44.415485,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+44.389408,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+44.723495,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+44.766724,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+44.777588,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+44.106735,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+44.021225,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+44.047176,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+44.406151,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+44.428108,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+44.408962,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+44.766487,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+44.708961,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+44.795536,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+44.114799,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+44.118668,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+44.042667,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+44.371822,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+44.425610,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+44.353947,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+44.751541,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+44.806797,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+44.747471,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+45.132477,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+45.039818,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+45.149982,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+45.376911,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+45.361240,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+45.394501,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+45.762890,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+45.804520,+0.500000] , 1. [-0.000000,-1.280001,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+45.726509,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+45.047314,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+45.151085,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+45.068310,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+45.404533,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+45.354855,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+45.357727,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+45.732796,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+45.716358,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+45.786625,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+45.089092,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+45.107750,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+45.113808,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+45.428574,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+45.440472,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+45.374008,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+45.703709,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+45.729309,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+45.783314,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+45.030602,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+45.094795,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+45.072205,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+45.361965,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+45.405521,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+45.473892,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+45.698975,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+45.691349,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+45.718800,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+45.027645,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+45.040169,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+45.053879,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+45.409901,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+45.377380,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+45.363804,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+45.689896,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+45.708015,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+45.733475,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+45.072460,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+45.129913,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+45.150829,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+45.353798,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+45.445652,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+45.463314,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+45.808502,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+45.811104,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+45.813660,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+45.034309,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+45.031441,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+45.106911,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+45.387493,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+45.404037,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+45.416225,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+45.729229,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+45.802589,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+45.753689,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+45.077900,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+45.021675,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+45.109291,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+45.408348,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+45.357689,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+45.447330,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+45.767666,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+45.690235,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+45.732681,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+45.136696,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+45.098843,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+45.096268,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+45.449051,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+45.362663,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+45.365688,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+45.724411,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+45.760857,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+45.749210,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+45.133091,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+45.141285,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+45.054512,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+45.357582,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+45.369320,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+45.384117,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+45.696510,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+45.708973,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+45.759235,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+45.142632,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+45.069939,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+45.129562,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+45.456928,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+45.474567,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+45.459629,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+45.694080,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+45.804287,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+45.795708,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+45.132507,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+45.145718,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+45.091728,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+45.456444,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+45.449955,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+45.379772,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+45.721306,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+45.805637,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+45.788094,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+46.121494,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+46.111961,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+46.036503,+0.500000] , 1. [-0.000000,-1.280001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+46.475475,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+46.363495,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+46.407547,+0.500000] , 1. [-0.000000,-1.280001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+46.765778,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+46.798603,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+46.772316,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+46.093632,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+46.101452,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+46.106190,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+46.409046,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+46.414986,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+46.398251,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+46.705730,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+46.691795,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+46.742680,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+46.074318,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+46.090038,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+46.026772,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+46.394588,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+46.435085,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+46.479729,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+46.721977,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+46.705902,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+46.780224,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+46.046989,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+46.123886,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+46.136822,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+46.404263,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+46.407982,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+46.458607,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+46.775505,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+46.702751,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+46.750938,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+46.088409,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+46.023197,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+46.118641,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+46.480839,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+46.422462,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+46.480618,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+46.721657,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+46.793011,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+46.729870,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+46.116169,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+46.049629,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+46.127537,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+46.366684,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+46.448250,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+46.444546,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+46.801933,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+46.785332,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+46.730553,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+46.031559,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+46.094704,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+46.065201,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+46.483482,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+46.462101,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+46.436996,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+46.787983,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+46.791142,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+46.772423,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+46.065414,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+46.065903,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+46.091698,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+46.399487,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+46.366077,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+46.383568,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+46.781296,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+46.739845,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+46.783718,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+46.055634,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+46.117439,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+46.049965,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+46.456970,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+46.443092,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+46.476654,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+46.792809,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+46.753975,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+46.798309,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+46.092270,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+46.044670,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+46.087147,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+46.362656,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+46.423145,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+46.438679,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+46.748310,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+46.700230,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+46.756725,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+46.146740,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+46.038452,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+46.053192,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+46.441769,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+46.371971,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+46.454586,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+46.715370,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+46.706146,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+46.743988,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+46.021599,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+46.029484,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+46.039997,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+46.451241,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+46.354633,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+46.451920,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+46.721848,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+46.726795,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+46.696335,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+47.151321,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+47.022842,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+47.152508,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+47.443550,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+47.439877,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+47.398518,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+47.766644,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+47.797874,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+47.694489,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+47.108204,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+47.085869,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+47.120823,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+47.417843,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+47.404873,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+47.397610,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+47.785942,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+47.707378,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+47.747696,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+47.137833,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+47.044304,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+47.073555,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+47.420265,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+47.431381,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+47.410191,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+47.732857,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+47.746620,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+47.735771,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+47.061600,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+47.147030,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+47.051212,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+47.420059,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+47.438358,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+47.388649,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+47.695484,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+47.789101,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+47.693413,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+47.031040,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+47.102707,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+47.087738,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+47.465965,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+47.365334,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+47.426186,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+47.781544,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+47.735119,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+47.816727,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+47.046696,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+47.122581,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+47.115150,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+47.408684,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+47.375561,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+47.423412,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+47.709846,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+47.792004,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+47.699455,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+47.057690,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+47.151146,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+47.113289,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+47.426826,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+47.382195,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+47.356186,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+47.765041,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+47.754208,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+47.734802,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+47.093925,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+47.111290,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+47.137630,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+47.389771,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+47.484047,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+47.411674,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+47.782944,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+47.699352,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+47.805115,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+47.073063,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+47.142467,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+47.088268,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+47.474361,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+47.405575,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+47.452557,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+47.726894,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+47.810410,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+47.768852,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+47.106766,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+47.133518,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+47.060558,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+47.375797,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+47.427536,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+47.472534,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+47.696526,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+47.779221,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+47.738335,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+47.044411,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+47.113926,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+47.103035,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+47.466965,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+47.469913,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+47.415791,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+47.772167,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+47.770496,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+47.796455,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+47.122753,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+47.082027,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+47.038723,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+47.374279,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+47.385239,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+47.417381,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+47.718266,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+47.717960,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+47.769409,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+48.046936,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+48.115627,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+48.037853,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+48.398476,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+48.422047,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+48.421352,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+48.733814,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+48.735279,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+48.791950,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+48.026085,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+48.055096,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+48.052586,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+48.436031,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+48.477104,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+48.387539,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+48.761845,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+48.693531,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+48.750523,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+48.072235,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+48.145050,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+48.124710,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+48.423340,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+48.465260,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+48.477001,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+48.724754,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+48.714428,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+48.691696,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+48.144409,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+48.047310,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+48.035248,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+48.393490,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+48.447567,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+48.427906,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+48.733063,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+48.818218,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+48.773010,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+48.041843,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+48.064266,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+48.094555,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+48.446583,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+48.393898,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+48.438004,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+48.767097,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+48.722027,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+48.713512,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+48.063873,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+48.054256,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+48.133331,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+48.470505,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+48.379482,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+48.369823,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+48.721535,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+48.704983,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+48.689220,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+48.116974,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+48.098965,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+48.044819,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+48.436371,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+48.440205,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+48.416210,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+48.738060,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+48.707603,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+48.744598,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+48.087708,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+48.030857,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+48.033585,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+48.476963,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+48.388115,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+48.354824,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+48.802917,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+48.690609,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+48.784565,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+48.039242,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+48.112801,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+48.090149,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+48.445576,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+48.381561,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+48.426994,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+48.714218,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+48.752945,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+48.808834,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+48.040199,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+48.056828,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+48.115498,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+48.447647,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+48.465206,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+48.451565,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+48.711334,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+48.741516,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+48.799961,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+48.146606,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+48.116993,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+48.106922,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+48.422497,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+48.485313,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+48.409069,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+48.721394,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+48.707657,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+48.767471,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+48.030632,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+48.078365,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+48.090004,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+48.443398,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+48.362404,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+48.439777,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+48.694069,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+48.788559,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+48.791187,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+49.110950,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+49.111591,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+49.048363,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+49.406841,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+49.378433,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+49.427490,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+49.805458,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+49.690090,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+49.722565,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+49.024281,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+49.033489,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+49.144405,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+49.358299,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+49.446136,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+49.371902,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+49.793896,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+49.782993,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+49.765976,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+49.129856,+0.500000] , 1. [-0.000000,-1.280001,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+49.114372,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+49.127190,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+49.390945,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+49.359795,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+49.390011,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+49.783127,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+49.786835,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+49.739529,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+49.093357,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+49.065456,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+49.042343,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+49.431763,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+49.383110,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+49.373207,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+49.728569,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+49.767151,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+49.734238,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+49.031883,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+49.034351,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+49.072727,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+49.432163,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+49.423855,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+49.418713,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+49.792568,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+49.774532,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+49.712837,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+49.031540,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+49.120773,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+49.102707,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+49.454113,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+49.431530,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+49.396103,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+49.802971,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+49.787231,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+49.740868,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+49.109127,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+49.034004,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+49.082333,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+49.427456,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+49.381283,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+49.391239,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+49.722721,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+49.785042,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+49.787472,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+49.135719,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+49.136475,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+49.129547,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+49.383705,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+49.449039,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+49.386208,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+49.732544,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+49.693115,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+49.740532,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+49.136383,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+49.095901,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+49.096447,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+49.365616,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+49.466022,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+49.457420,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+49.699993,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+49.810333,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+49.804466,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+49.128410,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+49.078297,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+49.040867,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+49.446209,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+49.485313,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+49.387634,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+49.791836,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+49.758736,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+49.745560,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+49.153236,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+49.116352,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+49.070900,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+49.478886,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+49.472404,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+49.475887,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+49.733551,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+49.687592,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+49.766708,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+49.065968,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+49.100441,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+49.026798,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+49.376003,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+49.452538,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+49.414894,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+49.783241,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+49.768993,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+49.811783,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+50.086609,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+50.048428,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+50.073811,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+50.359680,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+50.370567,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+50.403122,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+50.806477,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+50.707047,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+50.774162,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+50.030632,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+50.151627,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+50.024075,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+50.416973,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+50.442825,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+50.407795,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+50.793224,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+50.728447,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+50.797928,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+50.067966,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+50.103828,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+50.128571,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+50.453999,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+50.480618,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+50.425640,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+50.692554,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+50.781452,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+50.798920,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+50.049942,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+50.092468,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+50.132317,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+50.400696,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+50.366787,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+50.444698,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+50.773201,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+50.792450,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+50.689369,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+50.060757,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+50.104420,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+50.107914,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+50.366207,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+50.392979,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+50.364944,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+50.804539,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+50.779507,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+50.787670,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+50.059738,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+50.041561,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+50.062149,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+50.430374,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+50.472012,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+50.395462,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+50.709999,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+50.712841,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+50.799385,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+50.145370,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+50.066963,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+50.111458,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+50.479980,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+50.461063,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+50.357697,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+50.713184,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+50.749496,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+50.772289,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+50.143494,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+50.128426,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+50.065800,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+50.433567,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+50.418713,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+50.471294,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+50.712898,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+50.776878,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+50.712486,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+50.029079,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+50.101208,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+50.087463,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+50.394890,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+50.426998,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+50.372841,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+50.698891,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+50.803104,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+50.697094,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+50.042900,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+50.101345,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+50.048965,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+50.432339,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+50.392841,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+50.421032,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+50.690372,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+50.777466,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+50.741379,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+50.059444,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+50.109051,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+50.064964,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+50.382732,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+50.486549,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+50.449902,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+50.708652,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+50.696850,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+50.807312,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+50.052914,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+50.058376,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+50.029213,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+50.412804,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+50.485497,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+50.361420,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+50.721916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+50.795185,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+50.788738,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+51.109921,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+51.092083,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+51.029510,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+51.368679,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+51.392216,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+51.378498,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+51.723732,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+51.706669,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+51.812160,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+51.126446,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+51.040009,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+51.042156,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+51.415291,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+51.435795,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+51.473866,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+51.818092,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+51.814114,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+51.764488,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+51.055477,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+51.063442,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+51.067360,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+51.455421,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+51.375027,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+51.449863,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+51.776234,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+51.768440,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+51.692127,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+51.104530,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+51.131371,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+51.058296,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+51.441830,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+51.456440,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+51.365479,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+51.718262,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+51.710674,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+51.686832,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+51.113052,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+51.128544,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+51.137493,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+51.442169,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+51.378895,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+51.472725,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+51.686901,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+51.819294,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+51.797916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+51.140919,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+51.132343,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+51.124065,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+51.483356,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+51.480328,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+51.450912,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+51.767189,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+51.802509,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+51.731052,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+51.026524,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+51.030212,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+51.070850,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+51.434658,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+51.380913,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+51.403625,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+51.752487,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+51.748566,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+51.691799,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+51.138458,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+51.132057,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+51.123486,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+51.480865,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+51.474991,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+51.399265,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+51.757946,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+51.783428,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+51.795933,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+51.038002,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+51.107018,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+51.089622,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+51.434860,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+51.373199,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+51.444897,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+51.757740,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+51.799534,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+51.787121,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+51.067806,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+51.060810,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+51.028580,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+51.427433,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+51.476231,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+51.485394,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+51.710197,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+51.715252,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+51.738682,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+51.138058,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+51.083279,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+51.108013,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+51.476528,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+51.474983,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+51.449673,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+51.694569,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+51.819393,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+51.705307,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+51.123283,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+51.073353,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+51.101631,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+51.444935,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+51.483742,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+51.411709,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+51.804104,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+51.807636,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+51.811527,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+52.146057,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+52.112904,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+52.107677,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+52.413246,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+52.448528,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+52.439362,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+52.770496,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+52.746460,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+52.787548,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+52.130978,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+52.077244,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+52.084179,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+52.455318,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+52.469521,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+52.376640,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+52.774586,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+52.744308,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+52.715279,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+52.063217,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+52.027222,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+52.034927,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+52.427879,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+52.397537,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+52.375614,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+52.749340,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+52.747417,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+52.795052,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+52.080704,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+52.050377,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+52.092632,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+52.448448,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+52.388504,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+52.376545,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+52.734222,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+52.785336,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+52.718651,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+52.079586,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+52.023907,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+52.078564,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+52.394817,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+52.412090,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+52.435902,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+52.786991,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+52.784309,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+52.788776,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+52.099876,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+52.134495,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+52.149227,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+52.474514,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+52.367504,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+52.374836,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+52.695297,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+52.799091,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+52.740726,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+52.133926,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+52.038795,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+52.082916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+52.353386,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+52.370533,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+52.432178,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+52.801041,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+52.792294,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+52.803272,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+52.069508,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+52.114250,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+52.089855,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+52.461704,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+52.435486,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+52.390480,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+52.712536,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+52.752079,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+52.739601,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+52.100281,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+52.038990,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+52.080070,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+52.457672,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+52.466881,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+52.375031,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+52.776203,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+52.760059,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+52.749352,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+52.130112,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+52.116524,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+52.075508,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+52.472267,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+52.412598,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+52.426620,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+52.770329,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+52.797249,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+52.755424,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+52.032738,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+52.113850,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+52.125439,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+52.374916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+52.398937,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+52.432194,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+52.743210,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+52.703701,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+52.760284,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+52.140163,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+52.109859,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+52.082611,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+52.363880,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+52.445183,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+52.369133,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+52.785881,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+52.775578,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+52.804432,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-2.txt b/scenes/cell-growth/results/particles-frame-2.txt new file mode 100644 index 00000000..7aeaef96 --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-2.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+45.802944,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+45.904190,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+45.876522,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+46.132816,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+46.221092,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+46.132572,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+46.480152,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+46.568466,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+46.533466,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+45.887772,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+45.856586,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+45.906773,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+46.121101,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+46.153625,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+46.157360,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+46.572422,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+46.472229,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+46.537472,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+45.886803,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+45.849518,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+45.886986,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+46.130970,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+46.180599,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+46.122929,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+46.475616,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+46.538963,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+46.514446,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+45.877285,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+45.882263,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+45.830383,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+46.173103,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+46.229870,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+46.166687,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+46.504116,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+46.496716,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+46.467060,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+45.852230,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+45.874016,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+45.803440,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+46.171394,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+46.214767,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+46.163269,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+46.579994,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+46.511276,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+46.498508,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+45.797394,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+45.792107,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+45.860455,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+46.115593,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+46.173061,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+46.126278,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+46.506477,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+46.456902,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+46.472454,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+45.913033,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+45.833206,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+45.819019,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+46.221413,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+46.215340,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+46.187298,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+46.555801,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+46.491890,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+46.531540,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+45.787636,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+45.873913,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+45.904499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+46.143787,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+46.145145,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+46.123615,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+46.570518,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+46.466202,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+46.533772,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+45.836304,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+45.814682,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+45.818504,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+46.124493,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+46.114052,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+46.137833,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+46.528034,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+46.503113,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+46.517921,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+45.805908,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+45.820656,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+45.802891,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+46.241375,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+46.116268,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+46.222309,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+46.527363,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+46.481312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+46.497128,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+45.851368,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+45.894226,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+45.905922,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+46.210819,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+46.127945,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+46.157555,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+46.560822,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+46.546276,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+46.458492,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+45.866802,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+45.881874,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+45.851841,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+46.213688,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+46.176178,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+46.154491,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+46.525936,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+46.562908,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+46.453362,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+46.810925,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+46.878880,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+46.824337,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+47.172691,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+47.216110,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+47.143452,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+47.470478,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+47.527279,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+47.447594,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+46.860180,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+46.821026,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+46.783714,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+47.173225,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+47.179550,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+47.225895,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+47.496639,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+47.535515,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+47.505241,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+46.787991,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+46.823990,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+46.873726,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+47.231411,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+47.146866,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+47.119705,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+47.544903,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+47.474430,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+47.463440,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+46.818253,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+46.797508,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+46.797512,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+47.136318,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+47.220882,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+47.127998,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+47.505692,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+47.539486,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+47.520477,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+46.855446,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+46.792046,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+46.911552,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+47.167580,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+47.160812,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+47.144241,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+47.479389,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+47.572891,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+47.540436,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+46.802341,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+46.796993,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+46.818935,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+47.174603,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+47.242840,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+47.221825,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+47.561768,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+47.502148,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+47.453201,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+46.807671,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+46.827614,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+46.803856,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+47.137779,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+47.118080,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+47.232201,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+47.539417,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+47.547279,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+47.531830,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+46.831440,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+46.795254,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+46.790173,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+47.233574,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+47.237206,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+47.138062,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+47.541832,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+47.482292,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+47.542774,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+46.818985,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+46.886238,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+46.838654,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+47.134506,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+47.121410,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+47.245647,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+47.456230,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+47.449707,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+47.501865,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+46.807430,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+46.795666,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+46.906071,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+47.220215,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+47.182472,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+47.216961,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+47.480453,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+47.497509,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+47.515381,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+46.893562,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+46.831264,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+46.787273,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+47.178654,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+47.158733,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+47.174473,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+47.498955,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+47.557613,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+47.471481,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+46.812786,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+46.883644,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+46.843212,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+47.206203,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+47.210907,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+47.148209,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+47.512623,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+47.501846,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+47.474728,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+47.842819,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+47.802746,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+47.864876,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+48.204178,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+48.183079,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+48.238644,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+48.565144,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+48.450775,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+48.472240,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+47.832676,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+47.903042,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+47.906700,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+48.180191,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+48.123211,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+48.158241,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+48.526165,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+48.556046,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+48.475281,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+47.816925,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+47.800323,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+47.822945,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+48.232449,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+48.139565,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+48.114582,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+48.452370,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+48.458794,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+48.508930,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+47.804150,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+47.883358,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+47.883484,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+48.136120,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+48.243362,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+48.194710,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+48.558979,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+48.467392,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+48.523460,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+47.810749,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+47.897804,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+47.868919,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+48.225727,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+48.205551,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+48.134155,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+48.510445,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+48.522343,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+48.540344,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+47.810043,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+47.849102,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+47.789261,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+48.173717,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+48.128052,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+48.169205,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+48.501278,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+48.516338,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+48.464985,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+47.863819,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+47.804478,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+47.807293,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+48.152088,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+48.167774,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+48.234245,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+48.491421,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+48.578449,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+48.558315,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+47.895515,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+47.780548,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+47.912243,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+48.227329,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+48.125317,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+48.201065,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+48.495155,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+48.490654,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+48.529613,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+47.794968,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+47.823124,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+47.846748,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+48.236465,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+48.228050,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+48.155308,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+48.538185,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+48.534489,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+48.564144,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+47.910210,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+47.825619,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+47.847214,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+48.246494,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+48.126347,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+48.193115,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+48.481190,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+48.548794,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+48.449200,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+47.846424,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+47.780346,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+47.802284,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+48.158394,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+48.230965,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+48.203506,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+48.493107,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+48.520477,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+48.501457,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+47.818535,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+47.785461,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+47.897755,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+48.122669,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+48.244213,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+48.199207,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+48.497734,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+48.493370,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+48.505165,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+48.882103,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+48.843143,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+48.846684,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+49.232029,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+49.204918,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+49.122143,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+49.547897,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+49.459679,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+49.504280,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+48.910736,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+48.892597,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+48.795387,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+49.172375,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+49.155067,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+49.221771,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+49.534855,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+49.447208,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+49.453506,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+48.821655,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+48.846550,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+48.823246,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+49.190720,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+49.186069,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+49.188683,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+49.540325,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+49.557026,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+49.496914,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+48.835785,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+48.885273,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+48.869606,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+49.179485,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+49.210827,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+49.197147,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+49.449177,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+49.498940,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+49.558613,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+48.883923,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+48.837734,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+48.906635,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+49.178215,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+49.194057,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+49.116665,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+49.457390,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+49.561253,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+49.483135,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+48.837795,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+48.816422,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+48.862316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+49.245594,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+49.130089,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+49.158623,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+49.455696,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+49.537548,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+49.464420,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+48.819820,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+48.859341,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+48.816277,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+49.178322,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+49.118992,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+49.168842,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+49.518089,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+49.490341,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+49.505653,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+48.849926,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+48.797642,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+48.797680,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+49.213028,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+49.173832,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+49.224617,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+49.461712,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+49.569004,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+49.500114,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+48.802582,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+48.868370,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+48.879559,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+49.187679,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+49.126755,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+49.223892,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+49.575474,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+49.547825,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+49.553478,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+48.903023,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+48.871330,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+48.863571,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+49.231300,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+49.230820,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+49.170288,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+49.491741,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+49.493149,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+49.539333,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+48.852478,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+48.901176,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+48.811028,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+49.126530,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+49.158482,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+49.174973,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+49.509544,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+49.454754,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+49.542599,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+48.889225,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+48.879562,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+48.813721,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+49.185276,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+49.232635,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+49.187279,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+49.469017,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+49.539246,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+49.462765,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+49.863064,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+49.825409,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+49.889473,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+50.162197,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+50.171391,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+50.131611,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+50.523064,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+50.516663,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+50.473930,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+49.835064,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+49.810524,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+49.897449,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+50.210888,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+50.169056,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+50.155972,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+50.522472,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+50.506546,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+50.559635,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+49.862354,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+49.845078,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+49.793247,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+50.220634,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+50.197037,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+50.244110,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+50.475979,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+50.472553,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+50.549400,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+49.802650,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+49.817768,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+49.878353,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+50.245007,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+50.135639,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+50.196423,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+50.466854,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+50.515385,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+50.494068,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+49.818066,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+49.891956,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+49.912567,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+50.210842,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+50.158768,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+50.245922,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+50.464203,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+50.452271,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+50.520473,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+49.817760,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+49.870605,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+49.897079,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+50.149555,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+50.201660,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+50.173416,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+50.517616,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+50.520676,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+50.465958,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+49.794865,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+49.842525,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+49.851364,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+50.193115,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+50.136765,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+50.243717,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+50.467773,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+50.542271,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+50.484058,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+49.860367,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+49.911037,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+49.848206,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+50.151009,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+50.235966,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+50.157463,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+50.573330,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+50.563393,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+50.478264,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+49.795837,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+49.837944,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+49.785915,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+50.158657,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+50.138664,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+50.191162,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+50.471592,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+50.486126,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+50.556019,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+49.824432,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+49.858292,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+49.837727,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+50.186977,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+50.175488,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+50.149410,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+50.483498,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+50.526726,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+50.537590,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+49.866737,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+49.781227,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+49.807178,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+50.166153,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+50.188110,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+50.168964,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+50.526489,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+50.468964,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+50.555538,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+49.874802,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+49.878670,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+49.802670,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+50.131824,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+50.185612,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+50.113949,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+50.511543,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+50.566799,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+50.507473,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+50.892479,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+50.799820,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+50.909985,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+51.136913,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+51.121243,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+51.154503,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+51.522892,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+51.564522,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+51.486511,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+50.807316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+50.911087,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+50.828312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+51.164536,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+51.114857,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+51.117729,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+51.492798,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+51.476360,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+51.546627,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+50.849094,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+50.867752,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+50.873810,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+51.188576,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+51.200474,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+51.134010,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+51.463711,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+51.489311,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+51.543316,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+50.790604,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+50.854797,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+50.832207,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+51.121967,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+51.165524,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+51.233894,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+51.458977,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+51.451351,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+51.478802,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+50.787647,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+50.800171,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+50.813881,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+51.169903,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+51.137383,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+51.123806,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+51.449898,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+51.468018,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+51.493477,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+50.832462,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+50.889915,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+50.910831,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+51.113800,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+51.205654,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+51.223316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+51.568504,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+51.571106,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+51.573662,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+50.794312,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+50.791439,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+50.866913,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+51.147495,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+51.164040,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+51.176228,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+51.489231,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+51.562592,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+51.513691,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+50.837902,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+50.781677,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+50.869293,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+51.168350,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+51.117691,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+51.207333,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+51.527668,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+51.450237,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+51.492683,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+50.896698,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+50.858845,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+50.856270,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+51.209053,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+51.122665,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+51.125690,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+51.484413,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+51.520859,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+51.509212,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+50.893093,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+50.901287,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+50.814514,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+51.117584,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+51.129322,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+51.144119,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+51.456512,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+51.468975,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+51.519238,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+50.902634,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+50.829941,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+50.889565,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+51.216930,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+51.234570,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+51.219631,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+51.454082,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+51.564289,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+51.555710,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+50.892509,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+50.905720,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+50.851730,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+51.216446,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+51.209957,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+51.139774,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+51.481308,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+51.565639,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+51.548096,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+51.881496,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+51.871964,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+51.796505,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+52.235477,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+52.123497,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+52.167549,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+52.525780,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+52.558605,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+52.532318,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+51.853634,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+51.861454,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+51.866192,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+52.169048,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+52.174988,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+52.158253,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+52.465733,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+52.451797,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+52.502682,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+51.834320,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+51.850040,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+51.786774,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+52.154591,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+52.195087,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+52.239731,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+52.481979,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+52.465904,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+52.540226,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+51.806992,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+51.883888,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+51.896824,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+52.164265,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+52.167984,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+52.218609,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+52.535507,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+52.462753,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+52.510941,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+51.848412,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+51.783199,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+51.878643,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+52.240841,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+52.182465,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+52.240620,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+52.481659,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+52.553013,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+52.489872,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+51.876171,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+51.809631,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+51.887539,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+52.126686,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+52.208252,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+52.204548,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+52.561935,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+52.545334,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+52.490555,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+51.791561,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+51.854706,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+51.825203,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+52.243484,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+52.222103,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+52.196999,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+52.547985,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+52.551144,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+52.532425,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+51.825417,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+51.825905,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+51.851700,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+52.159489,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+52.126080,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+52.143570,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+52.541298,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+52.499847,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+52.543720,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+51.815636,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+51.877441,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+51.809967,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+52.216972,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+52.203094,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+52.236656,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+52.552811,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+52.513977,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+52.558311,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+51.852272,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+51.804672,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+51.847149,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+52.122658,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+52.183147,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+52.198681,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+52.508312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+52.460232,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+52.516727,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+51.906742,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+51.798454,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+51.813194,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+52.201771,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+52.131973,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+52.214588,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+52.475372,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+52.466148,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+52.503990,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+51.781601,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+51.789486,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+51.799999,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+52.211243,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+52.114635,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+52.211922,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+52.481850,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+52.486797,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+52.456337,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+52.911324,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+52.782845,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+52.912510,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+53.203552,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+53.199879,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+53.158520,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+53.526646,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+53.557877,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+53.454491,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+52.868206,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+52.845871,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+52.880825,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+53.177845,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+53.164875,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+53.157612,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+53.545944,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+53.467381,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+53.507698,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+52.897835,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+52.804306,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+52.833557,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+53.180267,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+53.191383,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+53.170193,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+53.492859,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+53.506622,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+53.495773,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+52.821602,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+52.907032,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+52.811214,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+53.180061,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+53.198360,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+53.148651,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+53.455486,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+53.549103,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+53.453415,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+52.791042,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+52.862709,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+52.847740,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+53.225967,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+53.125336,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+53.186188,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+53.541546,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+53.495121,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+53.576729,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+52.806698,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+52.882584,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+52.875153,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+53.168686,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+53.135563,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+53.183414,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+53.469849,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+53.552006,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+53.459457,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+52.817692,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+52.911148,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+52.873291,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+53.186829,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+53.142197,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+53.116188,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+53.525043,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+53.514210,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+53.494804,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+52.853928,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+52.871292,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+52.897633,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+53.149773,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+53.244049,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+53.171677,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+53.542946,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+53.459354,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+53.565117,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+52.833065,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+52.902470,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+52.848270,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+53.234364,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+53.165577,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+53.212559,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+53.486897,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+53.570412,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+53.528854,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+52.866768,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+52.893520,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+52.820560,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+53.135799,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+53.187538,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+53.232536,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+53.456528,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+53.539223,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+53.498337,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+52.804413,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+52.873928,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+52.863037,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+53.226967,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+53.229916,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+53.175793,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+53.532169,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+53.530499,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+53.556458,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+52.882755,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+52.842030,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+52.798725,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+53.134281,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+53.145241,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+53.177383,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+53.478268,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+53.477962,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+53.529411,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+53.806938,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+53.875629,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+53.797855,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+54.158478,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+54.182049,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+54.181355,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+54.493816,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+54.495281,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+54.551952,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+53.786087,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+53.815098,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+53.812588,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+54.196033,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+54.237106,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+54.147541,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+54.521847,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+54.453533,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+54.510525,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+53.832237,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+53.905052,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+53.884712,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+54.183342,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+54.225262,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+54.237003,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+54.484756,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+54.474430,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+54.451698,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+53.904411,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+53.807312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+53.795250,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+54.153492,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+54.207569,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+54.187908,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+54.493065,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+54.578220,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+54.533012,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+53.801846,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+53.824268,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+53.854557,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+54.206585,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+54.153900,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+54.198006,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+54.527100,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+54.482029,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+54.473515,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+53.823875,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+53.814259,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+53.893333,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+54.230507,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+54.139484,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+54.129826,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+54.481537,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+54.464985,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+54.449223,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+53.876976,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+53.858967,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+53.804821,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+54.196373,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+54.200207,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+54.176212,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+54.498062,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+54.467606,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+54.504601,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+53.847710,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+53.790859,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+53.793587,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+54.236965,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+54.148117,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+54.114826,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+54.562920,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+54.450611,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+54.544567,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+53.799244,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+53.872803,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+53.850151,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+54.205578,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+54.141563,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+54.186996,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+54.474220,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+54.512947,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+54.568836,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+53.800201,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+53.816830,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+53.875500,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+54.207649,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+54.225208,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+54.211567,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+54.471336,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+54.501518,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+54.559963,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+53.906609,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+53.876995,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+53.866924,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+54.182499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+54.245316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+54.169071,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+54.481396,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+54.467659,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+54.527473,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+53.790634,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+53.838367,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+53.850006,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+54.203400,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+54.122406,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+54.199780,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+54.454071,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+54.548561,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+54.551189,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+54.870953,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+54.871593,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+54.808365,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+55.166843,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+55.138435,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+55.187492,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+55.565456,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+55.450092,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+55.482567,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+54.784283,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+54.793491,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+54.904408,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+55.118301,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+55.206139,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+55.131905,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+55.553898,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+55.542995,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+55.525978,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+54.889858,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+54.874374,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+54.887192,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+55.150948,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+55.119797,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+55.150013,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+55.543129,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+55.546837,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+55.499531,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+54.853359,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+54.825459,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+54.802345,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+55.191765,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+55.143112,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+55.133209,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+55.488571,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+55.527153,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+55.494240,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+54.791885,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+54.794353,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+54.832729,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+55.192165,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+55.183857,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+55.178715,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+55.552570,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+55.534534,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+55.472839,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+54.791542,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+54.880775,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+54.862709,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+55.214115,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+55.191532,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+55.156105,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+55.562973,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+55.547234,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+55.500870,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+54.869129,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+54.794006,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+54.842335,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+55.187458,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+55.141285,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+55.151241,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+55.482723,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+55.545044,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+55.547474,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+54.895721,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+54.896477,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+54.889549,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+55.143707,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+55.209042,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+55.146210,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+55.492546,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+55.453117,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+55.500534,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+54.896385,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+54.855904,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+54.856449,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+55.125618,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+55.226025,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+55.217422,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+55.459995,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+55.570335,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+55.564468,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+54.888412,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+54.838299,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+54.800869,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+55.206211,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+55.245316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+55.147636,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+55.551838,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+55.518738,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+55.505562,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+54.913239,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+54.876354,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+54.830902,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+55.238888,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+55.232407,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+55.235889,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+55.493553,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+55.447594,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+55.526711,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+54.825970,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+54.860443,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+54.786800,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+55.136005,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+55.212540,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+55.174896,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+55.543243,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+55.528996,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+55.571785,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+55.846611,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+55.808430,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+55.833813,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+56.119682,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+56.130569,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+56.163124,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+56.566479,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+56.467045,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+56.534164,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+55.790634,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+55.911629,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+55.784077,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+56.176975,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+56.202827,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+56.167797,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+56.553226,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+56.488449,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+56.557930,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+55.827969,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+55.863831,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+55.888573,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+56.214001,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+56.240620,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+56.185642,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+56.452557,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+56.541454,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+56.558922,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+55.809944,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+55.852470,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+55.892319,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+56.160698,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+56.126789,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+56.204704,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+56.533203,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+56.552452,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+56.449371,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+55.820759,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+55.864422,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+55.867916,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+56.126209,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+56.152985,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+56.124947,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+56.564541,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+56.539509,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+56.547672,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+55.819740,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+55.801563,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+55.822151,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+56.190376,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+56.232014,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+56.155464,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+56.470001,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+56.472843,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+56.559387,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+55.905373,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+55.826965,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+55.871460,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+56.239983,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+56.221066,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+56.117699,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+56.473186,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+56.509499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+56.532291,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+55.903496,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+55.888428,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+55.825802,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+56.193569,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+56.178715,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+56.231297,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+56.472900,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+56.536880,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+56.472488,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+55.789082,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+55.861210,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+55.847466,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+56.154892,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+56.187000,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+56.132843,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+56.458893,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+56.563107,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+56.457096,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+55.802902,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+55.861347,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+55.808968,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+56.192341,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+56.152843,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+56.181034,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+56.450375,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+56.537468,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+56.501381,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+55.819447,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+55.869053,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+55.824966,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+56.142735,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+56.246552,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+56.209904,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+56.468655,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+56.456852,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+56.567314,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+55.812916,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+55.818378,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+55.789215,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+56.172806,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+56.245499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+56.121422,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+56.481918,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+56.555187,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+56.548740,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+56.869923,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+56.852085,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+56.789513,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+57.128681,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+57.152218,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+57.138500,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+57.483734,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+57.466671,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+57.572163,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+56.886448,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+56.800011,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+56.802158,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+57.175293,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+57.195797,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+57.233868,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+57.578094,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+57.574116,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+57.524490,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+56.815479,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+56.823444,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+56.827362,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+57.215424,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+57.135029,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+57.209866,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+57.536236,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+57.528442,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+57.452129,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+56.864532,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+56.891373,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+56.818298,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+57.201832,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+57.216442,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+57.125481,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+57.478264,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+57.470676,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+57.446835,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+56.873055,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+56.888550,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+56.897495,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+57.202171,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+57.138897,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+57.232727,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+57.446903,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+57.579296,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+57.557919,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+56.900925,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+56.892345,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+56.884071,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+57.243359,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+57.240334,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+57.210915,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+57.527191,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+57.562515,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+57.491055,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+56.786526,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+56.790215,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+56.830853,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+57.194660,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+57.140915,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+57.163628,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+57.512489,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+57.508568,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+57.451801,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+56.898460,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+56.892059,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+56.883488,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+57.240868,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+57.234993,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+57.159267,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+57.517948,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+57.543430,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+57.555935,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+56.798004,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+56.867020,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+56.849625,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+57.194862,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+57.133202,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+57.204899,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+57.517742,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+57.559536,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+57.547123,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+56.827808,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+56.820812,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+56.788582,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+57.187435,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+57.236233,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+57.245396,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+57.470200,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+57.475254,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+57.498684,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+56.898060,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+56.843281,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+56.868015,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+57.236530,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+57.234985,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+57.209675,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+57.454571,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+57.579395,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+57.465309,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+56.883286,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+56.833355,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+56.861633,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+57.204937,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+57.243744,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+57.171711,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+57.564106,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+57.567638,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+57.571529,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+57.906059,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+57.872906,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+57.867680,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+58.173248,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+58.208530,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+58.199364,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+58.530499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+58.506462,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+58.547550,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+57.890980,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+57.837246,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+57.844181,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+58.215321,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+58.229523,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+58.136642,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+58.534588,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+58.504311,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+58.475281,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+57.823219,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+57.787224,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+57.794930,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+58.187881,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+58.157539,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+58.135616,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+58.509342,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+58.507420,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+58.555054,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+57.840706,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+57.810379,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+57.852634,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+58.208450,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+58.148506,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+58.136547,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+58.494225,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+58.545338,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+58.478653,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+57.839588,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+57.783909,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+57.838566,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+58.154819,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+58.172092,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+58.195904,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+58.546993,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+58.544312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+58.548779,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+57.859879,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+57.894497,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+57.909233,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+58.234516,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+58.127510,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+58.134838,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+58.455299,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+58.559093,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+58.500732,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+57.893929,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+57.798798,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+57.842918,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+58.113388,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+58.130535,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+58.192181,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+58.561043,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+58.552296,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+58.563274,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+57.829510,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+57.874252,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+57.849857,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+58.221706,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+58.195488,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+58.150482,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+58.472538,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+58.512081,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+58.499603,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+57.860283,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+57.798992,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+57.840069,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+58.217674,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+58.226883,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+58.135029,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+58.536205,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+58.520061,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+58.509350,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+57.890114,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+57.876522,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+57.835510,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+58.232269,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+58.172600,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+58.186623,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+58.530331,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+58.557251,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+58.515427,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+57.792740,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+57.873852,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+57.885441,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+58.134918,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+58.158939,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+58.192196,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+58.503212,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+58.463703,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+58.520287,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+57.900166,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+57.869862,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+57.842613,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+58.123882,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+58.205185,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+58.129135,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+58.545883,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+58.535580,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+58.564434,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-3.txt b/scenes/cell-growth/results/particles-frame-3.txt new file mode 100644 index 00000000..2c1c0bc1 --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-3.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+45.450943,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+45.552189,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+45.524521,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+45.780815,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+45.869091,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+45.780571,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+46.128151,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+46.216465,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+46.181465,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+45.535770,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+45.504585,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+45.554771,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+45.769100,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+45.801624,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+45.805359,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+46.220421,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+46.120228,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+46.185471,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+45.534801,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+45.497517,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+45.534985,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+45.778969,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+45.828598,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+45.770927,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+46.123615,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+46.186962,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+46.162445,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+45.525284,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+45.530262,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+45.478382,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+45.821102,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+45.877869,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+45.814686,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+46.152115,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+46.144714,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+46.115059,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+45.500229,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+45.522015,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+45.451439,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+45.819393,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+45.862766,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+45.811268,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+46.227993,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+46.159275,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+46.146507,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+45.445393,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+45.440105,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+45.508453,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+45.763592,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+45.821060,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+45.774277,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+46.154476,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+46.104900,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+46.120453,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+45.561031,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+45.481205,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+45.467018,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+45.869411,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+45.863338,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+45.835297,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+46.203800,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+46.139889,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+46.179539,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+45.435635,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+45.521912,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+45.552498,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+45.791786,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+45.793144,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+45.771614,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+46.218517,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+46.114201,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+46.181770,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+45.484303,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+45.462681,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+45.466503,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+45.772491,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+45.762051,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+45.785831,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+46.176033,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+46.151112,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+46.165920,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+45.453907,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+45.468655,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+45.450890,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+45.889374,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+45.764267,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+45.870308,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+46.175362,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+46.129311,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+46.145126,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+45.499367,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+45.542225,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+45.553921,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+45.858818,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+45.775944,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+45.805553,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+46.208820,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+46.194275,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+46.106491,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+45.514801,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+45.529873,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+45.499840,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+45.861687,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+45.824177,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+45.802490,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+46.173935,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+46.210907,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+46.101360,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+46.458923,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+46.526878,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+46.472336,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+46.820690,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+46.864109,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+46.791451,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+47.118477,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+47.175278,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+47.095592,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+46.508179,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+46.469025,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+46.431713,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+46.821224,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+46.827549,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+46.873894,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+47.144638,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+47.183514,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+47.153240,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+46.435989,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+46.471989,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+46.521725,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+46.879410,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+46.794865,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+46.767704,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+47.192902,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+47.122429,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+47.111439,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+46.466251,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+46.445507,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+46.445511,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+46.784317,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+46.868881,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+46.775997,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+47.153690,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+47.187485,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+47.168476,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+46.503445,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+46.440044,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+46.559551,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+46.815578,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+46.808811,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+46.792240,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+47.127388,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+47.220890,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+47.188435,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+46.450340,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+46.444992,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+46.466934,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+46.822601,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+46.890839,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+46.869823,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+47.209766,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+47.150146,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+47.101200,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+46.455669,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+46.475613,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+46.451855,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+46.785778,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+46.766079,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+46.880199,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+47.187416,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+47.195278,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+47.179829,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+46.479439,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+46.443253,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+46.438171,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+46.881573,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+46.885204,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+46.786060,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+47.189831,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+47.130291,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+47.190773,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+46.466984,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+46.534237,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+46.486652,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+46.782505,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+46.769409,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+46.893646,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+47.104229,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+47.097706,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+47.149864,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+46.455429,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+46.443665,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+46.554070,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+46.868214,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+46.830471,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+46.864960,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+47.128452,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+47.145508,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+47.163380,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+46.541561,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+46.479263,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+46.435272,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+46.826653,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+46.806732,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+46.822472,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+47.146954,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+47.205612,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+47.119480,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+46.460785,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+46.531643,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+46.491211,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+46.854202,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+46.858906,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+46.796207,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+47.160622,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+47.149845,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+47.122726,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+47.490818,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+47.450745,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+47.512875,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+47.852177,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+47.831078,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+47.886642,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+48.213142,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+48.098774,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+48.120239,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+47.480675,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+47.551041,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+47.554699,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+47.828190,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+47.771210,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+47.806240,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+48.174164,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+48.204044,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+48.123280,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+47.464924,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+47.448322,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+47.470943,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+47.880447,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+47.787563,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+47.762581,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+48.100368,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+48.106792,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+48.156929,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+47.452148,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+47.531357,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+47.531483,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+47.784119,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+47.891361,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+47.842709,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+48.206978,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+48.115391,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+48.171459,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+47.458748,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+47.545803,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+47.516918,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+47.873726,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+47.853550,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+47.782154,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+48.158443,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+48.170341,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+48.188343,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+47.458042,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+47.497101,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+47.437260,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+47.821716,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+47.776051,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+47.817204,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+48.149277,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+48.164337,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+48.112984,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+47.511818,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+47.452477,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+47.455292,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+47.800087,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+47.815773,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+47.882244,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+48.139420,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+48.226448,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+48.206314,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+47.543514,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+47.428547,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+47.560242,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+47.875328,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+47.773315,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+47.849064,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+48.143154,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+48.138653,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+48.177612,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+47.442966,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+47.471123,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+47.494747,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+47.884464,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+47.876049,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+47.803307,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+48.186184,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+48.182487,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+48.212143,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+47.558208,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+47.473618,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+47.495213,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+47.894493,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+47.774345,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+47.841114,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+48.129189,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+48.196793,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+48.097198,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+47.494423,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+47.428345,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+47.450283,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+47.806393,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+47.878963,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+47.851505,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+48.141106,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+48.168476,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+48.149456,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+47.466534,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+47.433460,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+47.545753,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+47.770668,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+47.892212,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+47.847206,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+48.145733,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+48.141369,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+48.153164,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+48.530102,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+48.491142,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+48.494682,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+48.880028,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+48.852917,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+48.770142,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+49.195896,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+49.107677,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+49.152279,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+48.558735,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+48.540596,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+48.443386,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+48.820374,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+48.803066,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+48.869770,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+49.182854,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+49.095207,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+49.101505,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+48.469654,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+48.494549,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+48.471245,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+48.838718,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+48.834068,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+48.836681,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+49.188324,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+49.205025,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+49.144913,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+48.483784,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+48.533272,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+48.517605,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+48.827484,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+48.858826,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+48.845146,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+49.097176,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+49.146938,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+49.206612,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+48.531921,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+48.485733,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+48.554634,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+48.826214,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+48.842056,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+48.764664,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+49.105389,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+49.209251,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+49.131134,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+48.485794,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+48.464420,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+48.510315,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+48.893593,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+48.778088,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+48.806622,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+49.103695,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+49.185547,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+49.112419,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+48.467819,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+48.507339,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+48.464275,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+48.826321,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+48.766991,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+48.816841,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+49.166088,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+49.138340,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+49.153652,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+48.497925,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+48.445641,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+48.445679,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+48.861027,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+48.821831,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+48.872616,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+49.109711,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+49.217003,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+49.148113,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+48.450581,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+48.516369,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+48.527557,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+48.835678,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+48.774754,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+48.871891,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+49.223473,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+49.195824,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+49.201477,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+48.551022,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+48.519329,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+48.511570,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+48.879299,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+48.878819,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+48.818287,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+49.139740,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+49.141148,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+49.187332,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+48.500477,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+48.549175,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+48.459026,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+48.774529,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+48.806480,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+48.822971,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+49.157543,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+49.102753,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+49.190598,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+48.537224,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+48.527561,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+48.461720,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+48.833275,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+48.880634,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+48.835278,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+49.117016,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+49.187244,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+49.110764,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+49.511063,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+49.473408,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+49.537472,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+49.810196,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+49.819389,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+49.779610,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+50.171062,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+50.164661,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+50.121929,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+49.483063,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+49.458523,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+49.545448,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+49.858887,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+49.817055,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+49.803970,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+50.170471,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+50.154545,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+50.207634,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+49.510353,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+49.493076,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+49.441246,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+49.868633,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+49.845036,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+49.892109,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+50.123978,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+50.120552,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+50.197399,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+49.450649,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+49.465767,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+49.526352,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+49.893005,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+49.783638,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+49.844421,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+50.114853,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+50.163383,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+50.142067,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+49.466064,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+49.539955,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+49.560566,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+49.858841,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+49.806767,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+49.893921,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+50.112202,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+50.100269,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+50.168472,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+49.465759,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+49.518604,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+49.545078,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+49.797554,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+49.849659,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+49.821415,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+50.165615,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+50.168674,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+50.113956,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+49.442863,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+49.490524,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+49.499363,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+49.841114,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+49.784763,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+49.891716,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+50.115772,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+50.190269,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+50.132057,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+49.508366,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+49.559036,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+49.496204,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+49.799007,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+49.883965,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+49.805462,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+50.221329,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+50.211391,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+50.126263,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+49.443836,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+49.485943,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+49.433914,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+49.806656,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+49.786663,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+49.839161,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+50.119591,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+50.134125,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+50.204018,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+49.472431,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+49.506290,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+49.485725,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+49.834976,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+49.823486,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+49.797409,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+50.131496,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+50.174725,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+50.185589,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+49.514736,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+49.429226,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+49.455177,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+49.814152,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+49.836109,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+49.816963,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+50.174488,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+50.116962,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+50.203537,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+49.522800,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+49.526669,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+49.450668,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+49.779823,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+49.833611,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+49.761948,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+50.159542,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+50.214798,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+50.155472,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+50.540478,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+50.447819,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+50.557983,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+50.784912,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+50.769241,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+50.802502,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+51.170891,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+51.212521,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+51.134510,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+50.455315,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+50.559086,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+50.476311,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+50.812534,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+50.762856,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+50.765728,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+51.140797,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+51.124359,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+51.194626,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+50.497093,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+50.515751,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+50.521809,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+50.836575,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+50.848473,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+50.782009,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+51.111710,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+51.137310,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+51.191315,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+50.438602,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+50.502796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+50.480206,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+50.769966,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+50.813522,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+50.881893,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+51.106976,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+51.099350,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+51.126801,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+50.435646,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+50.448170,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+50.461880,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+50.817902,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+50.785381,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+50.771805,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+51.097897,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+51.116016,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+51.141476,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+50.480461,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+50.537914,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+50.558830,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+50.761799,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+50.853653,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+50.871315,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+51.216503,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+51.219105,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+51.221661,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+50.442310,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+50.439438,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+50.514912,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+50.795494,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+50.812038,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+50.824226,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+51.137230,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+51.210590,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+51.161690,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+50.485901,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+50.429676,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+50.517292,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+50.816349,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+50.765690,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+50.855331,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+51.175667,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+51.098236,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+51.140682,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+50.544697,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+50.506844,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+50.504269,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+50.857052,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+50.770664,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+50.773689,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+51.132412,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+51.168858,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+51.157211,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+50.541092,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+50.549286,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+50.462513,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+50.765583,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+50.777321,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+50.792118,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+51.104511,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+51.116974,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+51.167236,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+50.550632,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+50.477940,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+50.537563,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+50.864929,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+50.882568,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+50.867630,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+51.102081,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+51.212288,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+51.203709,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+50.540508,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+50.553719,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+50.499729,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+50.864445,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+50.857956,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+50.787773,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+51.129307,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+51.213638,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+51.196095,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+51.529495,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+51.519962,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+51.444504,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+51.883476,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+51.771496,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+51.815548,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+52.173779,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+52.206604,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+52.180317,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+51.501633,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+51.509453,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+51.514191,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+51.817047,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+51.822987,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+51.806252,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+52.113731,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+52.099796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+52.150681,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+51.482319,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+51.498039,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+51.434772,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+51.802589,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+51.843086,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+51.887730,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+52.129978,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+52.113903,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+52.188225,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+51.454990,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+51.531887,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+51.544823,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+51.812263,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+51.815983,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+51.866608,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+52.183506,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+52.110752,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+52.158939,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+51.496410,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+51.431198,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+51.526642,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+51.888840,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+51.830463,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+51.888618,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+52.129658,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+52.201012,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+52.137871,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+51.524170,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+51.457630,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+51.535538,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+51.774685,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+51.856251,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+51.852547,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+52.209934,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+52.193333,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+52.138554,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+51.439560,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+51.502705,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+51.473202,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+51.891483,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+51.870102,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+51.844997,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+52.195984,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+52.199142,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+52.180424,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+51.473415,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+51.473904,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+51.499699,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+51.807487,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+51.774078,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+51.791569,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+52.189297,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+52.147846,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+52.191719,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+51.463634,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+51.525440,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+51.457966,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+51.864971,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+51.851093,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+51.884655,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+52.200809,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+52.161976,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+52.206310,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+51.500271,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+51.452671,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+51.495148,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+51.770657,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+51.831146,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+51.846680,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+52.156311,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+52.108231,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+52.164726,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+51.554741,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+51.446453,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+51.461193,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+51.849770,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+51.779972,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+51.862587,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+52.123371,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+52.114147,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+52.151989,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+51.429600,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+51.437485,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+51.447998,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+51.859241,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+51.762634,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+51.859921,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+52.129848,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+52.134796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+52.104336,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+52.559322,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+52.430843,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+52.560509,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+52.851551,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+52.847878,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+52.806519,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+53.174644,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+53.205875,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+53.102489,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+52.516205,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+52.493870,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+52.528824,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+52.825844,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+52.812874,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+52.805611,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+53.193943,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+53.115379,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+53.155697,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+52.545834,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+52.452305,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+52.481556,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+52.828266,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+52.839382,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+52.818192,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+53.140858,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+53.154621,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+53.143772,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+52.469601,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+52.555031,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+52.459213,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+52.828060,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+52.846359,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+52.796650,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+53.103485,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+53.197102,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+53.101414,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+52.439041,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+52.510708,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+52.495739,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+52.873966,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+52.773335,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+52.834187,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+53.189545,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+53.143120,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+53.224728,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+52.454697,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+52.530582,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+52.523151,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+52.816685,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+52.783562,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+52.831413,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+53.117847,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+53.200005,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+53.107456,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+52.465691,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+52.559147,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+52.521290,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+52.834827,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+52.790195,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+52.764187,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+53.173042,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+53.162209,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+53.142803,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+52.501926,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+52.519291,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+52.545631,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+52.797771,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+52.892048,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+52.819675,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+53.190945,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+53.107353,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+53.213116,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+52.481064,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+52.550468,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+52.496269,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+52.882362,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+52.813576,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+52.860558,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+53.134895,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+53.218410,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+53.176853,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+52.514767,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+52.541519,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+52.468559,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+52.783798,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+52.835537,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+52.880535,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+53.104527,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+53.187222,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+53.146336,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+52.452412,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+52.521927,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+52.511036,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+52.874966,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+52.877914,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+52.823792,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+53.180168,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+53.178497,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+53.204456,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+52.530754,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+52.490028,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+52.446724,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+52.782280,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+52.793240,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+52.825382,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+53.126266,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+53.125961,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+53.177410,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+53.454937,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+53.523628,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+53.445854,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+53.806477,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+53.830048,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+53.829353,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+54.141815,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+54.143280,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+54.199951,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+53.434086,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+53.463097,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+53.460587,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+53.844032,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+53.885105,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+53.795540,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+54.169846,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+54.101532,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+54.158524,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+53.480236,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+53.553051,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+53.532711,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+53.831341,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+53.873260,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+53.885002,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+54.132755,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+54.122429,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+54.099697,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+53.552410,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+53.455311,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+53.443249,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+53.801491,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+53.855568,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+53.835907,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+54.141064,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+54.226219,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+54.181011,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+53.449844,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+53.472267,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+53.502556,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+53.854584,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+53.801899,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+53.846004,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+54.175098,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+54.130028,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+54.121513,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+53.471874,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+53.462257,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+53.541332,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+53.878506,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+53.787483,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+53.777824,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+54.129536,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+54.112984,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+54.097221,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+53.524975,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+53.506966,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+53.452820,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+53.844372,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+53.848206,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+53.824211,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+54.146061,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+54.115604,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+54.152599,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+53.495708,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+53.438858,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+53.441586,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+53.884964,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+53.796116,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+53.762825,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+54.210918,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+54.098610,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+54.192566,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+53.447243,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+53.520802,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+53.498150,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+53.853577,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+53.789562,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+53.834995,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+54.122219,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+54.160946,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+54.216835,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+53.448200,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+53.464828,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+53.523499,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+53.855648,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+53.873207,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+53.859566,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+54.119335,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+54.149517,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+54.207962,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+53.554607,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+53.524994,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+53.514923,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+53.830498,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+53.893314,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+53.817070,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+54.129395,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+54.115658,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+54.175472,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+53.438633,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+53.486366,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+53.498005,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+53.851398,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+53.770405,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+53.847778,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+54.102070,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+54.196560,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+54.199188,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+54.518951,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+54.519592,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+54.456364,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+54.814842,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+54.786434,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+54.835491,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+55.213455,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+55.098091,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+55.130566,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+54.432281,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+54.441490,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+54.552406,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+54.766300,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+54.854137,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+54.779903,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+55.201897,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+55.190994,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+55.173977,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+54.537857,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+54.522373,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+54.535191,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+54.798946,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+54.767796,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+54.798012,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+55.191128,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+55.194836,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+55.147530,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+54.501358,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+54.473457,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+54.450344,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+54.839764,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+54.791111,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+54.781208,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+55.136570,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+55.175152,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+55.142239,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+54.439884,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+54.442352,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+54.480728,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+54.840164,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+54.831856,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+54.826714,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+55.200569,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+55.182533,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+55.120838,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+54.439541,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+54.528774,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+54.510708,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+54.862114,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+54.839531,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+54.804104,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+55.210972,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+55.195232,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+55.148869,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+54.517128,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+54.442005,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+54.490334,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+54.835457,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+54.789284,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+54.799240,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+55.130722,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+55.193043,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+55.195473,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+54.543720,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+54.544476,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+54.537548,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+54.791706,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+54.857040,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+54.794209,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+55.140545,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+55.101116,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+55.148533,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+54.544384,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+54.503902,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+54.504448,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+54.773617,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+54.874023,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+54.865421,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+55.107994,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+55.218334,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+55.212467,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+54.536411,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+54.486298,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+54.448868,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+54.854210,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+54.893314,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+54.795635,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+55.199837,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+55.166737,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+55.153561,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+54.561237,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+54.524353,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+54.478901,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+54.886887,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+54.880405,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+54.883888,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+55.141552,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+55.095592,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+55.174709,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+54.473969,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+54.508442,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+54.434799,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+54.784004,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+54.860538,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+54.822895,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+55.191242,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+55.176994,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+55.219784,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+55.494610,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+55.456429,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+55.481812,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+55.767681,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+55.778568,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+55.811123,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+56.214478,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+56.115044,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+56.182163,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+55.438633,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+55.559628,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+55.432076,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+55.824974,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+55.850826,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+55.815796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+56.201225,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+56.136448,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+56.205929,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+55.475967,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+55.511829,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+55.536572,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+55.862000,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+55.888618,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+55.833641,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+56.100555,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+56.189453,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+56.206921,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+55.457943,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+55.500469,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+55.540318,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+55.808697,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+55.774788,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+55.852703,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+56.181202,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+56.200451,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+56.097370,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+55.468758,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+55.512421,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+55.515915,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+55.774208,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+55.800983,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+55.772945,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+56.212540,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+56.187508,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+56.195671,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+55.467739,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+55.449562,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+55.470150,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+55.838375,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+55.880013,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+55.803463,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+56.118000,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+56.120842,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+56.207386,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+55.553371,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+55.474964,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+55.519459,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+55.887981,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+55.869064,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+55.765697,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+56.121185,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+56.157497,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+56.180290,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+55.551495,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+55.536427,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+55.473801,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+55.841568,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+55.826714,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+55.879295,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+56.120899,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+56.184879,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+56.120487,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+55.437080,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+55.509209,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+55.495464,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+55.802891,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+55.834999,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+55.780842,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+56.106892,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+56.211105,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+56.105095,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+55.450901,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+55.509346,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+55.456966,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+55.840340,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+55.800842,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+55.829033,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+56.098373,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+56.185467,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+56.149380,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+55.467445,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+55.517052,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+55.472965,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+55.790733,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+55.894550,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+55.857903,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+56.116653,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+56.104851,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+56.215313,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+55.460915,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+55.466377,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+55.437214,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+55.820805,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+55.893497,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+55.769421,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+56.129917,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+56.203186,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+56.196739,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+56.517921,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+56.500084,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+56.437511,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+56.776680,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+56.800217,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+56.786499,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+57.131733,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+57.114670,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+57.220161,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+56.534447,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+56.448009,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+56.450157,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+56.823292,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+56.843796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+56.881866,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+57.226093,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+57.222115,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+57.172489,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+56.463478,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+56.471443,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+56.475361,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+56.863422,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+56.783028,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+56.857864,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+57.184235,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+57.176441,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+57.100128,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+56.512531,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+56.539371,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+56.466297,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+56.849831,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+56.864441,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+56.773479,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+57.126263,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+57.118675,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+57.094833,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+56.521053,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+56.536549,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+56.545494,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+56.850170,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+56.786896,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+56.880726,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+57.094902,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+57.227295,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+57.205917,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+56.548923,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+56.540344,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+56.532070,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+56.891357,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+56.888332,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+56.858913,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+57.175190,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+57.210514,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+57.139053,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+56.434525,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+56.438213,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+56.478851,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+56.842659,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+56.788914,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+56.811626,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+57.160488,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+57.156567,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+57.099800,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+56.546459,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+56.540058,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+56.531487,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+56.888866,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+56.882992,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+56.807266,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+57.165947,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+57.191429,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+57.203934,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+56.446003,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+56.515018,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+56.497623,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+56.842861,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+56.781200,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+56.852898,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+57.165741,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+57.207535,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+57.195122,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+56.475807,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+56.468811,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+56.436581,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+56.835434,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+56.884232,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+56.893394,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+57.118198,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+57.123253,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+57.146683,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+56.546059,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+56.491280,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+56.516014,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+56.884529,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+56.882984,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+56.857674,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+57.102570,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+57.227394,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+57.113308,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+56.531284,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+56.481354,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+56.509632,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+56.852936,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+56.891743,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+56.819710,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+57.212105,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+57.215637,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+57.219528,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+57.554058,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+57.520905,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+57.515678,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+57.821247,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+57.856529,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+57.847363,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+58.178497,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+58.154461,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+58.195549,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+57.538979,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+57.485245,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+57.492180,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+57.863319,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+57.877522,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+57.784641,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+58.182587,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+58.152309,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+58.123280,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+57.471218,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+57.435223,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+57.442928,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+57.835880,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+57.805538,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+57.783615,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+58.157341,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+58.155418,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+58.203053,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+57.488705,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+57.458378,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+57.500633,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+57.856449,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+57.796505,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+57.784546,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+58.142223,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+58.193336,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+58.126652,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+57.487587,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+57.431908,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+57.486565,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+57.802818,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+57.820091,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+57.843903,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+58.194992,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+58.192310,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+58.196777,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+57.507877,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+57.542496,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+57.557232,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+57.882515,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+57.775509,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+57.782837,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+58.103298,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+58.207092,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+58.148731,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+57.541927,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+57.446796,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+57.490917,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+57.761387,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+57.778534,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+57.840179,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+58.209042,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+58.200294,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+58.211273,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+57.477509,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+57.522251,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+57.497856,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+57.869705,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+57.843487,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+57.798481,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+58.120537,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+58.160080,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+58.147602,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+57.508282,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+57.446991,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+57.488068,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+57.865673,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+57.874882,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+57.783028,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+58.184204,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+58.168060,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+58.157349,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+57.538113,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+57.524521,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+57.483509,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+57.880268,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+57.820599,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+57.834621,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+58.178329,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+58.205250,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+58.163425,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+57.440739,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+57.521851,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+57.533440,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+57.782917,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+57.806938,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+57.840195,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+58.151211,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+58.111702,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+58.168285,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+57.548164,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+57.517860,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+57.490612,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+57.771881,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+57.853184,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+57.777134,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+58.193882,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+58.183578,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+58.212433,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-4.txt b/scenes/cell-growth/results/particles-frame-4.txt new file mode 100644 index 00000000..7178f2d4 --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-4.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+44.970943,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+45.072189,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+45.044521,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+45.300816,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+45.389091,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+45.300571,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+45.648151,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+45.736465,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+45.701466,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+45.055771,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+45.024586,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+45.074772,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+45.289101,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+45.321625,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+45.325359,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+45.740421,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+45.640228,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+45.705471,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+45.054802,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+45.017517,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+45.054985,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+45.298969,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+45.348598,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+45.290928,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+45.643616,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+45.706963,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+45.682446,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+45.045284,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+45.050262,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+44.998383,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+45.341103,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+45.397869,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+45.334686,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+45.672115,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+45.664715,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+45.635059,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+45.020229,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+45.042015,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+44.971439,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+45.339394,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+45.382767,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+45.331268,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+45.747993,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+45.679276,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+45.666508,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+44.965393,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+44.960106,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+45.028454,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+45.283592,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+45.341061,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+45.294277,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+45.674477,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+45.624901,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+45.640453,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+45.081032,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+45.001205,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+44.987019,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+45.389412,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+45.383339,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+45.355297,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+45.723801,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+45.659889,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+45.699539,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+44.955635,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+45.041912,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+45.072498,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+45.311787,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+45.313145,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+45.291615,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+45.738518,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+45.634201,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+45.701771,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+45.004303,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+44.982681,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+44.986504,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+45.292492,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+45.282051,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+45.305832,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+45.696033,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+45.671112,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+45.685921,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+44.973907,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+44.988655,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+44.970890,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+45.409374,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+45.284267,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+45.390308,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+45.695362,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+45.649311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+45.665127,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+45.019367,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+45.062225,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+45.073921,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+45.378819,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+45.295944,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+45.325554,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+45.728821,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+45.714275,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+45.626492,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+45.034801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+45.049873,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+45.019840,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+45.381687,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+45.344177,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+45.322491,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+45.693935,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+45.730907,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+45.621361,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+45.978924,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+46.046879,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+45.992336,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+46.340691,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+46.384109,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+46.311451,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+46.638477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+46.695278,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+46.615593,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+46.028179,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+45.989025,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+45.951714,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+46.341225,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+46.347549,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+46.393894,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+46.664639,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+46.703514,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+46.673241,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+45.955990,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+45.991989,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+46.041725,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+46.399410,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+46.314865,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+46.287704,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+46.712902,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+46.642429,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+46.631439,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+45.986252,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+45.965508,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+45.965511,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+46.304317,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+46.388882,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+46.295998,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+46.673691,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+46.707485,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+46.688477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+46.023445,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+45.960045,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+46.079552,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+46.335579,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+46.328812,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+46.312241,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+46.647388,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+46.740891,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+46.708435,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+45.970341,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+45.964993,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+45.986935,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+46.342602,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+46.410839,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+46.389824,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+46.729767,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+46.670147,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+46.621201,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+45.975670,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+45.995613,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+45.971855,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+46.305779,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+46.286079,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+46.400200,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+46.707417,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+46.715279,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+46.699829,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+45.999439,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+45.963253,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+45.958172,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+46.401573,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+46.405205,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+46.306061,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+46.709831,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+46.650291,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+46.710773,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+45.986984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+46.054237,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+46.006653,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+46.302505,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+46.289410,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+46.413647,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+46.624229,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+46.617706,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+46.669865,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+45.975430,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+45.963665,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+46.074070,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+46.388214,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+46.350471,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+46.384960,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+46.648453,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+46.665508,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+46.683380,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+46.061562,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+45.999264,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+45.955273,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+46.346653,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+46.326733,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+46.342472,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+46.666954,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+46.725613,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+46.639481,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+45.980785,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+46.051643,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+46.011211,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+46.374203,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+46.378906,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+46.316208,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+46.680622,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+46.669846,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+46.642727,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+47.010818,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+46.970745,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+47.032875,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+47.372177,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+47.351078,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+47.406643,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+47.733143,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+47.618774,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+47.640240,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+47.000675,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+47.071041,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+47.074699,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+47.348190,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+47.291210,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+47.326241,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+47.694164,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+47.724045,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+47.643280,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+46.984924,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+46.968323,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+46.990944,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+47.400448,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+47.307564,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+47.282581,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+47.620369,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+47.626793,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+47.676929,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+46.972149,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+47.051357,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+47.051483,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+47.304119,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+47.411362,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+47.362709,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+47.726978,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+47.635391,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+47.691460,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+46.978748,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+47.065804,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+47.036919,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+47.393726,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+47.373550,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+47.302155,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+47.678444,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+47.690342,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+47.708344,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+46.978043,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+47.017101,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+46.957260,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+47.341717,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+47.296051,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+47.337204,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+47.669277,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+47.684338,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+47.632984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+47.031818,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+46.972477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+46.975292,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+47.320087,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+47.335773,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+47.402245,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+47.659420,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+47.746449,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+47.726315,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+47.063515,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+46.948547,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+47.080242,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+47.395329,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+47.293316,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+47.369064,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+47.663155,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+47.658653,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+47.697613,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+46.962967,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+46.991123,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+47.014748,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+47.404465,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+47.396049,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+47.323307,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+47.706184,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+47.702488,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+47.732143,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+47.078209,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+46.993618,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+47.015213,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+47.414494,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+47.294346,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+47.361115,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+47.649189,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+47.716793,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+47.617199,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+47.014423,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+46.948345,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+46.970284,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+47.326393,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+47.398964,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+47.371506,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+47.661106,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+47.688477,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+47.669456,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+46.986534,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+46.953461,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+47.065754,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+47.290668,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+47.412212,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+47.367207,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+47.665733,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+47.661369,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+47.673164,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+48.050102,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+48.011143,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+48.014683,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+48.400028,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+48.372917,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+48.290142,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+48.715897,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+48.627678,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+48.672279,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+48.078735,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+48.060596,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+47.963387,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+48.340374,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+48.323067,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+48.389771,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+48.702854,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+48.615208,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+48.621506,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+47.989655,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+48.014549,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+47.991245,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+48.358719,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+48.354069,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+48.356682,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+48.708324,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+48.725025,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+48.664913,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+48.003784,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+48.053272,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+48.037605,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+48.347485,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+48.378826,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+48.365147,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+48.617176,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+48.666939,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+48.726612,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+48.051922,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+48.005733,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+48.074635,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+48.346214,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+48.362057,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+48.284664,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+48.625389,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+48.729252,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+48.651134,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+48.005795,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+47.984421,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+48.030315,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+48.413593,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+48.298088,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+48.326622,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+48.623695,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+48.705547,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+48.632420,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+47.987820,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+48.027340,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+47.984276,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+48.346321,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+48.286991,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+48.336842,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+48.686089,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+48.658340,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+48.673653,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+48.017925,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+47.965641,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+47.965679,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+48.381027,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+48.341831,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+48.392616,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+48.629711,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+48.737003,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+48.668114,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+47.970581,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+48.036369,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+48.047558,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+48.355679,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+48.294754,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+48.391891,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+48.743473,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+48.715824,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+48.721478,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+48.071022,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+48.039330,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+48.031570,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+48.399300,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+48.398819,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+48.338287,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+48.659740,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+48.661148,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+48.707333,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+48.020477,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+48.069176,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+47.979027,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+48.294529,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+48.326481,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+48.342972,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+48.677544,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+48.622753,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+48.710598,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+48.057224,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+48.047562,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+47.981720,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+48.353275,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+48.400635,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+48.355278,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+48.637016,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+48.707245,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+48.630764,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+49.031063,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+48.993408,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+49.057472,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+49.330196,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+49.339390,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+49.299610,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+49.691063,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+49.684662,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+49.641930,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+49.003063,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+48.978523,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+49.065449,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+49.378887,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+49.337055,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+49.323971,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+49.690472,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+49.674545,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+49.727634,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+49.030354,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+49.013077,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+48.961246,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+49.388634,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+49.365036,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+49.412109,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+49.643978,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+49.640553,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+49.717400,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+48.970650,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+48.985767,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+49.046352,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+49.413006,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+49.303638,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+49.364422,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+49.634853,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+49.683384,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+49.662067,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+48.986065,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+49.059956,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+49.080566,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+49.378841,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+49.326767,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+49.413921,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+49.632202,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+49.620270,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+49.688473,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+48.985760,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+49.038605,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+49.065079,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+49.317554,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+49.369659,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+49.341415,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+49.685616,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+49.688675,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+49.633957,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+48.962864,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+49.010525,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+49.019363,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+49.361115,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+49.304764,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+49.411716,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+49.635773,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+49.710270,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+49.652058,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+49.028366,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+49.079037,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+49.016205,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+49.319008,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+49.403965,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+49.325462,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+49.741329,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+49.731392,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+49.646263,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+48.963837,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+49.005943,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+48.953915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+49.326656,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+49.306664,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+49.359161,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+49.639591,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+49.654125,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+49.724018,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+48.992432,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+49.026291,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+49.005726,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+49.354977,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+49.343487,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+49.317410,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+49.651497,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+49.694725,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+49.705589,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+49.034737,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+48.949226,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+48.975178,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+49.334152,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+49.356110,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+49.336964,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+49.694489,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+49.636963,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+49.723537,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+49.042801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+49.046669,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+48.970669,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+49.299824,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+49.353611,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+49.281948,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+49.679543,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+49.734798,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+49.675472,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+50.060478,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+49.967819,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+50.077984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+50.304913,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+50.289242,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+50.322502,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+50.690891,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+50.732521,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+50.654510,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+49.975315,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+50.079086,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+49.996311,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+50.332535,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+50.282856,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+50.285728,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+50.660797,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+50.644360,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+50.714626,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+50.017094,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+50.035751,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+50.041809,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+50.356575,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+50.368473,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+50.302010,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+50.631710,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+50.657310,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+50.711315,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+49.958603,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+50.022797,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+50.000206,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+50.289967,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+50.333523,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+50.401894,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+50.626976,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+50.619350,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+50.646801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+49.955647,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+49.968170,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+49.981880,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+50.337902,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+50.305382,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+50.291805,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+50.617897,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+50.636017,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+50.661476,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+50.000462,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+50.057915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+50.078831,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+50.281799,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+50.373653,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+50.391315,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+50.736504,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+50.739105,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+50.741661,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+49.962311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+49.959438,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+50.034912,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+50.315495,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+50.332039,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+50.344227,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+50.657230,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+50.730591,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+50.681690,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+50.005901,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+49.949677,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+50.037292,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+50.336349,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+50.285690,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+50.375332,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+50.695667,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+50.618237,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+50.660683,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+50.064697,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+50.026844,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+50.024269,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+50.377052,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+50.290665,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+50.293690,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+50.652412,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+50.688858,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+50.677212,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+50.061092,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+50.069286,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+49.982513,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+50.285583,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+50.297321,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+50.312119,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+50.624512,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+50.636974,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+50.687237,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+50.070633,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+49.997940,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+50.057564,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+50.384930,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+50.402569,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+50.387630,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+50.622082,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+50.732288,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+50.723709,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+50.060509,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+50.073719,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+50.019730,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+50.384445,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+50.377956,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+50.307774,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+50.649307,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+50.733639,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+50.716095,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+51.049496,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+51.039963,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+50.964504,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+51.403477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+51.291496,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+51.335548,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+51.693779,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+51.726604,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+51.700317,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+51.021633,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+51.029453,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+51.034191,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+51.337048,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+51.342987,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+51.326252,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+51.633732,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+51.619797,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+51.670681,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+51.002319,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+51.018040,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+50.954773,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+51.322590,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+51.363087,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+51.407730,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+51.649979,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+51.633904,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+51.708225,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+50.974991,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+51.051888,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+51.064823,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+51.332264,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+51.335983,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+51.386608,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+51.703506,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+51.630753,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+51.678940,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+51.016411,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+50.951199,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+51.046642,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+51.408840,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+51.350464,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+51.408619,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+51.649658,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+51.721012,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+51.657871,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+51.044170,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+50.977631,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+51.055538,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+51.294685,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+51.376251,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+51.372547,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+51.729935,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+51.713333,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+51.658554,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+50.959560,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+51.022705,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+50.993202,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+51.411484,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+51.390102,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+51.364998,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+51.715984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+51.719143,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+51.700424,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+50.993416,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+50.993904,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+51.019699,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+51.327488,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+51.294079,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+51.311569,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+51.709297,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+51.667847,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+51.711720,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+50.983635,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+51.045441,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+50.977966,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+51.384972,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+51.371094,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+51.404655,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+51.720810,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+51.681976,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+51.726311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+51.020271,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+50.972672,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+51.015148,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+51.290657,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+51.351147,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+51.366680,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+51.676311,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+51.628231,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+51.684727,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+51.074741,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+50.966454,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+50.981194,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+51.369770,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+51.299973,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+51.382587,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+51.643372,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+51.634148,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+51.671989,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+50.949600,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+50.957485,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+50.967999,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+51.379242,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+51.282635,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+51.379921,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+51.649849,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+51.654797,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+51.624336,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+52.079323,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+51.950844,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+52.080509,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+52.371552,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+52.367878,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+52.326519,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+52.694645,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+52.725876,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+52.622490,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+52.036205,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+52.013870,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+52.048824,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+52.345844,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+52.332874,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+52.325611,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+52.713943,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+52.635380,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+52.675697,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+52.065834,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+51.972305,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+52.001556,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+52.348267,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+52.359383,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+52.338192,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+52.660858,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+52.674622,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+52.663773,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+51.989601,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+52.075031,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+51.979214,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+52.348061,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+52.366360,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+52.316650,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+52.623486,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+52.717102,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+52.621414,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+51.959042,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+52.030708,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+52.015739,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+52.393967,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+52.293335,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+52.354187,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+52.709545,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+52.663120,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+52.744728,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+51.974697,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+52.050583,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+52.043152,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+52.336685,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+52.303562,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+52.351414,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+52.637848,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+52.720005,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+52.627457,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+51.985691,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+52.079147,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+52.041290,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+52.354828,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+52.310196,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+52.284187,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+52.693043,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+52.682209,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+52.662804,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+52.021927,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+52.039291,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+52.065632,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+52.317772,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+52.412048,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+52.339676,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+52.710945,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+52.627354,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+52.733116,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+52.001064,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+52.070469,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+52.016270,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+52.402363,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+52.333576,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+52.380558,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+52.654896,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+52.738411,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+52.696854,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+52.034767,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+52.061520,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+51.988560,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+52.303799,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+52.355537,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+52.400536,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+52.624527,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+52.707222,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+52.666336,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+51.972412,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+52.041927,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+52.031036,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+52.394966,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+52.397915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+52.343792,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+52.700169,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+52.698498,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+52.724457,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+52.050755,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+52.010029,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+51.966724,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+52.302280,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+52.313240,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+52.345383,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+52.646267,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+52.645962,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+52.697411,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+52.974937,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+53.043629,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+52.965855,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+53.326477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+53.350048,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+53.349354,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+53.661816,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+53.663280,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+53.719952,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+52.954086,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+52.983097,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+52.980587,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+53.364033,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+53.405106,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+53.315540,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+53.689846,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+53.621532,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+53.678524,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+53.000237,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+53.073051,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+53.052711,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+53.351341,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+53.393261,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+53.405003,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+53.652756,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+53.642429,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+53.619698,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+53.072411,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+52.975311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+52.963249,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+53.321491,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+53.375568,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+53.355907,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+53.661064,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+53.746220,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+53.701012,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+52.969845,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+52.992268,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+53.022556,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+53.374584,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+53.321899,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+53.366005,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+53.695099,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+53.650028,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+53.641514,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+52.991875,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+52.982258,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+53.061333,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+53.398506,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+53.307484,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+53.297825,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+53.649536,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+53.632984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+53.617222,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+53.044975,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+53.026966,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+52.972820,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+53.364372,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+53.368206,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+53.344212,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+53.666061,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+53.635605,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+53.672600,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+53.015709,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+52.958858,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+52.961586,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+53.404964,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+53.316116,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+53.282825,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+53.730919,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+53.618610,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+53.712566,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+52.967243,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+53.040802,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+53.018150,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+53.373577,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+53.309563,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+53.354996,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+53.642220,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+53.680946,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+53.736835,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+52.968201,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+52.984829,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+53.043499,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+53.375648,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+53.393208,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+53.379566,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+53.639336,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+53.669518,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+53.727962,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+53.074608,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+53.044994,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+53.034924,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+53.350498,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+53.413315,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+53.337070,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+53.649395,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+53.635658,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+53.695473,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+52.958633,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+53.006367,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+53.018005,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+53.371399,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+53.290405,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+53.367779,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+53.622070,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+53.716560,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+53.719189,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+54.038952,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+54.039593,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+53.976364,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+54.334843,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+54.306435,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+54.355492,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+54.733456,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+54.618092,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+54.650566,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+53.952282,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+53.961491,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+54.072407,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+54.286301,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+54.374138,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+54.299904,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+54.721897,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+54.710995,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+54.693977,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+54.057858,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+54.042374,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+54.055191,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+54.318947,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+54.287796,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+54.318012,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+54.711128,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+54.714836,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+54.667530,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+54.021358,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+53.993458,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+53.970345,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+54.359764,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+54.311111,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+54.301208,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+54.656570,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+54.695152,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+54.662239,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+53.959885,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+53.962353,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+54.000729,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+54.360165,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+54.351856,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+54.346714,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+54.720570,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+54.702534,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+54.640839,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+53.959541,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+54.048775,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+54.030708,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+54.382114,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+54.359531,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+54.324104,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+54.730972,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+54.715233,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+54.668869,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+54.037128,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+53.962006,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+54.010334,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+54.355457,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+54.309284,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+54.319241,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+54.650723,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+54.713043,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+54.715473,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+54.063721,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+54.064476,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+54.057549,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+54.311707,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+54.377041,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+54.314209,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+54.660545,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+54.621117,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+54.668533,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+54.064384,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+54.023903,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+54.024448,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+54.293617,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+54.394024,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+54.385422,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+54.627995,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+54.738335,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+54.732468,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+54.056412,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+54.006298,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+53.968868,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+54.374210,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+54.413315,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+54.315636,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+54.719837,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+54.686737,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+54.673561,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+54.081238,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+54.044353,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+53.998901,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+54.406887,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+54.400406,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+54.403889,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+54.661552,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+54.615593,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+54.694710,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+53.993969,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+54.028442,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+53.954800,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+54.304005,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+54.380539,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+54.342896,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+54.711243,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+54.696995,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+54.739784,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+55.014610,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+54.976429,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+55.001812,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+55.287682,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+55.298569,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+55.331123,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+55.734478,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+55.635044,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+55.702164,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+54.958633,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+55.079628,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+54.952076,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+55.344975,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+55.370827,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+55.335796,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+55.721226,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+55.656448,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+55.725929,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+54.995968,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+55.031830,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+55.056572,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+55.382000,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+55.408619,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+55.353642,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+55.620556,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+55.709454,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+55.726921,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+54.977943,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+55.020470,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+55.060318,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+55.328697,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+55.294788,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+55.372704,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+55.701202,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+55.720451,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+55.617371,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+54.988758,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+55.032421,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+55.035915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+55.294209,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+55.320984,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+55.292946,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+55.732540,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+55.707508,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+55.715672,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+54.987740,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+54.969563,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+54.990150,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+55.358376,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+55.400013,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+55.323463,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+55.638000,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+55.640842,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+55.727386,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+55.073372,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+54.994965,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+55.039459,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+55.407982,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+55.389065,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+55.285698,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+55.641186,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+55.677498,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+55.700291,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+55.071495,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+55.056427,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+54.993801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+55.361568,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+55.346714,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+55.399296,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+55.640900,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+55.704880,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+55.640488,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+54.957081,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+55.029209,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+55.015465,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+55.322891,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+55.355000,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+55.300842,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+55.626892,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+55.731106,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+55.625095,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+54.970901,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+55.029346,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+54.976967,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+55.360340,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+55.320843,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+55.349033,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+55.618374,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+55.705467,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+55.669380,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+54.987446,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+55.037052,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+54.992966,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+55.310734,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+55.414551,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+55.377903,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+55.636654,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+55.624851,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+55.735313,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+54.980915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+54.986378,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+54.957214,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+55.340805,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+55.413498,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+55.289421,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+55.649918,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+55.723186,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+55.716740,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+56.037922,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+56.020084,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+55.957512,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+56.296680,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+56.320217,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+56.306499,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+56.651733,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+56.634670,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+56.740162,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+56.054447,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+55.968010,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+55.970158,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+56.343292,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+56.363796,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+56.401867,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+56.746094,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+56.742115,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+56.692490,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+55.983479,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+55.991444,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+55.995361,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+56.383423,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+56.303028,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+56.377865,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+56.704235,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+56.696442,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+56.620129,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+56.032532,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+56.059372,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+55.986298,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+56.369831,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+56.384441,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+56.293480,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+56.646263,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+56.638676,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+56.614834,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+56.041054,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+56.056549,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+56.065495,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+56.370171,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+56.306896,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+56.400726,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+56.614902,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+56.747295,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+56.725918,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+56.068924,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+56.060345,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+56.052071,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+56.411358,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+56.408333,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+56.378914,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+56.695190,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+56.730515,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+56.659054,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+55.954525,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+55.958214,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+55.998852,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+56.362659,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+56.308914,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+56.331627,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+56.680489,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+56.676567,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+56.619801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+56.066460,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+56.060059,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+56.051487,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+56.408867,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+56.402992,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+56.327267,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+56.685947,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+56.711430,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+56.723934,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+55.966003,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+56.035019,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+56.017624,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+56.362862,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+56.301201,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+56.372898,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+56.685741,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+56.727535,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+56.715122,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+55.995808,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+55.988811,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+55.956581,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+56.355434,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+56.404232,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+56.413395,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+56.638199,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+56.643253,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+56.666683,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+56.066059,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+56.011280,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+56.036015,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+56.404530,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+56.402985,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+56.377674,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+56.622570,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+56.747395,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+56.633308,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+56.051285,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+56.001354,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+56.029633,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+56.372936,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+56.411743,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+56.339710,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+56.732105,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+56.735638,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+56.739529,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+57.074059,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+57.040905,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+57.035679,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+57.341248,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+57.376530,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+57.367363,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+57.698498,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+57.674461,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+57.715549,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+57.058979,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+57.005245,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+57.012180,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+57.383320,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+57.397522,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+57.304642,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+57.702587,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+57.672310,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+57.643280,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+56.991219,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+56.955223,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+56.962929,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+57.355881,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+57.325539,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+57.303616,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+57.677341,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+57.675419,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+57.723053,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+57.008705,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+56.978378,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+57.020634,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+57.376450,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+57.316505,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+57.304546,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+57.662224,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+57.713337,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+57.646652,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+57.007587,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+56.951908,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+57.006565,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+57.322819,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+57.340092,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+57.363903,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+57.714993,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+57.712311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+57.716778,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+57.027878,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+57.062496,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+57.077232,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+57.402515,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+57.295509,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+57.302837,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+57.623299,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+57.727093,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+57.668732,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+57.061928,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+56.966797,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+57.010918,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+57.281387,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+57.298534,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+57.360180,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+57.729042,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+57.720295,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+57.731274,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+56.997509,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+57.042252,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+57.017857,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+57.389706,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+57.363487,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+57.318481,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+57.640537,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+57.680080,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+57.667603,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+57.028282,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+56.966991,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+57.008068,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+57.385674,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+57.394882,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+57.303028,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+57.704205,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+57.688061,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+57.677349,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+57.058113,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+57.044521,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+57.003510,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+57.400269,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+57.340599,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+57.354622,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+57.698330,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+57.725250,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+57.683426,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+56.960739,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+57.041851,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+57.053440,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+57.302917,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+57.326939,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+57.360195,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+57.671211,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+57.631702,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+57.688286,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+57.068165,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+57.037861,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+57.010612,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+57.291882,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+57.373184,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+57.297134,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+57.713882,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+57.703579,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+57.732433,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-5.txt b/scenes/cell-growth/results/particles-frame-5.txt new file mode 100644 index 00000000..9667afd2 --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-5.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+44.362946,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+44.464191,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+44.436523,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+44.692818,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+44.781094,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+44.692574,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+45.040154,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+45.128468,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+45.093468,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+44.447773,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+44.416588,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+44.466774,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+44.681103,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+44.713627,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+44.717361,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+45.132423,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+45.032230,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+45.097473,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+44.446804,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+44.409519,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+44.446987,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+44.690971,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+44.740601,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+44.682930,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+45.035618,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+45.098965,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+45.074448,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+44.437286,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+44.442265,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+44.390385,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+44.733105,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+44.789871,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+44.726688,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+45.064117,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+45.056717,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+45.027061,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+44.412231,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+44.434017,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+44.363441,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+44.731396,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+44.774769,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+44.723270,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+45.139996,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+45.071278,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+45.058510,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+44.357395,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+44.352108,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+44.420456,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+44.675594,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+44.733063,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+44.686279,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+45.066479,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+45.016903,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+45.032455,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+44.473034,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+44.393208,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+44.379021,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+44.781414,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+44.775341,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+44.747299,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+45.115803,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+45.051891,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+45.091541,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+44.347637,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+44.433914,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+44.464500,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+44.703789,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+44.705147,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+44.683617,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+45.130520,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+45.026203,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+45.093773,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+44.396305,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+44.374683,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+44.378506,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+44.684494,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+44.674053,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+44.697834,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+45.088036,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+45.063114,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+45.077923,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+44.365910,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+44.380657,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+44.362892,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+44.801376,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+44.676270,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+44.782310,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+45.087364,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+45.041313,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+45.057129,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+44.411369,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+44.454227,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+44.465923,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+44.770821,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+44.687946,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+44.717556,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+45.120823,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+45.106277,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+45.018494,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+44.426804,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+44.441875,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+44.411842,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+44.773689,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+44.736179,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+44.714493,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+45.085938,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+45.122910,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+45.013363,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+45.370926,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+45.438881,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+45.384338,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+45.732693,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+45.776112,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+45.703453,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+46.030479,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+46.087280,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+46.007595,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+45.420181,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+45.381027,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+45.343716,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+45.733227,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+45.739552,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+45.785896,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+46.056641,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+46.095516,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+46.065243,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+45.347992,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+45.383991,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+45.433727,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+45.791412,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+45.706867,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+45.679707,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+46.104904,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+46.034431,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+46.023441,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+45.378254,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+45.357510,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+45.357513,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+45.696320,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+45.780884,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+45.688000,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+46.065693,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+46.099487,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+46.080479,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+45.415447,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+45.352047,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+45.471554,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+45.727581,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+45.720814,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+45.704243,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+46.039391,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+46.132893,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+46.100437,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+45.362343,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+45.356995,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+45.378937,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+45.734604,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+45.802841,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+45.781826,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+46.121769,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+46.062149,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+46.013203,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+45.367672,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+45.387615,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+45.363857,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+45.697781,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+45.678082,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+45.792202,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+46.099419,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+46.107281,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+46.091831,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+45.391441,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+45.355255,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+45.350174,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+45.793575,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+45.797207,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+45.698063,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+46.101833,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+46.042294,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+46.102776,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+45.378986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+45.446239,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+45.398655,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+45.694508,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+45.681412,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+45.805649,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+46.016232,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+46.009708,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+46.061867,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+45.367432,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+45.355667,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+45.466072,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+45.780216,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+45.742474,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+45.776962,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+46.040455,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+46.057510,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+46.075382,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+45.453564,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+45.391266,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+45.347275,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+45.738655,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+45.718735,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+45.734474,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+46.058956,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+46.117615,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+46.031483,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+45.372787,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+45.443645,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+45.403214,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+45.766205,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+45.770908,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+45.708210,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+46.072624,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+46.061848,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+46.034729,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+46.402821,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+46.362747,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+46.424877,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+46.764179,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+46.743080,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+46.798645,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+47.125145,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+47.010777,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+47.032242,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+46.392677,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+46.463043,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+46.466702,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+46.740192,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+46.683212,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+46.718243,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+47.086166,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+47.116047,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+47.035282,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+46.376926,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+46.360325,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+46.382946,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+46.792450,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+46.699566,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+46.674583,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+47.012371,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+47.018795,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+47.068932,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+46.364151,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+46.443359,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+46.443485,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+46.696121,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+46.803364,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+46.754711,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+47.118980,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+47.027393,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+47.083462,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+46.370750,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+46.457806,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+46.428921,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+46.785728,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+46.765553,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+46.694157,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+47.070446,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+47.082344,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+47.100346,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+46.370045,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+46.409103,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+46.349262,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+46.733719,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+46.688053,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+46.729206,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+47.061279,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+47.076340,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+47.024986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+46.423820,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+46.364479,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+46.367294,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+46.712090,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+46.727776,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+46.794247,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+47.051422,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+47.138451,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+47.118317,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+46.455517,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+46.340549,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+46.472244,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+46.787331,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+46.685318,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+46.761066,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+47.055157,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+47.050655,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+47.089615,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+46.354969,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+46.383125,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+46.406750,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+46.796467,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+46.788052,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+46.715309,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+47.098186,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+47.094490,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+47.124146,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+46.470211,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+46.385620,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+46.407215,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+46.806496,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+46.686348,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+46.753117,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+47.041191,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+47.108795,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+47.009201,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+46.406425,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+46.340347,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+46.362286,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+46.718395,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+46.790966,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+46.763508,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+47.053108,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+47.080479,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+47.061459,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+46.378536,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+46.345463,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+46.457756,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+46.682671,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+46.804214,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+46.759209,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+47.057735,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+47.053371,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+47.065166,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+47.442104,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+47.403145,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+47.406685,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+47.792030,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+47.764919,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+47.682144,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+48.107899,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+48.019680,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+48.064281,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+47.470737,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+47.452599,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+47.355389,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+47.732376,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+47.715069,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+47.781773,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+48.094856,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+48.007210,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+48.013508,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+47.381657,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+47.406551,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+47.383247,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+47.750721,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+47.746071,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+47.748684,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+48.100327,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+48.117027,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+48.056915,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+47.395786,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+47.445274,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+47.429607,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+47.739487,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+47.770828,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+47.757149,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+48.009178,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+48.058937,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+48.118614,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+47.443924,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+47.397736,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+47.466637,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+47.738216,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+47.754059,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+47.676666,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+48.017391,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+48.121254,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+48.043137,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+47.397797,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+47.376423,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+47.422318,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+47.805595,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+47.690090,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+47.718624,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+48.015697,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+48.097549,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+48.024422,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+47.379822,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+47.419342,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+47.376278,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+47.738323,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+47.678993,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+47.728844,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+48.078091,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+48.050343,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+48.065655,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+47.409927,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+47.357643,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+47.357681,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+47.773029,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+47.733833,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+47.784618,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+48.021713,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+48.129005,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+48.060116,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+47.362583,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+47.428371,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+47.439560,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+47.747681,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+47.686756,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+47.783894,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+48.135475,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+48.107826,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+48.113480,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+47.463024,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+47.431332,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+47.423573,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+47.791302,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+47.790821,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+47.730289,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+48.051743,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+48.053150,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+48.099335,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+47.412479,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+47.461178,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+47.371029,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+47.686531,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+47.718483,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+47.734974,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+48.069546,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+48.014755,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+48.102600,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+47.449226,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+47.439564,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+47.373722,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+47.745277,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+47.792637,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+47.747280,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+48.029018,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+48.099247,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+48.022766,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+48.423065,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+48.385410,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+48.449474,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+48.722198,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+48.731392,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+48.691612,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+49.083065,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+49.076664,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+49.033932,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+48.395065,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+48.370525,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+48.457451,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+48.770889,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+48.729057,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+48.715973,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+49.082474,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+49.066547,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+49.119637,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+48.422356,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+48.405079,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+48.353249,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+48.780636,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+48.757038,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+48.804111,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+49.035980,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+49.032555,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+49.109402,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+48.362652,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+48.377769,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+48.438354,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+48.805008,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+48.695641,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+48.756424,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+49.026855,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+49.075386,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+49.054070,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+48.378067,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+48.451958,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+48.472569,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+48.770844,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+48.718769,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+48.805923,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+49.024204,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+49.012272,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+49.080475,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+48.377762,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+48.430607,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+48.457081,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+48.709557,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+48.761662,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+48.733418,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+49.077618,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+49.080677,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+49.025959,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+48.354866,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+48.402527,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+48.411366,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+48.753117,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+48.696766,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+48.803719,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+49.027775,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+49.102272,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+49.044060,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+48.420368,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+48.471039,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+48.408207,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+48.711010,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+48.795967,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+48.717464,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+49.133331,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+49.123394,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+49.038265,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+48.355839,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+48.397945,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+48.345917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+48.718658,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+48.698666,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+48.751163,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+49.031593,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+49.046127,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+49.116020,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+48.384434,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+48.418293,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+48.397728,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+48.746979,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+48.735489,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+48.709412,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+49.043499,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+49.086727,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+49.097591,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+48.426739,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+48.341228,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+48.367180,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+48.726154,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+48.748112,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+48.728966,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+49.086491,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+49.028965,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+49.115540,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+48.434803,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+48.438671,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+48.362671,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+48.691826,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+48.745613,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+48.673950,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+49.071545,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+49.126801,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+49.067474,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+49.452480,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+49.359821,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+49.469986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+49.696915,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+49.681244,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+49.714504,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+50.082893,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+50.124523,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+50.046513,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+49.367317,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+49.471088,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+49.388313,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+49.724537,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+49.674858,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+49.677731,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+50.052799,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+50.036362,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+50.106628,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+49.409096,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+49.427753,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+49.433811,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+49.748577,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+49.760475,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+49.694012,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+50.023712,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+50.049313,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+50.103317,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+49.350605,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+49.414799,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+49.392208,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+49.681969,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+49.725525,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+49.793896,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+50.018978,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+50.011353,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+50.038803,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+49.347649,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+49.360172,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+49.373882,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+49.729904,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+49.697384,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+49.683807,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+50.009899,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+50.028019,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+50.053478,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+49.392464,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+49.449917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+49.470833,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+49.673801,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+49.765656,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+49.783318,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+50.128506,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+50.131107,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+50.133663,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+49.354313,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+49.351440,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+49.426914,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+49.707497,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+49.724041,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+49.736229,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+50.049232,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+50.122593,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+50.073692,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+49.397903,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+49.341679,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+49.429295,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+49.728352,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+49.677692,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+49.767334,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+50.087669,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+50.010239,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+50.052685,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+49.456699,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+49.418846,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+49.416271,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+49.769054,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+49.682667,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+49.685692,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+50.044415,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+50.080860,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+50.069214,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+49.453094,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+49.461288,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+49.374516,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+49.677586,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+49.689323,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+49.704121,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+50.016514,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+50.028976,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+50.079239,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+49.462635,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+49.389942,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+49.449566,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+49.776932,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+49.794571,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+49.779633,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+50.014084,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+50.124290,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+50.115711,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+49.452511,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+49.465721,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+49.411732,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+49.776447,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+49.769958,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+49.699776,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+50.041309,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+50.125641,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+50.108097,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+50.441498,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+50.431965,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+50.356506,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+50.795479,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+50.683498,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+50.727551,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+51.085781,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+51.118607,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+51.092319,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+50.413635,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+50.421455,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+50.426193,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+50.729050,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+50.734989,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+50.718254,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+51.025734,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+51.011799,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+51.062683,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+50.394321,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+50.410042,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+50.346775,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+50.714592,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+50.755089,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+50.799732,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+51.041981,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+51.025906,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+51.100227,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+50.366993,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+50.443890,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+50.456825,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+50.724266,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+50.727985,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+50.778610,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+51.095509,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+51.022755,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+51.070942,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+50.408413,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+50.343201,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+50.438644,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+50.800842,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+50.742466,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+50.800621,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+51.041660,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+51.113014,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+51.049873,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+50.436172,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+50.369633,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+50.447540,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+50.686687,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+50.768253,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+50.764549,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+51.121937,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+51.105335,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+51.050556,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+50.351562,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+50.414707,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+50.385204,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+50.803486,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+50.782104,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+50.757000,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+51.107986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+51.111145,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+51.092426,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+50.385418,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+50.385906,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+50.411701,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+50.719490,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+50.686081,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+50.703571,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+51.101299,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+51.059849,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+51.103722,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+50.375637,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+50.437443,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+50.369968,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+50.776974,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+50.763096,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+50.796658,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+51.112812,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+51.073978,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+51.118313,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+50.412273,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+50.364674,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+50.407150,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+50.682659,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+50.743149,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+50.758682,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+51.068314,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+51.020233,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+51.076729,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+50.466743,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+50.358456,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+50.373196,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+50.761772,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+50.691975,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+50.774590,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+51.035374,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+51.026150,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+51.063992,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+50.341602,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+50.349487,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+50.360001,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+50.771244,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+50.674637,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+50.771923,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+51.041851,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+51.046799,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+51.016338,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+51.471325,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+51.342846,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+51.472511,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+51.763554,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+51.759880,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+51.718521,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+52.086647,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+52.117878,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+52.014492,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+51.428207,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+51.405872,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+51.440826,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+51.737846,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+51.724876,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+51.717613,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+52.105946,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+52.027382,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+52.067699,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+51.457836,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+51.364307,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+51.393559,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+51.740269,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+51.751385,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+51.730194,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+52.052860,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+52.066624,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+52.055775,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+51.381603,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+51.467033,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+51.371216,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+51.740063,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+51.758362,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+51.708652,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+52.015488,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+52.109104,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+52.013416,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+51.351044,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+51.422710,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+51.407742,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+51.785969,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+51.685337,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+51.746189,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+52.101547,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+52.055122,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+52.136730,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+51.366699,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+51.442585,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+51.435154,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+51.728687,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+51.695564,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+51.743416,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+52.029850,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+52.112007,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+52.019459,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+51.377693,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+51.471149,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+51.433292,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+51.746830,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+51.702198,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+51.676189,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+52.085045,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+52.074211,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+52.054806,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+51.413929,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+51.431293,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+51.457634,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+51.709774,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+51.804050,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+51.731678,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+52.102947,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+52.019356,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+52.125118,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+51.393066,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+51.462471,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+51.408272,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+51.794365,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+51.725578,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+51.772560,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+52.046898,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+52.130413,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+52.088856,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+51.426769,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+51.453522,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+51.380562,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+51.695801,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+51.747540,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+51.792538,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+52.016529,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+52.099224,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+52.058338,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+51.364414,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+51.433929,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+51.423038,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+51.786968,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+51.789917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+51.735794,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+52.092171,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+52.090500,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+52.116459,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+51.442757,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+51.402031,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+51.358727,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+51.694283,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+51.705242,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+51.737385,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+52.038269,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+52.037964,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+52.089413,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+52.366940,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+52.435631,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+52.357857,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+52.718479,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+52.742050,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+52.741356,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+53.053818,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+53.055283,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+53.111954,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+52.346088,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+52.375099,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+52.372589,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+52.756035,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+52.797108,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+52.707542,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+53.081848,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+53.013535,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+53.070526,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+52.392239,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+52.465054,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+52.444714,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+52.743343,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+52.785263,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+52.797005,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+53.044758,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+53.034431,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+53.011700,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+52.464413,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+52.367313,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+52.355251,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+52.713493,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+52.767570,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+52.747910,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+53.053066,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+53.138222,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+53.093014,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+52.361847,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+52.384270,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+52.414558,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+52.766586,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+52.713902,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+52.758007,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+53.087101,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+53.042030,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+53.033516,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+52.383877,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+52.374260,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+52.453335,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+52.790508,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+52.699486,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+52.689827,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+53.041538,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+53.024986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+53.009224,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+52.436977,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+52.418968,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+52.364822,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+52.756374,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+52.760208,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+52.736214,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+53.058064,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+53.027607,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+53.064602,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+52.407711,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+52.350861,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+52.353588,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+52.796967,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+52.708118,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+52.674828,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+53.122921,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+53.010612,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+53.104568,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+52.359245,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+52.432804,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+52.410152,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+52.765579,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+52.701565,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+52.746998,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+53.034222,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+53.072948,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+53.128838,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+52.360203,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+52.376831,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+52.435501,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+52.767651,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+52.785210,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+52.771568,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+53.031338,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+53.061520,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+53.119965,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+52.466610,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+52.436996,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+52.426926,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+52.742500,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+52.805317,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+52.729073,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+53.041397,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+53.027660,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+53.087475,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+52.350636,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+52.398369,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+52.410007,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+52.763401,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+52.682407,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+52.759781,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+53.014072,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+53.108562,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+53.111191,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+53.430954,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+53.431595,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+53.368366,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+53.726845,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+53.698437,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+53.747494,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+54.125458,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+54.010094,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+54.042568,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+53.344284,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+53.353493,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+53.464409,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+53.678303,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+53.766140,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+53.691906,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+54.113899,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+54.102997,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+54.085979,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+53.449860,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+53.434376,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+53.447193,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+53.710949,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+53.679798,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+53.710014,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+54.103130,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+54.106838,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+54.059532,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+53.413361,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+53.385460,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+53.362347,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+53.751766,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+53.703114,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+53.693211,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+54.048573,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+54.087154,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+54.054241,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+53.351887,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+53.354355,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+53.392731,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+53.752167,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+53.743858,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+53.738716,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+54.112572,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+54.094536,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+54.032841,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+53.351543,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+53.440777,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+53.422710,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+53.774117,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+53.751534,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+53.716106,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+54.122974,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+54.107235,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+54.060871,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+53.429131,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+53.354008,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+53.402336,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+53.747459,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+53.701286,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+53.711243,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+54.042725,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+54.105045,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+54.107475,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+53.455723,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+53.456478,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+53.449551,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+53.703709,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+53.769043,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+53.706211,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+54.052547,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+54.013119,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+54.060535,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+53.456387,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+53.415905,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+53.416451,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+53.685619,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+53.786026,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+53.777424,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+54.019997,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+54.130337,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+54.124470,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+53.448414,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+53.398300,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+53.360870,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+53.766212,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+53.805317,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+53.707638,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+54.111839,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+54.078739,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+54.065563,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+53.473240,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+53.436356,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+53.390903,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+53.798889,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+53.792408,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+53.795891,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+54.053555,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+54.007595,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+54.086712,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+53.385971,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+53.420444,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+53.346802,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+53.696007,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+53.772541,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+53.734898,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+54.103245,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+54.088997,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+54.131786,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+54.406612,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+54.368431,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+54.393814,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+54.679684,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+54.690571,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+54.723125,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+55.126480,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+55.027046,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+55.094166,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+54.350636,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+54.471630,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+54.344078,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+54.736977,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+54.762829,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+54.727798,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+55.113228,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+55.048450,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+55.117931,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+54.387970,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+54.423832,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+54.448574,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+54.774002,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+54.800621,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+54.745644,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+55.012558,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+55.101456,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+55.118923,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+54.369946,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+54.412472,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+54.452320,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+54.720699,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+54.686790,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+54.764702,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+55.093204,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+55.112453,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+55.009373,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+54.380760,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+54.424423,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+54.427917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+54.686211,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+54.712982,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+54.684948,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+55.124542,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+55.099510,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+55.107674,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+54.379742,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+54.361565,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+54.382153,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+54.750378,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+54.792015,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+54.715466,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+55.030003,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+55.032845,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+55.119389,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+54.465374,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+54.386967,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+54.431461,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+54.799984,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+54.781067,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+54.677700,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+55.033188,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+55.069500,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+55.092293,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+54.463497,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+54.448429,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+54.385803,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+54.753571,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+54.738716,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+54.791298,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+55.032902,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+55.096882,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+55.032490,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+54.349083,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+54.421211,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+54.407467,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+54.714893,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+54.747002,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+54.692844,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+55.018894,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+55.123108,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+55.017097,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+54.362904,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+54.421349,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+54.368969,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+54.752342,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+54.712845,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+54.741035,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+55.010376,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+55.097469,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+55.061382,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+54.379448,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+54.429054,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+54.384968,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+54.702736,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+54.806553,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+54.769905,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+55.028656,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+55.016853,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+55.127316,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+54.372917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+54.378380,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+54.349216,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+54.732807,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+54.805500,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+54.681423,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+55.041920,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+55.115189,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+55.108742,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+55.429924,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+55.412086,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+55.349514,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+55.688683,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+55.712219,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+55.698502,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+56.043736,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+56.026672,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+56.132164,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+55.446449,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+55.360012,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+55.362160,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+55.735294,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+55.755798,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+55.793869,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+56.138096,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+56.134117,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+56.084492,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+55.375481,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+55.383446,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+55.387363,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+55.775425,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+55.695030,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+55.769867,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+56.096237,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+56.088444,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+56.012131,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+55.424534,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+55.451374,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+55.378300,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+55.761833,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+55.776443,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+55.685482,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+56.038265,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+56.030678,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+56.006836,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+55.433056,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+55.448547,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+55.457497,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+55.762173,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+55.698898,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+55.792728,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+56.006905,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+56.139297,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+56.117920,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+55.460922,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+55.452347,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+55.444069,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+55.803360,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+55.800331,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+55.770916,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+56.087193,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+56.122513,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+56.051056,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+55.346527,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+55.350216,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+55.390854,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+55.754662,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+55.700916,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+55.723629,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+56.072491,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+56.068569,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+56.011803,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+55.458462,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+55.452061,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+55.443489,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+55.800869,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+55.794994,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+55.719269,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+56.077950,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+56.103432,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+56.115936,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+55.358006,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+55.427021,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+55.409626,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+55.754864,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+55.693203,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+55.764900,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+56.077744,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+56.119537,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+56.107124,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+55.387810,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+55.380814,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+55.348583,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+55.747437,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+55.796234,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+55.805397,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+56.030201,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+56.035255,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+56.058685,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+55.458061,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+55.403282,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+55.428017,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+55.796532,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+55.794987,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+55.769676,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+56.014572,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+56.139397,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+56.025311,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+55.443287,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+55.393356,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+55.421635,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+55.764938,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+55.803745,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+55.731712,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+56.124107,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+56.127640,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+56.131531,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+56.466061,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+56.432907,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+56.427681,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+56.733250,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+56.768532,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+56.759365,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+57.090500,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+57.066463,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+57.107552,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+56.450981,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+56.397247,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+56.404182,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+56.775322,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+56.789524,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+56.696644,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+57.094589,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+57.064312,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+57.035282,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+56.383221,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+56.347225,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+56.354931,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+56.747883,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+56.717541,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+56.695618,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+57.069344,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+57.067421,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+57.115055,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+56.400707,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+56.370380,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+56.412636,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+56.768452,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+56.708508,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+56.696548,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+57.054226,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+57.105339,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+57.038654,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+56.399590,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+56.343910,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+56.398567,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+56.714821,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+56.732094,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+56.755905,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+57.106995,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+57.104313,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+57.108780,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+56.419880,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+56.454498,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+56.469231,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+56.794518,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+56.687508,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+56.694839,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+57.015301,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+57.119095,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+57.060730,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+56.453930,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+56.358799,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+56.402920,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+56.673389,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+56.690536,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+56.752182,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+57.121044,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+57.112297,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+57.123276,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+56.389511,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+56.434254,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+56.409859,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+56.781708,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+56.755489,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+56.710484,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+57.032539,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+57.072083,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+57.059605,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+56.420284,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+56.358994,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+56.400070,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+56.777676,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+56.786884,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+56.695030,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+57.096207,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+57.080063,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+57.069351,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+56.450115,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+56.436523,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+56.395512,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+56.792271,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+56.732601,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+56.746624,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+57.090332,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+57.117252,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+57.075428,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+56.352741,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+56.433853,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+56.445442,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+56.694920,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+56.718941,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+56.752197,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+57.063213,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+57.023705,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+57.080288,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+56.460167,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+56.429863,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+56.402615,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+56.683884,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+56.765186,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+56.689137,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+57.105885,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+57.095581,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+57.124435,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-6.txt b/scenes/cell-growth/results/particles-frame-6.txt new file mode 100644 index 00000000..eb50bff2 --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-6.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+43.626945,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+43.728191,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+43.700523,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+43.956818,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+44.045094,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+43.956573,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+44.304153,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+44.392467,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+44.357468,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+43.711773,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+43.680588,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+43.730774,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+43.945103,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+43.977627,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+43.981361,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+44.396423,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+44.296230,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+44.361473,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+43.710804,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+43.673519,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+43.710987,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+43.954971,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+44.004601,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+43.946930,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+44.299618,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+44.362965,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+44.338448,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+43.701286,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+43.706264,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+43.654385,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+43.997105,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+44.053871,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+43.990688,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+44.328117,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+44.320717,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+44.291061,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+43.676231,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+43.698017,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+43.627441,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+43.995396,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+44.038769,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+43.987270,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+44.403996,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+44.335278,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+44.322510,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+43.621395,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+43.616108,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+43.684456,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+43.939594,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+43.997063,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+43.950279,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+44.330479,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+44.280903,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+44.296455,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+43.737034,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+43.657207,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+43.643021,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+44.045414,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+44.039341,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+44.011299,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+44.379803,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+44.315891,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+44.355541,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+43.611637,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+43.697914,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+43.728500,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+43.967789,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+43.969147,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+43.947617,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+44.394520,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+44.290203,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+44.357773,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+43.660305,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+43.638683,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+43.642506,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+43.948494,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+43.938053,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+43.961834,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+44.352036,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+44.327114,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+44.341923,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+43.629910,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+43.644657,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+43.626892,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+44.065376,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+43.940269,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+44.046310,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+44.351364,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+44.305313,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+44.321129,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+43.675369,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+43.718227,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+43.729923,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+44.034821,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+43.951946,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+43.981556,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+44.384823,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+44.370277,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+44.282494,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+43.690804,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+43.705875,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+43.675842,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+44.037689,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+44.000179,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+43.978493,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+44.349937,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+44.386909,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+44.277363,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+44.634926,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+44.702881,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+44.648338,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+44.996693,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+45.040112,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+44.967453,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+45.294479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+45.351280,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+45.271595,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+44.684181,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+44.645027,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+44.607716,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+44.997227,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+45.003551,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+45.049896,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+45.320641,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+45.359516,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+45.329243,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+44.611992,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+44.647991,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+44.697727,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+45.055412,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+44.970867,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+44.943707,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+45.368904,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+45.298431,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+45.287441,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+44.642254,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+44.621510,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+44.621513,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+44.960320,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+45.044884,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+44.952000,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+45.329693,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+45.363487,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+45.344479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+44.679447,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+44.616047,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+44.735554,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+44.991581,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+44.984814,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+44.968243,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+45.303391,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+45.396893,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+45.364437,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+44.626343,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+44.620995,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+44.642937,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+44.998604,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+45.066841,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+45.045826,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+45.385769,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+45.326149,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+45.277203,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+44.631672,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+44.651615,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+44.627857,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+44.961781,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+44.942081,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+45.056202,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+45.363419,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+45.371281,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+45.355831,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+44.655441,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+44.619255,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+44.614174,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+45.057575,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+45.061207,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+44.962063,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+45.365833,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+45.306293,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+45.366776,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+44.642986,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+44.710239,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+44.662655,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+44.958508,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+44.945412,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+45.069649,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+45.280231,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+45.273708,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+45.325867,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+44.631432,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+44.619667,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+44.730072,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+45.044216,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+45.006474,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+45.040962,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+45.304455,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+45.321510,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+45.339382,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+44.717564,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+44.655266,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+44.611275,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+45.002655,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+44.982735,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+44.998474,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+45.322956,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+45.381615,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+45.295483,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+44.636787,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+44.707645,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+44.667213,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+45.030205,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+45.034908,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+44.972210,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+45.336624,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+45.325848,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+45.298729,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+45.666821,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+45.626747,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+45.688877,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+46.028179,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+46.007080,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+46.062645,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+46.389145,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+46.274776,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+46.296242,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+45.656677,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+45.727043,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+45.730701,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+46.004192,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+45.947212,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+45.982243,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+46.350166,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+46.380047,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+46.299282,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+45.640926,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+45.624325,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+45.646946,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+46.056450,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+45.963566,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+45.938583,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+46.276371,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+46.282795,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+46.332932,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+45.628151,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+45.707359,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+45.707485,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+45.960121,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+46.067364,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+46.018711,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+46.382980,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+46.291393,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+46.347462,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+45.634750,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+45.721806,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+45.692921,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+46.049728,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+46.029552,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+45.958157,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+46.334446,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+46.346344,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+46.364346,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+45.634045,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+45.673103,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+45.613262,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+45.997719,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+45.952053,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+45.993206,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+46.325279,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+46.340340,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+46.288986,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+45.687820,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+45.628479,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+45.631294,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+45.976089,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+45.991776,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+46.058247,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+46.315422,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+46.402451,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+46.382317,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+45.719517,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+45.604549,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+45.736244,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+46.051331,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+45.949318,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+46.025066,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+46.319157,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+46.314655,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+46.353615,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+45.618969,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+45.647125,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+45.670750,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+46.060467,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+46.052052,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+45.979309,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+46.362186,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+46.358490,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+46.388145,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+45.734211,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+45.649620,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+45.671215,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+46.070496,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+45.950348,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+46.017117,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+46.305191,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+46.372795,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+46.273201,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+45.670425,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+45.604347,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+45.626286,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+45.982395,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+46.054966,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+46.027508,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+46.317108,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+46.344479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+46.325459,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+45.642536,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+45.609463,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+45.721756,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+45.946671,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+46.068214,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+46.023209,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+46.321735,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+46.317371,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+46.329166,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+46.706104,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+46.667145,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+46.670685,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+47.056030,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+47.028919,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+46.946144,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+47.371899,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+47.283680,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+47.328281,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+46.734737,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+46.716599,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+46.619389,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+46.996376,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+46.979069,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+47.045773,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+47.358856,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+47.271210,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+47.277508,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+46.645657,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+46.670551,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+46.647247,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+47.014721,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+47.010071,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+47.012684,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+47.364326,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+47.381027,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+47.320915,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+46.659786,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+46.709274,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+46.693607,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+47.003487,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+47.034828,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+47.021149,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+47.273178,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+47.322937,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+47.382614,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+46.707924,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+46.661736,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+46.730637,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+47.002216,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+47.018059,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+46.940666,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+47.281391,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+47.385254,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+47.307137,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+46.661797,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+46.640423,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+46.686317,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+47.069595,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+46.954090,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+46.982624,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+47.279697,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+47.361549,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+47.288422,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+46.643822,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+46.683342,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+46.640278,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+47.002323,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+46.942993,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+46.992844,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+47.342091,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+47.314342,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+47.329655,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+46.673927,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+46.621643,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+46.621681,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+47.037029,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+46.997833,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+47.048618,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+47.285713,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+47.393005,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+47.324116,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+46.626583,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+46.692371,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+46.703560,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+47.011681,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+46.950756,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+47.047894,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+47.399475,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+47.371826,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+47.377480,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+46.727024,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+46.695332,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+46.687572,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+47.055302,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+47.054821,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+46.994289,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+47.315742,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+47.317150,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+47.363335,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+46.676479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+46.725178,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+46.635029,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+46.950531,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+46.982483,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+46.998974,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+47.333546,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+47.278755,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+47.366600,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+46.713226,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+46.703564,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+46.637722,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+47.009277,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+47.056637,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+47.011280,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+47.293018,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+47.363247,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+47.286766,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+47.687065,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+47.649410,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+47.713474,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+47.986198,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+47.995392,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+47.955612,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+48.347065,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+48.340664,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+48.297932,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+47.659065,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+47.634525,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+47.721451,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+48.034889,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+47.993057,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+47.979973,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+48.346474,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+48.330547,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+48.383636,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+47.686356,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+47.669079,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+47.617249,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+48.044636,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+48.021038,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+48.068111,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+48.299980,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+48.296555,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+48.373402,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+47.626652,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+47.641769,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+47.702354,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+48.069008,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+47.959641,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+48.020424,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+48.290855,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+48.339386,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+48.318069,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+47.642067,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+47.715958,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+47.736568,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+48.034843,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+47.982769,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+48.069923,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+48.288204,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+48.276272,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+48.344475,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+47.641762,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+47.694607,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+47.721081,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+47.973557,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+48.025661,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+47.997417,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+48.341618,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+48.344677,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+48.289959,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+47.618866,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+47.666527,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+47.675365,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+48.017117,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+47.960766,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+48.067719,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+48.291775,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+48.366272,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+48.308060,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+47.684368,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+47.735039,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+47.672207,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+47.975010,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+48.059967,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+47.981464,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+48.397331,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+48.387394,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+48.302265,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+47.619839,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+47.661945,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+47.609917,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+47.982658,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+47.962666,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+48.015163,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+48.295593,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+48.310127,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+48.380020,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+47.648434,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+47.682293,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+47.661728,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+48.010979,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+47.999489,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+47.973412,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+48.307499,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+48.350727,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+48.361591,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+47.690739,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+47.605228,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+47.631180,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+47.990154,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+48.012112,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+47.992966,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+48.350491,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+48.292965,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+48.379539,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+47.698803,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+47.702671,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+47.626671,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+47.955826,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+48.009613,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+47.937950,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+48.335545,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+48.390800,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+48.331474,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+48.716480,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+48.623821,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+48.733986,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+48.960915,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+48.945244,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+48.978504,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+49.346893,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+49.388523,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+49.310513,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+48.631317,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+48.735088,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+48.652313,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+48.988537,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+48.938858,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+48.941730,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+49.316799,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+49.300362,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+49.370628,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+48.673096,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+48.691753,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+48.697811,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+49.012577,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+49.024475,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+48.958012,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+49.287712,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+49.313313,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+49.367317,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+48.614605,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+48.678799,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+48.656208,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+48.945969,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+48.989525,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+49.057896,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+49.282978,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+49.275352,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+49.302803,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+48.611649,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+48.624172,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+48.637882,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+48.993904,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+48.961384,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+48.947807,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+49.273899,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+49.292019,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+49.317478,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+48.656464,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+48.713917,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+48.734833,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+48.937801,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+49.029655,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+49.047318,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+49.392506,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+49.395107,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+49.397663,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+48.618313,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+48.615440,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+48.690914,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+48.971497,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+48.988041,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+49.000229,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+49.313232,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+49.386593,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+49.337692,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+48.661903,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+48.605679,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+48.693295,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+48.992352,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+48.941692,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+49.031334,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+49.351669,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+49.274239,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+49.316685,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+48.720699,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+48.682846,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+48.680271,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+49.033054,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+48.946667,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+48.949692,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+49.308414,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+49.344860,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+49.333214,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+48.717094,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+48.725288,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+48.638515,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+48.941586,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+48.953323,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+48.968121,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+49.280514,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+49.292976,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+49.343239,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+48.726635,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+48.653942,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+48.713566,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+49.040932,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+49.058571,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+49.043633,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+49.278084,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+49.388290,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+49.379711,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+48.716511,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+48.729721,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+48.675732,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+49.040447,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+49.033958,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+48.963776,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+49.305309,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+49.389641,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+49.372097,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+49.705498,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+49.695965,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+49.620506,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+50.059479,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+49.947498,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+49.991550,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+50.349781,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+50.382607,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+50.356319,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+49.677635,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+49.685455,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+49.690193,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+49.993050,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+49.998989,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+49.982254,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+50.289734,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+50.275799,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+50.326683,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+49.658321,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+49.674042,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+49.610775,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+49.978592,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+50.019089,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+50.063732,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+50.305981,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+50.289906,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+50.364227,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+49.630993,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+49.707890,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+49.720825,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+49.988266,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+49.991985,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+50.042610,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+50.359509,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+50.286755,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+50.334942,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+49.672413,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+49.607201,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+49.702644,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+50.064842,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+50.006466,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+50.064621,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+50.305660,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+50.377014,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+50.313873,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+49.700172,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+49.633633,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+49.711540,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+49.950687,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+50.032253,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+50.028549,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+50.385937,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+50.369335,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+50.314556,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+49.615562,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+49.678707,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+49.649204,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+50.067486,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+50.046104,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+50.021000,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+50.371986,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+50.375145,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+50.356426,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+49.649418,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+49.649906,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+49.675701,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+49.983490,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+49.950081,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+49.967571,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+50.365299,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+50.323849,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+50.367722,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+49.639637,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+49.701443,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+49.633968,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+50.040974,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+50.027096,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+50.060658,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+50.376812,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+50.337978,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+50.382313,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+49.676273,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+49.628674,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+49.671150,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+49.946659,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+50.007149,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+50.022682,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+50.332314,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+50.284233,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+50.340729,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+49.730743,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+49.622456,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+49.637196,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+50.025772,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+49.955975,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+50.038589,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+50.299374,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+50.290150,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+50.327991,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+49.605602,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+49.613487,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+49.624001,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+50.035244,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+49.938637,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+50.035923,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+50.305851,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+50.310799,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+50.280338,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+50.735325,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+50.606846,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+50.736511,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+51.027554,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+51.023880,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+50.982521,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+51.350647,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+51.381878,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+51.278492,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+50.692207,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+50.669872,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+50.704826,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+51.001846,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+50.988876,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+50.981613,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+51.369946,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+51.291382,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+51.331699,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+50.721836,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+50.628307,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+50.657558,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+51.004269,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+51.015385,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+50.994194,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+51.316860,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+51.330624,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+51.319775,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+50.645603,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+50.731033,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+50.635216,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+51.004063,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+51.022362,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+50.972652,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+51.279488,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+51.373104,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+51.277416,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+50.615044,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+50.686710,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+50.671741,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+51.049969,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+50.949337,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+51.010189,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+51.365547,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+51.319122,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+51.400730,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+50.630699,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+50.706585,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+50.699154,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+50.992687,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+50.959564,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+51.007416,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+51.293850,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+51.376007,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+51.283459,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+50.641693,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+50.735149,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+50.697292,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+51.010830,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+50.966198,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+50.940189,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+51.349045,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+51.338211,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+51.318806,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+50.677929,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+50.695293,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+50.721634,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+50.973774,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+51.068050,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+50.995678,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+51.366947,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+51.283356,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+51.389118,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+50.657066,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+50.726471,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+50.672272,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+51.058365,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+50.989578,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+51.036560,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+51.310898,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+51.394413,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+51.352856,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+50.690769,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+50.717522,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+50.644562,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+50.959801,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+51.011539,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+51.056538,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+51.280529,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+51.363224,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+51.322338,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+50.628414,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+50.697929,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+50.687038,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+51.050968,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+51.053917,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+50.999794,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+51.356171,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+51.354500,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+51.380459,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+50.706757,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+50.666031,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+50.622726,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+50.958282,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+50.969242,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+51.001385,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+51.302269,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+51.301964,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+51.353413,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+51.630939,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+51.699631,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+51.621857,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+51.982479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+52.006050,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+52.005356,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+52.317818,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+52.319283,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+52.375954,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+51.610088,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+51.639099,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+51.636589,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+52.020035,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+52.061108,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+51.971542,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+52.345848,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+52.277534,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+52.334526,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+51.656239,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+51.729053,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+51.708714,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+52.007343,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+52.049263,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+52.061005,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+52.308758,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+52.298431,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+52.275700,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+51.728413,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+51.631313,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+51.619251,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+51.977493,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+52.031570,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+52.011909,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+52.317066,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+52.402222,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+52.357014,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+51.625847,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+51.648270,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+51.678558,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+52.030586,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+51.977901,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+52.022007,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+52.351101,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+52.306030,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+52.297516,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+51.647877,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+51.638260,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+51.717335,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+52.054508,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+51.963486,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+51.953827,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+52.305538,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+52.288986,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+52.273224,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+51.700977,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+51.682968,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+51.628822,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+52.020374,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+52.024208,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+52.000214,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+52.322063,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+52.291607,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+52.328602,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+51.671711,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+51.614861,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+51.617588,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+52.060966,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+51.972118,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+51.938828,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+52.386921,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+52.274612,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+52.368568,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+51.623245,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+51.696804,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+51.674152,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+52.029579,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+51.965565,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+52.010998,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+52.298222,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+52.336948,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+52.392838,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+51.624203,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+51.640831,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+51.699501,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+52.031651,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+52.049210,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+52.035568,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+52.295338,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+52.325520,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+52.383965,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+51.730610,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+51.700996,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+51.690926,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+52.006500,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+52.069317,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+51.993073,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+52.305397,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+52.291660,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+52.351475,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+51.614635,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+51.662369,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+51.674007,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+52.027401,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+51.946407,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+52.023781,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+52.278072,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+52.372562,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+52.375191,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+52.694954,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+52.695595,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+52.632366,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+52.990845,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+52.962437,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+53.011494,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+53.389458,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+53.274094,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+53.306568,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+52.608284,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+52.617493,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+52.728409,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+52.942303,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+53.030140,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+52.955906,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+53.377899,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+53.366997,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+53.349979,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+52.713860,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+52.698376,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+52.711193,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+52.974949,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+52.943798,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+52.974014,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+53.367130,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+53.370838,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+53.323532,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+52.677361,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+52.649460,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+52.626347,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+53.015766,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+52.967113,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+52.957211,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+53.312572,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+53.351154,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+53.318241,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+52.615887,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+52.618355,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+52.656731,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+53.016167,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+53.007858,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+53.002716,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+53.376572,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+53.358536,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+53.296841,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+52.615543,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+52.704777,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+52.686710,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+53.038116,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+53.015533,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+52.980106,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+53.386974,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+53.371235,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+53.324871,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+52.693130,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+52.618008,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+52.666336,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+53.011459,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+52.965286,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+52.975243,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+53.306725,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+53.369045,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+53.371475,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+52.719723,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+52.720478,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+52.713551,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+52.967709,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+53.033043,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+52.970211,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+53.316547,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+53.277119,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+53.324535,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+52.720387,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+52.679905,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+52.680450,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+52.949619,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+53.050026,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+53.041424,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+53.283997,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+53.394337,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+53.388470,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+52.712414,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+52.662300,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+52.624870,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+53.030212,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+53.069317,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+52.971638,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+53.375839,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+53.342739,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+53.329563,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+52.737240,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+52.700356,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+52.654903,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+53.062889,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+53.056408,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+53.059891,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+53.317554,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+53.271595,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+53.350712,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+52.649971,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+52.684444,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+52.610802,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+52.960007,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+53.036541,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+52.998898,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+53.367245,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+53.352997,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+53.395786,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+53.670612,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+53.632431,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+53.657814,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+53.943684,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+53.954571,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+53.987125,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+54.390480,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+54.291046,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+54.358166,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+53.614635,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+53.735630,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+53.608078,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+54.000977,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+54.026829,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+53.991798,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+54.377228,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+54.312450,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+54.381931,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+53.651970,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+53.687832,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+53.712574,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+54.038002,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+54.064621,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+54.009644,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+54.276558,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+54.365456,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+54.382923,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+53.633945,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+53.676472,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+53.716320,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+53.984699,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+53.950790,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+54.028702,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+54.357204,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+54.376453,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+54.273373,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+53.644760,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+53.688423,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+53.691917,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+53.950211,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+53.976982,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+53.948948,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+54.388542,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+54.363510,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+54.371674,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+53.643742,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+53.625565,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+53.646152,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+54.014378,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+54.056015,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+53.979465,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+54.294003,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+54.296844,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+54.383389,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+53.729374,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+53.650967,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+53.695461,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+54.063984,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+54.045067,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+53.941700,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+54.297188,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+54.333500,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+54.356293,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+53.727497,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+53.712429,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+53.649803,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+54.017570,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+54.002716,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+54.055298,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+54.296902,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+54.360882,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+54.296490,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+53.613083,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+53.685211,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+53.671467,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+53.978893,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+54.011002,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+53.956844,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+54.282894,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+54.387108,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+54.281097,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+53.626904,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+53.685349,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+53.632969,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+54.016342,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+53.976845,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+54.005035,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+54.274376,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+54.361469,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+54.325382,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+53.643448,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+53.693054,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+53.648968,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+53.966736,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+54.070553,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+54.033905,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+54.292656,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+54.280853,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+54.391315,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+53.636917,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+53.642380,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+53.613216,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+53.996807,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+54.069500,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+53.945423,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+54.305920,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+54.379189,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+54.372742,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+54.693924,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+54.676086,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+54.613514,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+54.952682,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+54.976219,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+54.962502,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+55.307735,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+55.290672,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+55.396164,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+54.710449,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+54.624012,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+54.626160,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+54.999294,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+55.019798,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+55.057869,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+55.402096,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+55.398117,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+55.348492,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+54.639481,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+54.647446,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+54.651363,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+55.039425,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+54.959030,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+55.033867,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+55.360237,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+55.352444,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+55.276131,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+54.688534,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+54.715374,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+54.642300,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+55.025833,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+55.040443,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+54.949482,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+55.302265,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+55.294678,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+55.270836,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+54.697056,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+54.712547,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+54.721497,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+55.026173,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+54.962898,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+55.056728,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+55.270905,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+55.403297,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+55.381920,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+54.724922,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+54.716347,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+54.708069,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+55.067360,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+55.064331,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+55.034916,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+55.351192,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+55.386513,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+55.315056,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+54.610527,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+54.614216,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+54.654854,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+55.018661,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+54.964916,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+54.987629,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+55.336491,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+55.332569,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+55.275803,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+54.722462,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+54.716061,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+54.707489,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+55.064869,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+55.058994,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+54.983269,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+55.341949,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+55.367432,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+55.379936,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+54.622005,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+54.691021,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+54.673626,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+55.018864,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+54.957203,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+55.028900,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+55.341743,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+55.383537,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+55.371124,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+54.651810,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+54.644814,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+54.612583,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+55.011436,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+55.060234,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+55.069397,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+55.294201,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+55.299255,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+55.322685,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+54.722061,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+54.667282,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+54.692017,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+55.060532,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+55.058987,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+55.033676,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+55.278572,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+55.403397,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+55.289310,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+54.707287,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+54.657356,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+54.685635,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+55.028938,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+55.067745,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+54.995712,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+55.388107,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+55.391640,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+55.395531,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+55.730061,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+55.696907,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+55.691681,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+55.997250,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+56.032532,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+56.023365,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+56.354500,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+56.330463,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+56.371552,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+55.714981,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+55.661247,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+55.668182,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+56.039322,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+56.053524,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+55.960644,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+56.358589,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+56.328312,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+56.299282,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+55.647221,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+55.611225,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+55.618931,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+56.011883,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+55.981541,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+55.959618,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+56.333344,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+56.331421,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+56.379055,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+55.664707,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+55.634380,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+55.676636,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+56.032452,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+55.972507,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+55.960548,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+56.318226,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+56.369339,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+56.302654,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+55.663589,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+55.607910,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+55.662567,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+55.978821,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+55.996094,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+56.019905,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+56.370995,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+56.368313,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+56.372780,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+55.683880,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+55.718498,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+55.733231,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+56.058517,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+55.951508,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+55.958839,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+56.279301,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+56.383095,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+56.324730,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+55.717930,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+55.622799,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+55.666920,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+55.937389,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+55.954536,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+56.016182,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+56.385044,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+56.376297,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+56.387276,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+55.653511,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+55.698254,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+55.673859,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+56.045708,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+56.019489,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+55.974483,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+56.296539,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+56.336082,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+56.323605,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+55.684284,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+55.622993,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+55.664070,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+56.041676,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+56.050884,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+55.959030,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+56.360207,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+56.344063,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+56.333351,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+55.714115,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+55.700523,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+55.659512,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+56.056271,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+55.996601,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+56.010624,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+56.354332,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+56.381252,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+56.339428,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+55.616741,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+55.697853,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+55.709442,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+55.958920,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+55.982941,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+56.016197,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+56.327213,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+56.287704,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+56.344288,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+55.724167,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+55.693863,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+55.666615,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+55.947884,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+56.029186,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+55.953136,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+56.369884,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+56.359581,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+56.388435,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-7.txt b/scenes/cell-growth/results/particles-frame-7.txt new file mode 100644 index 00000000..9d2bf78c --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-7.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+42.762943,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+42.864189,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+42.836521,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+43.092815,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+43.181091,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+43.092571,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+43.440151,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+43.528465,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+43.493465,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+42.847771,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+42.816586,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+42.866772,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+43.081100,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+43.113625,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+43.117359,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+43.532421,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+43.432228,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+43.497471,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+42.846802,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+42.809517,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+42.846985,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+43.090969,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+43.140598,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+43.082928,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+43.435616,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+43.498962,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+43.474445,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+42.837284,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+42.842262,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+42.790382,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+43.133102,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+43.189869,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+43.126686,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+43.464115,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+43.456715,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+43.427059,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+42.812229,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+42.834015,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+42.763439,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+43.131393,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+43.174767,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+43.123268,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+43.539993,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+43.471275,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+43.458508,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+42.757393,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+42.752106,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+42.820454,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+43.075592,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+43.133060,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+43.086277,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+43.466476,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+43.416901,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+43.432453,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+42.873032,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+42.793205,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+42.779018,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+43.181412,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+43.175339,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+43.147297,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+43.515800,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+43.451889,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+43.491539,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+42.747635,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+42.833912,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+42.864498,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+43.103786,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+43.105145,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+43.083614,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+43.530518,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+43.426201,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+43.493771,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+42.796303,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+42.774681,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+42.778503,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+43.084492,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+43.074051,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+43.097832,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+43.488033,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+43.463112,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+43.477921,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+42.765907,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+42.780655,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+42.762890,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+43.201374,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+43.076267,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+43.182308,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+43.487362,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+43.441311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+43.457127,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+42.811367,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+42.854225,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+42.865921,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+43.170818,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+43.087944,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+43.117554,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+43.520821,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+43.506275,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+43.418491,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+42.826801,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+42.841873,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+42.811840,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+43.173687,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+43.136177,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+43.114491,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+43.485935,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+43.522907,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+43.413361,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+43.770924,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+43.838879,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+43.784336,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+44.132690,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+44.176109,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+44.103451,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+44.430477,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+44.487278,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+44.407593,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+43.820179,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+43.781025,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+43.743713,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+44.133224,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+44.139549,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+44.185894,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+44.456642,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+44.495514,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+44.465240,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+43.747990,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+43.783989,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+43.833725,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+44.191410,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+44.106865,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+44.079704,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+44.504902,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+44.434429,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+44.423439,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+43.778252,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+43.757507,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+43.757511,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+44.096317,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+44.180882,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+44.087997,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+44.465691,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+44.499485,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+44.480476,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+43.815445,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+43.752045,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+43.871552,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+44.127579,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+44.120811,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+44.104240,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+44.439388,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+44.532890,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+44.500435,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+43.762341,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+43.756992,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+43.778934,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+44.134602,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+44.202839,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+44.181824,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+44.521767,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+44.462147,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+44.413200,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+43.767670,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+43.787613,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+43.763855,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+44.097778,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+44.078079,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+44.192200,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+44.499416,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+44.507278,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+44.491829,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+43.791439,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+43.755253,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+43.750172,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+44.193573,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+44.197205,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+44.098061,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+44.501831,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+44.442291,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+44.502773,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+43.778984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+43.846237,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+43.798653,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+44.094505,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+44.081409,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+44.205647,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+44.416229,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+44.409706,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+44.461864,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+43.767429,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+43.755665,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+43.866070,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+44.180214,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+44.142471,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+44.176960,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+44.440453,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+44.457508,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+44.475380,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+43.853561,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+43.791264,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+43.747272,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+44.138653,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+44.118732,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+44.134472,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+44.458954,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+44.517612,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+44.431480,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+43.772785,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+43.843643,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+43.803211,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+44.166203,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+44.170906,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+44.108208,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+44.472622,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+44.461845,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+44.434727,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+44.802818,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+44.762749,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+44.824875,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+45.164177,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+45.143082,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+45.198647,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+45.525146,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+45.410774,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+45.432243,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+44.792679,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+44.863045,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+44.866699,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+45.140190,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+45.083210,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+45.118240,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+45.486164,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+45.516045,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+45.435280,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+44.776924,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+44.760323,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+44.782944,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+45.192448,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+45.099564,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+45.074581,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+45.412369,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+45.418793,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+45.468929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+44.764149,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+44.843357,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+44.843483,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+45.096119,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+45.203362,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+45.154709,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+45.518978,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+45.427391,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+45.483459,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+44.770748,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+44.857803,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+44.828918,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+45.185726,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+45.165550,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+45.094154,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+45.470444,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+45.482342,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+45.500343,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+44.770042,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+44.809101,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+44.749260,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+45.133717,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+45.088051,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+45.129204,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+45.461277,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+45.476337,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+45.424984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+44.823818,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+44.764477,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+44.767292,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+45.112087,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+45.127773,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+45.194244,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+45.451420,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+45.538448,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+45.518314,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+44.855515,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+44.740547,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+44.872242,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+45.187328,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+45.085316,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+45.161064,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+45.455154,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+45.450653,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+45.489613,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+44.754967,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+44.783123,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+44.806747,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+45.196465,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+45.188049,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+45.115307,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+45.498184,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+45.494488,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+45.524143,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+44.870209,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+44.785618,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+44.807213,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+45.206493,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+45.086346,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+45.153114,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+45.441189,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+45.508793,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+45.409199,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+44.806423,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+44.740345,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+44.762283,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+45.118393,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+45.190964,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+45.163506,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+45.453106,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+45.480476,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+45.461456,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+44.778534,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+44.745461,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+44.857754,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+45.082668,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+45.204212,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+45.159206,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+45.457733,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+45.453369,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+45.465164,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+45.842102,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+45.803146,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+45.806683,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+46.192032,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+46.164917,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+46.082146,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+46.507896,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+46.419678,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+46.464279,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+45.870735,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+45.852596,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+45.755386,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+46.132374,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+46.115067,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+46.181770,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+46.494854,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+46.407207,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+46.413506,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+45.781654,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+45.806549,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+45.783245,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+46.150719,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+46.146069,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+46.148682,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+46.500324,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+46.517025,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+46.456913,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+45.795784,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+45.845272,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+45.829605,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+46.139484,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+46.170826,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+46.157146,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+46.409176,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+46.458935,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+46.518612,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+45.843922,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+45.797733,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+45.866634,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+46.138214,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+46.154057,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+46.076664,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+46.417389,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+46.521252,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+46.443134,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+45.797794,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+45.776421,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+45.822315,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+46.205593,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+46.090088,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+46.118622,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+46.415695,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+46.497547,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+46.424419,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+45.779819,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+45.819340,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+45.776276,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+46.138321,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+46.078991,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+46.128841,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+46.478088,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+46.450340,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+46.465652,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+45.809925,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+45.757641,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+45.757679,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+46.173027,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+46.133831,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+46.184616,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+46.421711,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+46.529003,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+46.460114,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+45.762581,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+45.828369,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+45.839558,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+46.147678,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+46.086754,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+46.183891,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+46.535473,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+46.507824,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+46.513477,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+45.863022,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+45.831329,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+45.823570,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+46.191299,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+46.190819,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+46.130287,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+46.451740,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+46.453148,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+46.499332,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+45.812477,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+45.861176,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+45.771027,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+46.086529,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+46.118481,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+46.134972,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+46.469543,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+46.414753,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+46.502598,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+45.849224,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+45.839561,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+45.773720,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+46.145275,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+46.192635,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+46.147278,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+46.429016,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+46.499245,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+46.422764,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+46.823063,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+46.785408,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+46.849472,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+47.122196,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+47.131390,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+47.091610,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+47.483063,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+47.476662,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+47.433929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+46.795063,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+46.770523,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+46.857449,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+47.170887,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+47.129055,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+47.115971,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+47.482471,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+47.466545,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+47.519634,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+46.822353,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+46.805077,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+46.753246,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+47.180634,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+47.157036,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+47.204109,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+47.435978,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+47.432552,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+47.509399,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+46.762650,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+46.777767,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+46.838352,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+47.205006,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+47.095638,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+47.156422,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+47.426853,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+47.475384,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+47.454067,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+46.778065,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+46.851955,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+46.872566,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+47.170841,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+47.118767,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+47.205921,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+47.424202,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+47.412270,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+47.480473,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+46.777760,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+46.830605,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+46.857079,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+47.109554,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+47.161659,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+47.133415,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+47.477615,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+47.480675,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+47.425957,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+46.754864,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+46.802525,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+46.811363,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+47.153114,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+47.096764,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+47.203716,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+47.427773,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+47.502274,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+47.444057,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+46.820366,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+46.871037,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+46.808205,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+47.111008,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+47.195965,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+47.117462,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+47.533329,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+47.523392,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+47.438263,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+46.755836,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+46.797943,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+46.745914,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+47.118656,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+47.098663,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+47.151161,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+47.431591,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+47.446125,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+47.516018,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+46.784431,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+46.818291,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+46.797726,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+47.146976,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+47.135487,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+47.109409,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+47.443497,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+47.486725,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+47.497589,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+46.826736,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+46.741226,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+46.767178,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+47.126152,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+47.148109,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+47.128963,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+47.486488,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+47.428963,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+47.515537,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+46.834801,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+46.838669,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+46.762669,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+47.091824,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+47.145611,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+47.073948,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+47.471542,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+47.526798,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+47.467472,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+47.852478,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+47.759819,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+47.869984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+48.096912,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+48.081242,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+48.114502,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+48.482891,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+48.524521,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+48.446510,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+47.767315,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+47.871086,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+47.788311,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+48.124535,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+48.074856,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+48.077728,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+48.452797,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+48.436359,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+48.506626,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+47.809093,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+47.827751,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+47.833809,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+48.148575,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+48.160473,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+48.094009,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+48.423710,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+48.449310,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+48.503315,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+47.750603,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+47.814796,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+47.792206,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+48.081966,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+48.125523,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+48.193893,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+48.418976,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+48.411350,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+48.438801,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+47.747646,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+47.760170,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+47.773880,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+48.129902,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+48.097382,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+48.083805,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+48.409897,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+48.428017,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+48.453476,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+47.792461,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+47.849915,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+47.870831,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+48.073799,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+48.165653,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+48.183315,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+48.528503,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+48.531105,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+48.533661,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+47.754311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+47.751442,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+47.826912,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+48.107494,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+48.124039,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+48.136227,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+48.449230,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+48.522591,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+48.473690,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+47.797901,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+47.741676,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+47.829292,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+48.128349,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+48.077690,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+48.167332,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+48.487667,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+48.410236,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+48.452682,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+47.856697,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+47.818844,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+47.816269,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+48.169052,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+48.082664,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+48.085690,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+48.444412,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+48.480858,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+48.469212,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+47.853092,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+47.861286,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+47.774513,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+48.077583,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+48.089321,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+48.104118,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+48.416512,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+48.428974,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+48.479237,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+47.862633,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+47.789940,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+47.849564,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+48.176929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+48.194569,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+48.179630,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+48.414082,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+48.524288,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+48.515709,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+47.852509,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+47.865719,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+47.811729,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+48.176445,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+48.169956,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+48.099773,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+48.441307,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+48.525639,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+48.508095,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+48.841496,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+48.831963,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+48.756504,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+49.195477,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+49.083496,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+49.127548,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+49.485779,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+49.518604,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+49.492317,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+48.813633,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+48.821453,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+48.826191,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+49.129047,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+49.134987,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+49.118252,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+49.425732,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+49.411797,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+49.462681,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+48.794319,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+48.810040,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+48.746773,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+49.114590,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+49.155087,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+49.199730,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+49.441978,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+49.425903,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+49.500225,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+48.766991,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+48.843887,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+48.856823,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+49.124264,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+49.127983,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+49.178608,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+49.495506,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+49.422752,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+49.470940,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+48.808411,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+48.743198,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+48.838642,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+49.200840,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+49.142464,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+49.200619,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+49.441658,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+49.513012,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+49.449871,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+48.836170,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+48.769630,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+48.847538,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+49.086685,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+49.168251,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+49.164547,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+49.521935,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+49.505333,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+49.450554,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+48.751560,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+48.814705,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+48.785202,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+49.203484,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+49.182102,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+49.156998,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+49.507984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+49.511143,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+49.492424,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+48.785416,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+48.785904,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+48.811699,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+49.119488,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+49.086079,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+49.103569,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+49.501297,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+49.459846,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+49.503719,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+48.775635,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+48.837440,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+48.769966,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+49.176971,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+49.163094,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+49.196655,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+49.512810,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+49.473976,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+49.518311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+48.812271,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+48.764671,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+48.807148,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+49.082657,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+49.143147,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+49.158680,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+49.468311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+49.420231,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+49.476727,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+48.866741,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+48.758453,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+48.773193,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+49.161770,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+49.091972,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+49.174587,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+49.435371,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+49.426147,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+49.463989,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+48.741600,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+48.749485,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+48.759998,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+49.171242,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+49.074635,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+49.171921,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+49.441849,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+49.446796,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+49.416336,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+49.871323,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+49.742844,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+49.872509,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+50.163551,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+50.159878,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+50.118519,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+50.486645,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+50.517876,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+50.414490,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+49.828205,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+49.805870,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+49.840824,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+50.137844,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+50.124874,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+50.117611,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+50.505943,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+50.427380,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+50.467697,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+49.857834,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+49.764305,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+49.793556,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+50.140266,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+50.151382,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+50.130192,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+50.452858,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+50.466621,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+50.455772,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+49.781601,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+49.867031,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+49.771214,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+50.140060,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+50.158360,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+50.108650,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+50.415485,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+50.509102,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+50.413414,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+49.751041,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+49.822708,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+49.807739,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+50.185966,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+50.085335,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+50.146187,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+50.501545,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+50.455120,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+50.536728,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+49.766697,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+49.842583,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+49.835152,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+50.128685,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+50.095562,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+50.143414,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+50.429848,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+50.512005,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+50.419456,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+49.777691,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+49.871147,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+49.833290,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+50.146828,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+50.102196,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+50.076187,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+50.485043,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+50.474209,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+50.454803,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+49.813927,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+49.831291,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+49.857632,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+50.109772,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+50.204048,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+50.131676,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+50.502945,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+50.419353,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+50.525116,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+49.793064,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+49.862469,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+49.808270,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+50.194363,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+50.125576,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+50.172558,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+50.446896,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+50.530411,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+50.488853,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+49.826767,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+49.853519,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+49.780560,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+50.095798,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+50.147537,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+50.192535,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+50.416527,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+50.499222,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+50.458336,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+49.764412,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+49.833927,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+49.823036,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+50.186966,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+50.189915,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+50.135792,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+50.492168,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+50.490498,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+50.516457,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+49.842754,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+49.802029,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+49.758724,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+50.094280,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+50.105240,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+50.137383,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+50.438267,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+50.437962,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+50.489410,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+50.766937,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+50.835629,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+50.757854,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+51.118477,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+51.142048,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+51.141354,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+51.453815,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+51.455280,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+51.511951,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+50.746086,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+50.775097,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+50.772587,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+51.156033,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+51.197105,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+51.107540,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+51.481846,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+51.413532,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+51.470524,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+50.792236,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+50.865051,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+50.844711,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+51.143341,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+51.185261,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+51.197002,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+51.444756,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+51.434429,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+51.411697,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+50.864410,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+50.767311,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+50.755249,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+51.113491,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+51.167568,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+51.147907,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+51.453064,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+51.538219,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+51.493011,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+50.761845,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+50.784267,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+50.814556,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+51.166584,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+51.113899,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+51.158005,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+51.487099,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+51.442028,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+51.433514,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+50.783875,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+50.774258,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+50.853333,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+51.190506,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+51.099483,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+51.089825,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+51.441536,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+51.424984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+51.409222,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+50.836975,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+50.818966,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+50.764820,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+51.156372,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+51.160206,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+51.136211,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+51.458061,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+51.427605,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+51.464600,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+50.807709,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+50.750858,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+50.753586,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+51.196964,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+51.108116,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+51.074825,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+51.522919,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+51.410610,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+51.504566,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+50.759243,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+50.832802,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+50.810150,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+51.165577,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+51.101562,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+51.146996,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+51.434219,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+51.472946,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+51.528835,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+50.760201,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+50.776829,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+50.835499,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+51.167648,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+51.185207,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+51.171566,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+51.431335,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+51.461517,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+51.519962,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+50.866608,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+50.836994,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+50.826923,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+51.142498,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+51.205315,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+51.129070,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+51.441395,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+51.427658,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+51.487473,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+50.750633,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+50.798367,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+50.810005,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+51.163399,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+51.082405,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+51.159779,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+51.414070,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+51.508560,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+51.511189,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+51.830952,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+51.831593,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+51.768364,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+52.126842,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+52.098434,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+52.147491,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+52.525459,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+52.410091,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+52.442566,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+51.744282,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+51.753490,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+51.864407,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+52.078300,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+52.166138,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+52.091904,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+52.513897,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+52.502995,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+52.485977,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+51.849857,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+51.834373,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+51.847191,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+52.110947,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+52.079796,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+52.110012,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+52.503128,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+52.506836,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+52.459530,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+51.813358,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+51.785458,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+51.762344,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+52.151764,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+52.103111,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+52.093208,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+52.448570,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+52.487152,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+52.454239,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+51.751884,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+51.754353,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+51.792728,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+52.152164,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+52.143856,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+52.138714,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+52.512569,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+52.494534,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+52.432838,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+51.751541,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+51.840775,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+51.822708,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+52.174114,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+52.151531,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+52.116104,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+52.522972,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+52.507233,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+52.460869,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+51.829128,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+51.754005,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+51.802334,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+52.147457,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+52.101284,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+52.111240,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+52.442722,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+52.505043,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+52.507473,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+51.855721,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+51.856476,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+51.849548,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+52.103706,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+52.169041,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+52.106209,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+52.452545,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+52.413116,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+52.460533,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+51.856384,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+51.815903,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+51.816448,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+52.085617,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+52.186024,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+52.177422,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+52.419994,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+52.530334,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+52.524467,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+51.848412,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+51.798298,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+51.760868,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+52.166210,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+52.205315,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+52.107635,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+52.511837,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+52.478737,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+52.465561,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+51.873238,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+51.836353,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+51.790901,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+52.198887,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+52.192406,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+52.195889,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+52.453552,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+52.407593,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+52.486710,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+51.785969,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+51.820442,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+51.746799,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+52.096004,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+52.172539,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+52.134895,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+52.503242,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+52.488995,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+52.531784,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+52.806610,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+52.768429,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+52.793812,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+53.079681,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+53.090569,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+53.123123,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+53.526478,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+53.427048,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+53.494164,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+52.750633,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+52.871628,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+52.744076,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+53.136974,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+53.162827,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+53.127796,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+53.513226,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+53.448448,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+53.517929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+52.787968,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+52.823830,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+52.848572,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+53.174000,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+53.200619,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+53.145641,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+53.412556,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+53.501453,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+53.518921,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+52.769943,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+52.812469,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+52.852318,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+53.120697,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+53.086788,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+53.164700,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+53.493202,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+53.512451,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+53.409370,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+52.780758,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+52.824421,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+52.827915,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+53.086208,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+53.112980,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+53.084946,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+53.524540,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+53.499508,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+53.507671,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+52.779739,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+52.761562,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+52.782150,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+53.150375,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+53.192013,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+53.115463,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+53.430000,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+53.432842,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+53.519386,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+52.865372,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+52.786964,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+52.831459,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+53.199982,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+53.181065,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+53.077698,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+53.433186,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+53.469498,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+53.492290,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+52.863495,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+52.848427,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+52.785801,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+53.153568,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+53.138714,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+53.191296,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+53.432899,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+53.496880,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+53.432487,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+52.749081,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+52.821209,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+52.807465,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+53.114891,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+53.146999,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+53.092842,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+53.418892,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+53.523106,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+53.417095,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+52.762901,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+52.821346,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+52.768967,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+53.152340,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+53.112843,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+53.141033,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+53.410374,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+53.497467,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+53.461380,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+52.779446,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+52.829052,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+52.784966,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+53.102734,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+53.206551,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+53.169903,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+53.428654,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+53.416851,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+53.527313,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+52.772915,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+52.778378,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+52.749214,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+53.132805,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+53.205498,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+53.081421,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+53.441917,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+53.515186,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+53.508739,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+53.829922,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+53.812084,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+53.749512,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+54.088680,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+54.112217,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+54.098499,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+54.443733,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+54.426670,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+54.532162,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+53.846447,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+53.760010,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+53.762157,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+54.135292,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+54.155796,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+54.193867,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+54.538094,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+54.534115,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+54.484489,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+53.775478,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+53.783443,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+53.787361,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+54.175423,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+54.095028,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+54.169865,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+54.496235,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+54.488441,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+54.412128,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+53.824532,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+53.851372,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+53.778297,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+54.161831,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+54.176441,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+54.085480,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+54.438263,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+54.430676,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+54.406834,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+53.833054,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+53.848545,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+53.857494,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+54.162170,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+54.098896,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+54.192726,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+54.406902,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+54.539295,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+54.517918,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+53.860920,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+53.852345,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+53.844067,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+54.203358,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+54.200329,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+54.170914,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+54.487190,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+54.522511,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+54.451054,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+53.746525,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+53.750214,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+53.790852,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+54.154659,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+54.100914,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+54.123627,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+54.472488,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+54.468567,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+54.411800,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+53.858459,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+53.852058,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+53.843487,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+54.200867,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+54.194992,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+54.119267,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+54.477947,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+54.503429,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+54.515934,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+53.758003,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+53.827019,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+53.809624,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+54.154861,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+54.093201,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+54.164898,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+54.477741,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+54.519535,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+54.507122,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+53.787807,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+53.780811,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+53.748581,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+54.147434,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+54.196232,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+54.205395,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+54.430199,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+54.435253,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+54.458683,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+53.858059,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+53.803280,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+53.828014,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+54.196529,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+54.194984,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+54.169674,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+54.414570,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+54.539394,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+54.425308,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+53.843285,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+53.793354,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+53.821632,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+54.164936,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+54.203743,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+54.131710,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+54.524105,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+54.527637,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+54.531528,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+54.866058,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+54.832905,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+54.827679,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+55.133247,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+55.168530,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+55.159363,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+55.490498,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+55.466461,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+55.507549,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+54.850979,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+54.797245,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+54.804180,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+55.175320,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+55.189522,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+55.096642,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+55.494587,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+55.464310,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+55.435280,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+54.783218,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+54.747223,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+54.754929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+55.147881,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+55.117538,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+55.095615,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+55.469341,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+55.467419,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+55.515053,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+54.800705,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+54.770378,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+54.812634,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+55.168449,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+55.108505,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+55.096546,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+55.454224,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+55.505337,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+55.438652,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+54.799587,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+54.743908,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+54.798565,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+55.114819,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+55.132092,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+55.155903,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+55.506992,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+55.504311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+55.508778,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+54.819878,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+54.854496,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+54.869228,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+55.194515,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+55.087505,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+55.094837,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+55.415298,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+55.519093,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+55.460728,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+54.853928,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+54.758797,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+54.802917,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+55.073387,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+55.090534,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+55.152180,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+55.521042,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+55.512295,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+55.523273,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+54.789509,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+54.834251,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+54.809856,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+55.181705,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+55.155487,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+55.110481,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+55.432537,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+55.472080,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+55.459602,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+54.820282,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+54.758991,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+54.800072,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+55.177673,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+55.186882,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+55.095032,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+55.496204,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+55.480061,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+55.469353,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+54.850113,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+54.836525,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+54.795509,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+55.192268,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+55.132599,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+55.146622,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+55.490330,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+55.517250,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+55.475426,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+54.752739,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+54.833851,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+54.845440,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+55.094917,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+55.118938,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+55.152195,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+55.463211,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+55.423702,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+55.480286,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+54.860165,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+54.829861,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+54.802612,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+55.083881,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+55.165184,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+55.089134,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+55.505882,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+55.495579,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+55.524433,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-8.txt b/scenes/cell-growth/results/particles-frame-8.txt new file mode 100644 index 00000000..af466a23 --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-8.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+41.770943,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+41.872189,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+41.844521,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+42.100815,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+42.189091,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+42.100571,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+42.448151,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+42.536465,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+42.501465,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+41.855770,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+41.824585,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+41.874771,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+42.089100,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+42.121624,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+42.125359,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+42.540421,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+42.440228,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+42.505470,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+41.854801,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+41.817516,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+41.854984,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+42.098969,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+42.148598,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+42.090927,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+42.443615,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+42.506962,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+42.482445,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+41.845284,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+41.850262,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+41.798382,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+42.141102,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+42.197868,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+42.134686,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+42.472115,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+42.464714,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+42.435059,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+41.820229,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+41.842014,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+41.771439,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+42.139393,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+42.182766,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+42.131268,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+42.547993,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+42.479275,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+42.466507,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+41.765392,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+41.760105,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+41.828453,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+42.083591,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+42.141060,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+42.094276,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+42.474476,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+42.424900,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+42.440453,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+41.881031,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+41.801205,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+41.787018,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+42.189411,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+42.183338,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+42.155296,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+42.523800,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+42.459888,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+42.499538,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+41.755634,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+41.841911,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+41.872498,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+42.111786,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+42.113144,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+42.091614,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+42.538517,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+42.434200,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+42.501770,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+41.804302,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+41.782681,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+41.786503,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+42.092491,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+42.082050,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+42.105831,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+42.496033,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+42.471111,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+42.485920,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+41.773907,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+41.788654,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+41.770889,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+42.209373,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+42.084267,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+42.190308,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+42.495361,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+42.449310,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+42.465126,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+41.819366,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+41.862225,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+41.873920,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+42.178818,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+42.095943,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+42.125553,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+42.528820,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+42.514275,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+42.426491,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+41.834801,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+41.849873,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+41.819839,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+42.181686,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+42.144176,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+42.122490,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+42.493935,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+42.530907,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+42.421360,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+42.778923,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+42.846878,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+42.792336,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+43.140690,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+43.184109,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+43.111450,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+43.438477,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+43.495277,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+43.415592,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+42.828178,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+42.789024,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+42.751713,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+43.141224,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+43.147549,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+43.193893,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+43.464642,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+43.503513,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+43.473240,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+42.755989,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+42.791988,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+42.841724,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+43.199409,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+43.114864,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+43.087704,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+43.512901,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+43.442429,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+43.431438,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+42.786251,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+42.765507,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+42.765511,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+43.104317,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+43.188881,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+43.095997,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+43.473690,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+43.507484,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+43.488476,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+42.823444,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+42.760044,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+42.879551,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+43.135578,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+43.128811,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+43.112240,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+43.447388,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+43.540890,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+43.508434,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+42.770340,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+42.764992,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+42.786934,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+43.142601,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+43.210838,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+43.189823,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+43.529766,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+43.470146,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+43.421200,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+42.775669,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+42.795612,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+42.771854,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+43.105778,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+43.086079,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+43.200199,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+43.507416,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+43.515278,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+43.499828,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+42.799438,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+42.763252,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+42.758171,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+43.201572,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+43.205204,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+43.106060,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+43.509830,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+43.450291,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+43.510773,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+42.786983,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+42.854237,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+42.806652,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+43.102505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+43.089409,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+43.213646,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+43.424229,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+43.417706,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+43.469864,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+42.775429,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+42.763664,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+42.874069,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+43.188213,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+43.150471,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+43.184959,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+43.448452,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+43.465508,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+43.483379,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+42.861561,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+42.799263,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+42.755272,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+43.146652,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+43.126732,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+43.142471,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+43.466953,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+43.525612,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+43.439480,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+42.780785,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+42.851643,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+42.811211,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+43.174202,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+43.178905,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+43.116207,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+43.480621,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+43.469845,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+43.442726,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+43.810818,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+43.770748,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+43.832874,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+44.172176,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+44.151081,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+44.206646,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+44.533146,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+44.418774,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+44.440243,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+43.800678,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+43.871044,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+43.874699,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+44.148190,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+44.091209,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+44.126240,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+44.494164,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+44.524044,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+44.443279,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+43.784924,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+43.768322,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+43.790943,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+44.200447,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+44.107563,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+44.082581,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+44.420368,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+44.426792,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+44.476929,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+43.772148,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+43.851357,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+43.851482,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+44.104118,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+44.211361,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+44.162708,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+44.526978,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+44.435390,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+44.491459,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+43.778748,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+43.865803,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+43.836918,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+44.193726,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+44.173550,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+44.102154,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+44.478443,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+44.490341,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+44.508343,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+43.778042,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+43.817101,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+43.757259,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+44.141716,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+44.096050,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+44.137203,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+44.469276,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+44.484337,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+44.432983,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+43.831818,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+43.772476,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+43.775291,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+44.120087,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+44.135773,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+44.202244,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+44.459419,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+44.546448,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+44.526314,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+43.863514,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+43.748547,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+43.880241,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+44.195328,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+44.093315,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+44.169064,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+44.463154,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+44.458652,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+44.497612,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+43.762966,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+43.791122,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+43.814747,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+44.204464,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+44.196049,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+44.123306,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+44.506184,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+44.502487,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+44.532143,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+43.878208,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+43.793617,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+43.815212,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+44.214493,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+44.094345,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+44.161114,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+44.449188,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+44.516792,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+44.417198,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+43.814423,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+43.748344,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+43.770283,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+44.126392,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+44.198963,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+44.171505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+44.461105,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+44.488476,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+44.469456,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+43.786533,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+43.753460,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+43.865753,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+44.090668,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+44.212212,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+44.167206,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+44.465733,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+44.461369,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+44.473164,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+44.850101,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+44.811146,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+44.814682,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+45.200031,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+45.172916,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+45.090145,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+45.515896,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+45.427677,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+45.472279,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+44.878735,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+44.860596,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+44.763386,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+45.140373,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+45.123066,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+45.189770,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+45.502853,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+45.415207,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+45.421505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+44.789654,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+44.814548,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+44.791245,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+45.158718,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+45.154068,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+45.156681,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+45.508324,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+45.525024,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+45.464912,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+44.803783,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+44.853271,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+44.837605,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+45.147484,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+45.178825,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+45.165146,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+45.417175,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+45.466934,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+45.526611,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+44.851921,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+44.805733,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+44.874634,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+45.146214,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+45.162056,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+45.084663,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+45.425388,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+45.529251,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+45.451134,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+44.805794,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+44.784420,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+44.830315,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+45.213593,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+45.098087,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+45.126621,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+45.423695,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+45.505547,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+45.432419,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+44.787819,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+44.827339,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+44.784275,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+45.146320,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+45.086990,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+45.136841,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+45.486088,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+45.458340,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+45.473652,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+44.817924,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+44.765640,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+44.765678,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+45.181026,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+45.141830,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+45.192616,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+45.429710,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+45.537003,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+45.468113,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+44.770580,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+44.836369,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+44.847557,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+45.155678,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+45.094753,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+45.191891,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+45.543472,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+45.515823,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+45.521477,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+44.871021,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+44.839329,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+44.831570,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+45.199299,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+45.198818,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+45.138287,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+45.459740,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+45.461147,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+45.507332,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+44.820477,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+44.869175,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+44.779026,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+45.094528,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+45.126480,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+45.142971,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+45.477543,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+45.422752,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+45.510597,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+44.857224,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+44.847561,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+44.781719,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+45.153275,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+45.200634,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+45.155277,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+45.437016,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+45.507244,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+45.430763,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+45.831062,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+45.793407,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+45.857471,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+46.130196,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+46.139389,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+46.099609,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+46.491062,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+46.484661,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+46.441929,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+45.803062,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+45.778522,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+45.865448,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+46.178886,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+46.137054,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+46.123970,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+46.490471,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+46.474545,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+46.527634,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+45.830353,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+45.813076,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+45.761246,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+46.188633,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+46.165035,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+46.212109,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+46.443977,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+46.440552,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+46.517399,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+45.770649,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+45.785767,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+45.846352,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+46.213005,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+46.103638,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+46.164421,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+46.434853,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+46.483383,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+46.462067,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+45.786064,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+45.859955,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+45.880566,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+46.178841,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+46.126766,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+46.213921,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+46.432201,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+46.420269,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+46.488472,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+45.785759,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+45.838604,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+45.865078,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+46.117554,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+46.169659,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+46.141415,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+46.485615,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+46.488674,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+46.433956,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+45.762863,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+45.810524,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+45.819363,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+46.161114,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+46.104763,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+46.211716,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+46.435772,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+46.510273,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+46.452057,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+45.828365,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+45.879036,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+45.816204,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+46.119007,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+46.203964,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+46.125462,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+46.541328,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+46.531391,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+46.446262,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+45.763836,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+45.805943,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+45.753914,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+46.126656,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+46.106663,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+46.159161,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+46.439590,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+46.454124,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+46.524017,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+45.792431,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+45.826290,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+45.805725,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+46.154976,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+46.143486,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+46.117409,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+46.451496,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+46.494724,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+46.505589,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+45.834736,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+45.749226,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+45.775177,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+46.134151,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+46.156109,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+46.136963,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+46.494488,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+46.436962,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+46.523537,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+45.842800,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+45.846668,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+45.770668,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+46.099823,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+46.153610,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+46.081947,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+46.479542,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+46.534798,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+46.475471,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+46.860477,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+46.767818,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+46.877983,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+47.104912,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+47.089241,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+47.122501,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+47.490891,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+47.532520,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+47.454510,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+46.775314,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+46.879086,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+46.796310,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+47.132534,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+47.082855,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+47.085728,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+47.460796,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+47.444359,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+47.514626,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+46.817093,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+46.835751,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+46.841808,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+47.156574,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+47.168472,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+47.102009,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+47.431709,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+47.457310,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+47.511314,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+46.758602,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+46.822796,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+46.800205,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+47.089966,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+47.133522,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+47.201893,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+47.426975,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+47.419350,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+47.446800,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+46.755646,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+46.768169,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+46.781879,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+47.137901,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+47.105381,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+47.091805,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+47.417896,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+47.436016,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+47.461475,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+46.800461,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+46.857914,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+46.878830,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+47.081799,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+47.173653,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+47.191315,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+47.536503,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+47.539104,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+47.541660,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+46.762310,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+46.759441,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+46.834911,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+47.115494,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+47.132038,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+47.144226,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+47.457230,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+47.530590,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+47.481689,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+46.805901,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+46.749676,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+46.837292,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+47.136349,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+47.085690,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+47.175331,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+47.495667,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+47.418236,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+47.460682,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+46.864697,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+46.826843,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+46.824268,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+47.177052,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+47.090664,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+47.093689,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+47.452412,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+47.488857,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+47.477211,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+46.861092,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+46.869286,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+46.782513,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+47.085583,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+47.097321,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+47.112118,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+47.424511,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+47.436974,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+47.487236,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+46.870632,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+46.797939,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+46.857563,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+47.184929,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+47.202568,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+47.187630,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+47.422081,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+47.532288,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+47.523708,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+46.860508,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+46.873718,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+46.819729,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+47.184444,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+47.177956,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+47.107773,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+47.449306,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+47.533638,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+47.516094,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+47.849495,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+47.839962,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+47.764503,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+48.203476,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+48.091496,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+48.135548,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+48.493778,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+48.526604,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+48.500317,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+47.821632,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+47.829453,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+47.834190,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+48.137047,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+48.142986,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+48.126251,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+48.433731,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+48.419796,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+48.470680,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+47.802319,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+47.818039,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+47.754772,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+48.122589,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+48.163086,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+48.207729,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+48.449978,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+48.433903,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+48.508224,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+47.774990,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+47.851887,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+47.864822,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+48.132263,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+48.135983,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+48.186607,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+48.503506,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+48.430752,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+48.478939,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+47.816410,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+47.751198,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+47.846642,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+48.208839,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+48.150463,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+48.208618,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+48.449657,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+48.521011,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+48.457870,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+47.844170,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+47.777630,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+47.855537,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+48.094685,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+48.176250,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+48.172546,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+48.529934,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+48.513332,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+48.458553,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+47.759560,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+47.822704,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+47.793201,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+48.211483,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+48.190102,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+48.164997,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+48.515984,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+48.519142,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+48.500423,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+47.793415,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+47.793903,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+47.819698,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+48.127487,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+48.094078,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+48.111568,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+48.509296,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+48.467846,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+48.511719,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+47.783634,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+47.845440,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+47.777966,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+48.184971,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+48.171093,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+48.204655,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+48.520809,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+48.481976,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+48.526310,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+47.820271,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+47.772671,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+47.815147,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+48.090656,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+48.151146,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+48.166679,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+48.476311,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+48.428230,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+48.484726,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+47.874741,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+47.766453,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+47.781193,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+48.169769,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+48.099972,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+48.182587,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+48.443371,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+48.434147,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+48.471989,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+47.749599,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+47.757484,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+47.767998,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+48.179241,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+48.082634,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+48.179920,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+48.449848,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+48.454796,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+48.424335,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+48.879322,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+48.750843,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+48.880508,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+49.171551,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+49.167877,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+49.126518,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+49.494644,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+49.525875,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+49.422489,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+48.836205,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+48.813869,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+48.848824,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+49.145844,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+49.132874,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+49.125610,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+49.513943,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+49.435379,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+49.475697,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+48.865833,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+48.772305,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+48.801556,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+49.148266,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+49.159382,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+49.138191,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+49.460857,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+49.474621,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+49.463772,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+48.789600,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+48.875031,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+48.779213,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+49.148060,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+49.166359,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+49.116650,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+49.423485,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+49.517101,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+49.421413,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+48.759041,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+48.830708,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+48.815739,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+49.193966,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+49.093334,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+49.154186,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+49.509544,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+49.463120,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+49.544727,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+48.774696,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+48.850582,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+48.843151,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+49.136684,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+49.103561,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+49.151413,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+49.437847,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+49.520004,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+49.427456,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+48.785690,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+48.879147,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+48.841290,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+49.154827,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+49.110195,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+49.084187,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+49.493042,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+49.482208,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+49.462803,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+48.821926,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+48.839291,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+48.865631,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+49.117771,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+49.212048,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+49.139675,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+49.510944,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+49.427353,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+49.533115,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+48.801064,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+48.870468,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+48.816269,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+49.202362,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+49.133575,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+49.180557,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+49.454895,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+49.538410,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+49.496853,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+48.834766,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+48.861519,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+48.788559,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+49.103798,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+49.155537,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+49.200535,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+49.424526,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+49.507221,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+49.466335,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+48.772411,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+48.841927,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+48.831036,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+49.194965,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+49.197914,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+49.143791,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+49.500168,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+49.498497,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+49.524456,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+48.850754,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+48.810028,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+48.766724,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+49.102280,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+49.113239,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+49.145382,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+49.446266,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+49.445961,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+49.497410,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+49.774937,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+49.843628,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+49.765854,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+50.126476,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+50.150047,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+50.149353,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+50.461815,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+50.463280,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+50.519951,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+49.754086,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+49.783096,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+49.780586,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+50.164032,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+50.205105,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+50.115540,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+50.489845,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+50.421532,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+50.478523,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+49.800236,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+49.873051,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+49.852711,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+50.151340,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+50.193260,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+50.205002,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+50.452755,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+50.442429,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+50.419697,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+49.872410,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+49.775311,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+49.763248,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+50.121490,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+50.175568,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+50.155907,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+50.461063,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+50.546219,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+50.501011,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+49.769844,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+49.792267,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+49.822556,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+50.174583,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+50.121899,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+50.166004,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+50.495098,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+50.450027,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+50.441513,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+49.791874,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+49.782257,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+49.861332,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+50.198505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+50.107483,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+50.097824,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+50.449535,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+50.432983,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+50.417221,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+49.844975,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+49.826965,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+49.772820,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+50.164371,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+50.168205,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+50.144211,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+50.466061,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+50.435604,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+50.472599,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+49.815708,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+49.758858,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+49.761585,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+50.204964,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+50.116116,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+50.082825,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+50.530918,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+50.418610,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+50.512566,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+49.767242,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+49.840801,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+49.818150,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+50.173576,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+50.109562,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+50.154995,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+50.442219,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+50.480946,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+50.536835,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+49.768200,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+49.784828,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+49.843498,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+50.175648,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+50.193207,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+50.179565,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+50.439335,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+50.469517,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+50.527962,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+49.874607,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+49.844994,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+49.834923,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+50.150497,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+50.213314,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+50.137070,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+50.449394,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+50.435658,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+50.495472,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+49.758633,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+49.806366,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+49.818005,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+50.171398,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+50.090405,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+50.167778,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+50.422070,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+50.516560,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+50.519188,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+50.838951,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+50.839592,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+50.776363,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+51.134842,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+51.106434,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+51.155491,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+51.533459,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+51.418091,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+51.450565,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+50.752281,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+50.761490,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+50.872406,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+51.086300,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+51.174137,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+51.099903,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+51.521896,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+51.510994,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+51.493977,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+50.857857,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+50.842373,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+50.855190,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+51.118946,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+51.087795,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+51.118011,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+51.511127,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+51.514835,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+51.467529,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+50.821358,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+50.793457,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+50.770344,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+51.159763,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+51.111111,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+51.101208,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+51.456570,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+51.495152,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+51.462238,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+50.759884,+0.500000] , 1. [-0.000000,-1.088001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+50.762352,+0.500000] , 1. [-0.000000,-1.088001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+50.800728,+0.500000] , 1. [+0.000000,-1.088001,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+51.160164,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+51.151855,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+51.146713,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+51.520569,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+51.502533,+0.500000] , 1. [+0.000000,-1.088001,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+51.440838,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+50.759541,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+50.848774,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+50.830708,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+51.182114,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+51.159531,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+51.124104,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+51.530972,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+51.515232,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+51.468868,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+50.837128,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+50.762005,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+50.810333,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+51.155457,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+51.109283,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+51.119240,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+51.450722,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+51.513042,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+51.515472,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+50.863720,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+50.864475,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+50.857548,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+51.111706,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+51.177040,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+51.114208,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+51.460545,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+51.421116,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+51.468533,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+50.864384,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+50.823902,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+50.824448,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+51.093616,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+51.194023,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+51.185421,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+51.427994,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+51.538334,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+51.532467,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+50.856411,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+50.806297,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+50.768867,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+51.174210,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+51.213314,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+51.115635,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+51.519836,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+51.486736,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+51.473560,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+50.881237,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+50.844353,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+50.798901,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+51.206886,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+51.200405,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+51.203888,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+51.461552,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+51.415592,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+51.494709,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+50.793968,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+50.828442,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+50.754799,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+51.104004,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+51.180538,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+51.142895,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+51.511242,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+51.496994,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+51.539783,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+51.814610,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+51.776428,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+51.801811,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+52.087681,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+52.098568,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+52.131123,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+52.534477,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+52.435047,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+52.502163,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+51.758633,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+51.879627,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+51.752075,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+52.144974,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+52.170826,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+52.135796,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+52.521225,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+52.456448,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+52.525928,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+51.795967,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+51.831829,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+51.856571,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+52.181999,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+52.208618,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+52.153641,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+52.420555,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+52.509453,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+52.526920,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+51.777943,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+51.820469,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+51.860317,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+52.128696,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+52.094788,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+52.172699,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+52.501202,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+52.520451,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+52.417370,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+51.788757,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+51.832420,+0.500000] , 1. [+0.000000,-1.088001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+51.835915,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+52.094208,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+52.120979,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+52.092945,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+52.532539,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+52.507507,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+52.515671,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+51.787739,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+51.769562,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+51.790150,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+52.158375,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+52.200012,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+52.123463,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+52.438000,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+52.440842,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+52.527386,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+51.873371,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+51.794964,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+51.839458,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+52.207981,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+52.189064,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+52.085697,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+52.441185,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+52.477497,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+52.500290,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+51.871494,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+51.856426,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+51.793800,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+52.161568,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+52.146713,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+52.199295,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+52.440899,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+52.504879,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+52.440487,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+51.757080,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+51.829208,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+51.815464,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+52.122890,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+52.154999,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+52.100842,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+52.426891,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+52.531105,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+52.425095,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+51.770901,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+51.829346,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+51.776966,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+52.160339,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+52.120842,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+52.149033,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+52.418373,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+52.505466,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+52.469379,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+51.787445,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+51.837051,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+51.792965,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+52.110733,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+52.214550,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+52.177902,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+52.436653,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+52.424850,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+52.535313,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+51.780914,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+51.786377,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+51.757214,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+52.140804,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+52.213497,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+52.089420,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+52.449917,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+52.523186,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+52.516739,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+52.837921,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+52.820084,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+52.757511,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+53.096680,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+53.120216,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+53.106499,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+53.451733,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+53.434669,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+53.540161,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+52.854446,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+52.768009,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+52.770157,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+53.143291,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+53.163795,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+53.201866,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+53.546093,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+53.542114,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+53.492489,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+52.783478,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+52.791443,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+52.795361,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+53.183422,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+53.103027,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+53.177864,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+53.504234,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+53.496441,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+53.420128,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+52.832531,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+52.859371,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+52.786297,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+53.169830,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+53.184441,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+53.093479,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+53.446262,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+53.438675,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+53.414833,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+52.841053,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+52.856544,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+52.865494,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+53.170170,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+53.106895,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+53.200726,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+53.414902,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+53.547295,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+53.525917,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+52.868919,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+52.860344,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+52.852066,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+53.211357,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+53.208328,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+53.178913,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+53.495190,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+53.530510,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+53.459053,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+52.754524,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+52.758213,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+52.798851,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+53.162659,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+53.108913,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+53.131626,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+53.480488,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+53.476566,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+53.419800,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+52.866459,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+52.860058,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+52.851486,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+53.208866,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+53.202991,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+53.127266,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+53.485947,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+53.511429,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+53.523933,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+52.766003,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+52.835018,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+52.817623,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+53.162861,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+53.101200,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+53.172897,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+53.485741,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+53.527534,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+53.515121,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+52.795807,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+52.788811,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+52.756580,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+53.155434,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+53.204231,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+53.213394,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+53.438198,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+53.443253,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+53.466682,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+52.866058,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+52.811279,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+52.836014,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+53.204529,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+53.202984,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+53.177673,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+53.422569,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+53.547394,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+53.433308,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+52.851284,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+52.801353,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+52.829632,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+53.172935,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+53.211742,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+53.139709,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+53.532104,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+53.535637,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+53.539528,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+53.874058,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+53.840904,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+53.835678,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+54.141247,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+54.176529,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+54.167362,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+54.498497,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+54.474461,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+54.515549,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+53.858978,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+53.805244,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+53.812180,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+54.183319,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+54.197521,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+54.104641,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+54.502586,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+54.472309,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+54.443279,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+53.791218,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+53.755222,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+53.762928,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+54.155880,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+54.125538,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+54.103615,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+54.477341,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+54.475418,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+54.523052,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+53.808704,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+53.778378,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+53.820633,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+54.176449,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+54.116505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+54.104546,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+54.462223,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+54.513336,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+54.446651,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+53.807587,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+53.751907,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+53.806564,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+54.122818,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+54.140091,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+54.163902,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+54.514992,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+54.512310,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+54.516777,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+53.827877,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+53.862495,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+53.877228,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+54.202515,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+54.095505,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+54.102837,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+54.423298,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+54.527092,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+54.468727,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+53.861927,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+53.766796,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+53.810917,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+54.081387,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+54.098534,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+54.160179,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+54.529041,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+54.520294,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+54.531273,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+53.797508,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+53.842251,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+53.817856,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+54.189705,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+54.163486,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+54.118481,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+54.440536,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+54.480080,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+54.467602,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+53.828281,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+53.766991,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+53.808071,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+54.185673,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+54.194881,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+54.103031,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+54.504204,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+54.488060,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+54.477352,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+53.858112,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+53.844524,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+53.803509,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+54.200268,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+54.140598,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+54.154621,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+54.498329,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+54.525249,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+54.483425,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+53.760738,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+53.841850,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+53.853439,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+54.102917,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+54.126938,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+54.160194,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+54.471210,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+54.431702,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+54.488285,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+53.868164,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+53.837860,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+53.810612,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+54.091881,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+54.173183,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+54.097134,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+54.513882,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+54.503578,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+54.532433,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-9.txt b/scenes/cell-growth/results/particles-frame-9.txt new file mode 100644 index 00000000..388037da --- /dev/null +++ b/scenes/cell-growth/results/particles-frame-9.txt @@ -0,0 +1,1405 @@ +1404, pdata: 4 (0,0,4) +0: [+26.122849,+40.650944,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1: [+26.453651,+40.752190,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +2: [+26.898863,+40.724522,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +3: [+26.112722,+40.980816,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +4: [+26.557117,+41.069092,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +5: [+26.849995,+40.980572,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +6: [+26.138176,+41.328152,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +7: [+26.462387,+41.416466,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +8: [+26.819349,+41.381466,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +9: [+27.153780,+40.735771,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +10: [+27.494139,+40.704586,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +11: [+27.813742,+40.754772,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +12: [+27.153406,+40.969101,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +13: [+27.490547,+41.001625,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +14: [+27.873196,+41.005360,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +15: [+27.105312,+41.420422,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +16: [+27.469255,+41.320229,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +17: [+27.878544,+41.385471,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +18: [+28.123091,+40.734802,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +19: [+28.439552,+40.697517,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +20: [+28.845465,+40.734985,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +21: [+28.226366,+40.978970,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +22: [+28.520102,+41.028599,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +23: [+28.832155,+40.970928,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +24: [+28.162525,+41.323616,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +25: [+28.546179,+41.386963,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +26: [+28.770546,+41.362446,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +27: [+29.172895,+40.725285,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +28: [+29.528704,+40.730263,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +29: [+29.772995,+40.678383,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +30: [+29.205616,+41.021103,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +31: [+29.467356,+41.077869,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +32: [+29.827061,+41.014687,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +33: [+29.137615,+41.352116,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +34: [+29.550869,+41.344715,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +35: [+29.832321,+41.315060,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +36: [+30.152824,+40.700230,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +37: [+30.549763,+40.722015,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +38: [+30.891993,+40.651440,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +39: [+30.106831,+41.019394,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +40: [+30.458609,+41.062767,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +41: [+30.861563,+41.011269,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +42: [+30.217495,+41.427994,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +43: [+30.445452,+41.359276,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +44: [+30.895409,+41.346508,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +45: [+31.183985,+40.645393,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +46: [+31.439808,+40.640106,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +47: [+31.788757,+40.708454,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +48: [+31.118870,+40.963593,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +49: [+31.541147,+41.021061,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +50: [+31.883717,+40.974277,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +51: [+31.168335,+41.354477,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +52: [+31.551752,+41.304901,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +53: [+31.825630,+41.320454,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +54: [+32.145584,+40.761032,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +55: [+32.473881,+40.681206,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +56: [+32.822876,+40.667019,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +57: [+32.152546,+41.069412,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +58: [+32.463303,+41.063339,+0.500000] , 1. [-0.000000,-1.216001,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +59: [+32.872761,+41.035297,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +60: [+32.207577,+41.403801,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +61: [+32.544460,+41.339890,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +62: [+32.773670,+41.379539,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +63: [+33.142303,+40.635635,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +64: [+33.502468,+40.721912,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +65: [+33.791725,+40.752499,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +66: [+33.225315,+40.991787,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +67: [+33.535233,+40.993145,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +68: [+33.817600,+40.971615,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +69: [+33.219772,+41.418518,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +70: [+33.561031,+41.314201,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +71: [+33.869625,+41.381771,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +72: [+34.177883,+40.684303,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +73: [+34.547924,+40.662682,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +74: [+34.869041,+40.666504,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +75: [+34.150482,+40.972492,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +76: [+34.486000,+40.962051,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +77: [+34.816849,+40.985832,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +78: [+34.230778,+41.376034,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +79: [+34.453911,+41.351112,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +80: [+34.899162,+41.365921,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +81: [+35.123138,+40.653908,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +82: [+35.436810,+40.668655,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +83: [+35.838326,+40.650890,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +84: [+35.167435,+41.089375,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +85: [+35.562302,+40.964268,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +86: [+35.860424,+41.070309,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +87: [+35.117565,+41.375362,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +88: [+35.461723,+41.329311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +89: [+35.823490,+41.345127,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +90: [+36.148548,+40.699368,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +91: [+36.494766,+40.742226,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +92: [+36.855301,+40.753922,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +93: [+36.193554,+41.058819,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +94: [+36.562984,+40.975945,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +95: [+36.771172,+41.005554,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +96: [+36.223289,+41.408821,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +97: [+36.454117,+41.394276,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +98: [+36.863091,+41.306492,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +99: [+37.204296,+40.714802,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +100: [+37.534031,+40.729874,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +101: [+37.882481,+40.699841,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +102: [+37.227539,+41.061687,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +103: [+37.459435,+41.024178,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +104: [+37.791687,+41.002491,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +105: [+37.188446,+41.373936,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +106: [+37.436653,+41.410908,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +107: [+37.839451,+41.301361,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +108: [+26.134302,+41.658924,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +109: [+26.436390,+41.726879,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +110: [+26.830048,+41.672337,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +111: [+26.187742,+42.020691,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +112: [+26.506567,+42.064110,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +113: [+26.780262,+41.991451,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +114: [+26.197567,+42.318478,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +115: [+26.462009,+42.375278,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +116: [+26.818893,+42.295593,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +117: [+27.233263,+41.708179,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +118: [+27.561996,+41.669025,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +119: [+27.868622,+41.631714,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +120: [+27.157400,+42.021225,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +121: [+27.555435,+42.027550,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +122: [+27.807611,+42.073895,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +123: [+27.169657,+42.344643,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +124: [+27.541897,+42.383514,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +125: [+27.836441,+42.353241,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +126: [+28.222355,+41.635990,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +127: [+28.493765,+41.671989,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +128: [+28.787922,+41.721725,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +129: [+28.211403,+42.079411,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +130: [+28.510069,+41.994865,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +131: [+28.872284,+41.967705,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +132: [+28.202276,+42.392902,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +133: [+28.461607,+42.322430,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +134: [+28.895288,+42.311440,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +135: [+29.148680,+41.666252,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +136: [+29.534229,+41.645508,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +137: [+29.879578,+41.645512,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +138: [+29.205069,+41.984318,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +139: [+29.506006,+42.068882,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +140: [+29.810587,+41.975998,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +141: [+29.144794,+42.353691,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +142: [+29.512489,+42.387486,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +143: [+29.769785,+42.368477,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +144: [+30.224350,+41.703445,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +145: [+30.558168,+41.640045,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +146: [+30.812428,+41.759552,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +147: [+30.218658,+42.015579,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +148: [+30.500448,+42.008812,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +149: [+30.822800,+41.992241,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +150: [+30.188595,+42.327389,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +151: [+30.463940,+42.420891,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +152: [+30.830355,+42.388435,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +153: [+31.191008,+41.650341,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +154: [+31.458574,+41.644993,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +155: [+31.826435,+41.666935,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +156: [+31.213301,+42.022602,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +157: [+31.528881,+42.090839,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +158: [+31.877710,+42.069824,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +159: [+31.125656,+42.409767,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +160: [+31.512571,+42.350147,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +161: [+31.835161,+42.301201,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +162: [+32.208939,+41.655670,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +163: [+32.559525,+41.675613,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +164: [+32.773132,+41.651855,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +165: [+32.185898,+41.985779,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +166: [+32.553947,+41.966080,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +167: [+32.817741,+42.080200,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +168: [+32.136250,+42.387417,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +169: [+32.491989,+42.395279,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +170: [+32.856728,+42.379829,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +171: [+33.105419,+41.679440,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +172: [+33.525867,+41.643253,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +173: [+33.770809,+41.638172,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +174: [+33.209290,+42.081573,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +175: [+33.443485,+42.085205,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +176: [+33.804619,+41.986061,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +177: [+33.201485,+42.389832,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +178: [+33.535725,+42.330292,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +179: [+33.852070,+42.390774,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +180: [+34.192776,+41.666985,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +181: [+34.523987,+41.734238,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +182: [+34.884480,+41.686653,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +183: [+34.171944,+41.982506,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +184: [+34.529778,+41.969410,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +185: [+34.775215,+42.093647,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +186: [+34.148880,+42.304230,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +187: [+34.561382,+42.297707,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +188: [+34.889530,+42.349865,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +189: [+35.151173,+41.655430,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +190: [+35.482433,+41.643665,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +191: [+35.866528,+41.754070,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +192: [+35.233025,+42.068214,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +193: [+35.447628,+42.030472,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +194: [+35.795452,+42.064960,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +195: [+35.213272,+42.328453,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +196: [+35.476944,+42.345509,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +197: [+35.881947,+42.363380,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +198: [+36.206589,+41.741562,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +199: [+36.497036,+41.679264,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +200: [+36.811108,+41.635273,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +201: [+36.230728,+42.026653,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +202: [+36.495499,+42.006733,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +203: [+36.835556,+42.022472,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +204: [+36.186050,+42.346954,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +205: [+36.558033,+42.405613,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +206: [+36.881329,+42.319481,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +207: [+37.150661,+41.660786,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +208: [+37.455067,+41.731644,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +209: [+37.852238,+41.691212,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +210: [+37.110943,+42.054203,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +211: [+37.457649,+42.058907,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +212: [+37.892643,+41.996208,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +213: [+37.195641,+42.360622,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +214: [+37.494659,+42.349846,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +215: [+37.835560,+42.322727,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +216: [+26.137892,+42.690819,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +217: [+26.564360,+42.650749,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +218: [+26.776350,+42.712875,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +219: [+26.198835,+43.052177,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +220: [+26.530161,+43.031082,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +221: [+26.838749,+43.086647,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +222: [+26.154537,+43.413147,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +223: [+26.507488,+43.298775,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +224: [+26.883324,+43.320244,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +225: [+27.131704,+42.680679,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +226: [+27.465704,+42.751045,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +227: [+27.882172,+42.754700,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +228: [+27.139505,+43.028191,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +229: [+27.494884,+42.971210,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +230: [+27.810974,+43.006241,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +231: [+27.145584,+43.374165,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +232: [+27.504309,+43.404045,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +233: [+27.837379,+43.323280,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +234: [+28.135340,+42.664925,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +235: [+28.466215,+42.648323,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +236: [+28.882393,+42.670944,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +237: [+28.180235,+43.080448,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +238: [+28.486662,+42.987564,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +239: [+28.808311,+42.962582,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +240: [+28.216877,+43.300369,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +241: [+28.556948,+43.306793,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +242: [+28.896992,+43.356930,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +243: [+29.107769,+42.652149,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +244: [+29.481386,+42.731358,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +245: [+29.894106,+42.731483,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +246: [+29.216625,+42.984119,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +247: [+29.482180,+43.091362,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +248: [+29.792667,+43.042709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +249: [+29.155807,+43.406979,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +250: [+29.507292,+43.315392,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +251: [+29.817974,+43.371460,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +252: [+30.176001,+42.658749,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +253: [+30.519308,+42.745804,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +254: [+30.849657,+42.716919,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +255: [+30.171932,+43.073727,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +256: [+30.478603,+43.053551,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +257: [+30.814066,+42.982155,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +258: [+30.163485,+43.358444,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +259: [+30.485065,+43.370342,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +260: [+30.776529,+43.388344,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +261: [+31.158104,+42.658043,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +262: [+31.547148,+42.697102,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +263: [+31.778715,+42.637260,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +264: [+31.108032,+43.021717,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +265: [+31.550665,+42.976051,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +266: [+31.833670,+43.017204,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +267: [+31.188221,+43.349277,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +268: [+31.524952,+43.364338,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +269: [+31.865829,+43.312984,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +270: [+32.187241,+42.711819,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +271: [+32.525917,+42.652477,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +272: [+32.896835,+42.655293,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +273: [+32.219246,+43.000088,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +274: [+32.475636,+43.015774,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +275: [+32.770649,+43.082245,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +276: [+32.155064,+43.339420,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +277: [+32.547039,+43.426449,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +278: [+32.886135,+43.406315,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +279: [+33.150379,+42.743515,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +280: [+33.438343,+42.628548,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +281: [+33.809101,+42.760242,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +282: [+33.146351,+43.075329,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +283: [+33.541290,+42.973316,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +284: [+33.788952,+43.049065,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +285: [+33.155712,+43.343155,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +286: [+33.523201,+43.338654,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +287: [+33.850475,+43.377613,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +288: [+34.220455,+42.642967,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +289: [+34.452557,+42.671124,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +290: [+34.865963,+42.694748,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +291: [+34.121689,+43.084465,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +292: [+34.444706,+43.076050,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +293: [+34.777813,+43.003307,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +294: [+34.203300,+43.386185,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +295: [+34.518120,+43.382488,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +296: [+34.888905,+43.412144,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +297: [+35.223499,+42.758209,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +298: [+35.561901,+42.673618,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +299: [+35.767326,+42.695213,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +300: [+35.139511,+43.094494,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +301: [+35.513969,+42.974346,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +302: [+35.795242,+43.041115,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +303: [+35.162331,+43.329189,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +304: [+35.462322,+43.396793,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +305: [+35.858078,+43.297199,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +306: [+36.189159,+42.694424,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +307: [+36.543751,+42.628345,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +308: [+36.877609,+42.650284,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +309: [+36.198441,+43.006393,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +310: [+36.486958,+43.078964,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +311: [+36.824402,+43.051506,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +312: [+36.127506,+43.341106,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +313: [+36.528912,+43.368477,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +314: [+36.815155,+43.349457,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +315: [+37.140553,+42.666534,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +316: [+37.543423,+42.633461,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +317: [+37.821983,+42.745754,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +318: [+37.177097,+42.970669,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +319: [+37.502895,+43.092213,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +320: [+37.848942,+43.047207,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +321: [+37.222137,+43.345734,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +322: [+37.438778,+43.341370,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +323: [+37.834827,+43.353165,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +324: [+26.227354,+43.730103,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +325: [+26.563053,+43.691147,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +326: [+26.886887,+43.694683,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +327: [+26.206791,+44.080032,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +328: [+26.448837,+44.052917,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +329: [+26.874334,+43.970146,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +330: [+26.107798,+44.395897,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +331: [+26.438719,+44.307678,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +332: [+26.898939,+44.352280,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +333: [+27.137068,+43.758736,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +334: [+27.446260,+43.740597,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +335: [+27.874607,+43.643387,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +336: [+27.126635,+44.020374,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +337: [+27.441753,+44.003067,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +338: [+27.817467,+44.069771,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +339: [+27.117468,+44.382854,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +340: [+27.445736,+44.295208,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +341: [+27.816925,+44.301506,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +342: [+28.203413,+43.669655,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +343: [+28.541676,+43.694550,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +344: [+28.798317,+43.671246,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +345: [+28.175928,+44.038719,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +346: [+28.526117,+44.034069,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +347: [+28.822929,+44.036682,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +348: [+28.202461,+44.388325,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +349: [+28.480091,+44.405025,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +350: [+28.841024,+44.344913,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +351: [+29.132620,+43.683784,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +352: [+29.514725,+43.733273,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +353: [+29.823849,+43.717606,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +354: [+29.186638,+44.027485,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +355: [+29.439491,+44.058826,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +356: [+29.890490,+44.045147,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +357: [+29.193056,+44.297176,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +358: [+29.471260,+44.346935,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +359: [+29.795563,+44.406612,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +360: [+30.112366,+43.731922,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +361: [+30.503618,+43.685734,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +362: [+30.814365,+43.754635,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +363: [+30.181637,+44.026215,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +364: [+30.560530,+44.042057,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +365: [+30.772852,+43.964664,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +366: [+30.117184,+44.305389,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +367: [+30.477829,+44.409252,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +368: [+30.809412,+44.331135,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +369: [+31.204601,+43.685795,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +370: [+31.533020,+43.664421,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +371: [+31.797686,+43.710316,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +372: [+31.116585,+44.093594,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +373: [+31.514935,+43.978088,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +374: [+31.831554,+44.006622,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +375: [+31.221670,+44.303696,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +376: [+31.469028,+44.385548,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +377: [+31.777996,+44.312420,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +378: [+32.116398,+43.667820,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +379: [+32.454342,+43.707340,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +380: [+32.878414,+43.664276,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +381: [+32.203915,+44.026321,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +382: [+32.434147,+43.966991,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +383: [+32.840946,+44.016842,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +384: [+32.200455,+44.366089,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +385: [+32.466507,+44.338341,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +386: [+32.805607,+44.353653,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +387: [+33.215591,+43.697926,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +388: [+33.448513,+43.645641,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +389: [+33.832005,+43.645679,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +390: [+33.176243,+44.061028,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +391: [+33.521526,+44.021832,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +392: [+33.819328,+44.072617,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +393: [+33.105701,+44.309711,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +394: [+33.475674,+44.417004,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +395: [+33.860840,+44.348114,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +396: [+34.212353,+43.650581,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +397: [+34.451550,+43.716370,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +398: [+34.768917,+43.727558,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +399: [+34.198761,+44.035679,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +400: [+34.499531,+43.974754,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +401: [+34.879768,+44.071892,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +402: [+34.100639,+44.423473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +403: [+34.526295,+44.395824,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +404: [+34.808968,+44.401478,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +405: [+35.128220,+43.751022,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +406: [+35.532078,+43.719330,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +407: [+35.811043,+43.711571,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +408: [+35.212860,+44.079300,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +409: [+35.522419,+44.078819,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +410: [+35.877869,+44.018288,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +411: [+35.194744,+44.339741,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +412: [+35.442913,+44.341148,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +413: [+35.866562,+44.387333,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +414: [+36.217724,+43.700478,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +415: [+36.504215,+43.749176,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +416: [+36.855545,+43.659027,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +417: [+36.229424,+43.974529,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +418: [+36.462029,+44.006481,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +419: [+36.795109,+44.022972,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +420: [+36.121078,+44.357544,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +421: [+36.552444,+44.302753,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +422: [+36.792374,+44.390598,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +423: [+37.178127,+43.737225,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +424: [+37.463825,+43.727562,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +425: [+37.829937,+43.661720,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +426: [+37.215340,+44.033276,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +427: [+37.525673,+44.080635,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +428: [+37.843407,+44.035278,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +429: [+37.203705,+44.317017,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +430: [+37.444981,+44.387245,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +431: [+37.878426,+44.310764,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +432: [+26.169304,+44.711063,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +433: [+26.494114,+44.673409,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +434: [+26.891106,+44.737473,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +435: [+26.223078,+45.010197,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +436: [+26.560616,+45.019390,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +437: [+26.813375,+44.979610,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +438: [+26.153881,+45.371063,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +439: [+26.441355,+45.364662,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +440: [+26.769859,+45.321930,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +441: [+27.131750,+44.683064,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +442: [+27.454788,+44.658524,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +443: [+27.891685,+44.745449,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +444: [+27.147400,+45.058887,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +445: [+27.542244,+45.017056,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +446: [+27.780220,+45.003971,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +447: [+27.228924,+45.370472,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +448: [+27.554148,+45.354546,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +449: [+27.801252,+45.407635,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +450: [+28.222219,+44.710354,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +451: [+28.542095,+44.693077,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +452: [+28.866283,+44.641247,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +453: [+28.138630,+45.068634,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +454: [+28.498919,+45.045036,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +455: [+28.804499,+45.092110,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +456: [+28.155701,+45.323978,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +457: [+28.501501,+45.320553,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +458: [+28.839417,+45.397400,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +459: [+29.150669,+44.650650,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +460: [+29.433784,+44.665768,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +461: [+29.805565,+44.726353,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +462: [+29.162487,+45.093006,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +463: [+29.545538,+44.983639,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +464: [+29.821646,+45.044422,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +465: [+29.125980,+45.314854,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +466: [+29.467813,+45.363384,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +467: [+29.837288,+45.342068,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +468: [+30.130318,+44.666065,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +469: [+30.554920,+44.739956,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +470: [+30.889189,+44.760567,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +471: [+30.150354,+45.058842,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +472: [+30.440491,+45.006767,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +473: [+30.790045,+45.093922,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +474: [+30.191614,+45.312202,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +475: [+30.487307,+45.300270,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +476: [+30.846930,+45.368473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +477: [+31.134449,+44.665760,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +478: [+31.536016,+44.718605,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +479: [+31.875565,+44.745079,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +480: [+31.110296,+44.997555,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +481: [+31.498850,+45.049660,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +482: [+31.796240,+45.021416,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +483: [+31.223383,+45.365616,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +484: [+31.437931,+45.368675,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +485: [+31.772993,+45.313957,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +486: [+32.186558,+44.642864,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +487: [+32.502735,+44.690525,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +488: [+32.841076,+44.699364,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +489: [+32.108414,+45.041115,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +490: [+32.551327,+44.984764,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +491: [+32.872826,+45.091717,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +492: [+32.194412,+45.315773,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +493: [+32.445648,+45.390274,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +494: [+32.873524,+45.332058,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +495: [+33.180828,+44.708366,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +496: [+33.513321,+44.759037,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +497: [+33.869335,+44.696205,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +498: [+33.219219,+44.999008,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +499: [+33.444836,+45.083965,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +500: [+33.771328,+45.005463,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +501: [+33.137901,+45.421329,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +502: [+33.504860,+45.411392,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +503: [+33.868862,+45.326263,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +504: [+34.173611,+44.643837,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +505: [+34.465107,+44.685944,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +506: [+34.859787,+44.633915,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +507: [+34.162338,+45.006657,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +508: [+34.450863,+44.986664,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +509: [+34.891315,+45.039162,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +510: [+34.199619,+45.319592,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +511: [+34.459911,+45.334126,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +512: [+34.875820,+45.404018,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +513: [+35.179668,+44.672432,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +514: [+35.473339,+44.706291,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +515: [+35.885281,+44.685726,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +516: [+35.191395,+45.034977,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +517: [+35.529182,+45.023487,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +518: [+35.796116,+44.997410,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +519: [+35.227928,+45.331497,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +520: [+35.475693,+45.374725,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +521: [+35.802204,+45.385590,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +522: [+36.153381,+44.714737,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +523: [+36.470707,+44.629227,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +524: [+36.808128,+44.655178,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +525: [+36.156731,+45.014153,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +526: [+36.562897,+45.036110,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +527: [+36.844894,+45.016964,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +528: [+36.204403,+45.374489,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +529: [+36.555809,+45.316963,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +530: [+36.770992,+45.403538,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +531: [+37.130798,+44.722801,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +532: [+37.555607,+44.726669,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +533: [+37.852448,+44.650669,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +534: [+37.160126,+44.979824,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +535: [+37.492367,+45.033611,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +536: [+37.857296,+44.961948,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +537: [+37.176144,+45.359543,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +538: [+37.479027,+45.414799,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +539: [+37.811455,+45.355473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +540: [+26.120478,+45.740479,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +541: [+26.538008,+45.647820,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +542: [+26.768793,+45.757984,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +543: [+26.120617,+45.984913,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +544: [+26.437273,+45.969242,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +545: [+26.773155,+46.002502,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +546: [+26.206295,+46.370892,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +547: [+26.556435,+46.412521,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +548: [+26.790157,+46.334511,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +549: [+27.113314,+45.655315,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +550: [+27.476250,+45.759087,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +551: [+27.832233,+45.676311,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +552: [+27.146631,+46.012535,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +553: [+27.467800,+45.962856,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +554: [+27.862038,+45.965729,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +555: [+27.120094,+46.340797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +556: [+27.565704,+46.324360,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +557: [+27.789284,+46.394627,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +558: [+28.175238,+45.697094,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +559: [+28.545595,+45.715752,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +560: [+28.790249,+45.721809,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +561: [+28.125372,+46.036575,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +562: [+28.546757,+46.048473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +563: [+28.884050,+45.982010,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +564: [+28.158592,+46.311710,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +565: [+28.525600,+46.337311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +566: [+28.868280,+46.391315,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +567: [+29.149418,+45.638603,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +568: [+29.512615,+45.702797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +569: [+29.792858,+45.680206,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +570: [+29.220648,+45.969967,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +571: [+29.447641,+46.013523,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +572: [+29.821341,+46.081894,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +573: [+29.134836,+46.306976,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +574: [+29.561060,+46.299351,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +575: [+29.792946,+46.326801,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +576: [+30.165318,+45.635647,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +577: [+30.543350,+45.648170,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +578: [+30.821724,+45.661880,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +579: [+30.116848,+46.017902,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +580: [+30.462219,+45.985382,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +581: [+30.846199,+45.971806,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +582: [+30.114794,+46.297897,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +583: [+30.442413,+46.316017,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +584: [+30.856615,+46.341476,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +585: [+31.135983,+45.680462,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +586: [+31.524622,+45.737915,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +587: [+31.832605,+45.758831,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +588: [+31.152872,+45.961800,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +589: [+31.466873,+46.053654,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +590: [+31.867289,+46.071316,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +591: [+31.100241,+46.416504,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +592: [+31.471888,+46.419106,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +593: [+31.852221,+46.421661,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +594: [+32.183708,+45.642311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +595: [+32.494385,+45.639442,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +596: [+32.784027,+45.714912,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +597: [+32.191868,+45.995495,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +598: [+32.554089,+46.012039,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +599: [+32.890656,+46.024227,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +600: [+32.200447,+46.337231,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +601: [+32.560368,+46.410591,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +602: [+32.880180,+46.361691,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +603: [+33.217876,+45.685902,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +604: [+33.547699,+45.629677,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +605: [+33.861176,+45.717293,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +606: [+33.166458,+46.016350,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +607: [+33.517853,+45.965691,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +608: [+33.806286,+46.055332,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +609: [+33.187016,+46.375668,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +610: [+33.471409,+46.298237,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +611: [+33.794468,+46.340683,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +612: [+34.195618,+45.744698,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +613: [+34.517776,+45.706844,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +614: [+34.868855,+45.704269,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +615: [+34.232933,+46.057053,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +616: [+34.483116,+45.970665,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +617: [+34.815872,+45.973690,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +618: [+34.219654,+46.332413,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +619: [+34.445362,+46.368858,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +620: [+34.806679,+46.357212,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +621: [+35.140030,+45.741093,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +622: [+35.471497,+45.749287,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +623: [+35.846958,+45.662514,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +624: [+35.131748,+45.965584,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +625: [+35.513260,+45.977322,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +626: [+35.875885,+45.992119,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +627: [+35.210693,+46.304512,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +628: [+35.548458,+46.316975,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +629: [+35.856850,+46.367237,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +630: [+36.123787,+45.750633,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +631: [+36.463593,+45.677940,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +632: [+36.819050,+45.737564,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +633: [+36.108574,+46.064930,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +634: [+36.487011,+46.082569,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +635: [+36.883583,+46.067631,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +636: [+36.212326,+46.302082,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +637: [+36.456989,+46.412289,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +638: [+36.889515,+46.403709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +639: [+37.220661,+45.740509,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +640: [+37.444389,+45.753719,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +641: [+37.846413,+45.699730,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +642: [+37.174664,+46.064445,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +643: [+37.539112,+46.057957,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +644: [+37.804855,+45.987774,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +645: [+37.188816,+46.329308,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +646: [+37.504467,+46.413639,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +647: [+37.896751,+46.396095,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +648: [+26.114902,+46.729496,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +649: [+26.520790,+46.719963,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +650: [+26.852573,+46.644505,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +651: [+26.120377,+47.083477,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +652: [+26.442600,+46.971497,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +653: [+26.815523,+47.015549,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +654: [+26.121269,+47.373779,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +655: [+26.481812,+47.406605,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +656: [+26.814674,+47.380318,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +657: [+27.109781,+46.701633,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +658: [+27.508560,+46.709454,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +659: [+27.883595,+46.714191,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +660: [+27.130463,+47.017048,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +661: [+27.465319,+47.022987,+0.500000] , 1. [-0.000000,-1.216001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +662: [+27.804302,+47.006252,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +663: [+27.231428,+47.313732,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +664: [+27.496567,+47.299797,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +665: [+27.825541,+47.350681,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +666: [+28.117174,+46.682320,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +667: [+28.511965,+46.698040,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +668: [+28.796368,+46.634773,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +669: [+28.170046,+47.002590,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +670: [+28.541039,+47.043087,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +671: [+28.894999,+47.087730,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +672: [+28.212408,+47.329979,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +673: [+28.532591,+47.313904,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +674: [+28.802336,+47.388226,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +675: [+29.154409,+46.654991,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +676: [+29.459482,+46.731888,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +677: [+29.790939,+46.744823,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +678: [+29.133907,+47.012264,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +679: [+29.522968,+47.015984,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +680: [+29.860323,+47.066608,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +681: [+29.113859,+47.383507,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +682: [+29.467556,+47.310753,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +683: [+29.792334,+47.358940,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +684: [+30.200785,+46.696411,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +685: [+30.517881,+46.631199,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +686: [+30.797268,+46.726643,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +687: [+30.151020,+47.088840,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +688: [+30.565712,+47.030464,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +689: [+30.829908,+47.088619,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +690: [+30.164457,+47.329659,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +691: [+30.521252,+47.401012,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +692: [+30.803623,+47.337872,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +693: [+31.154066,+46.724171,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +694: [+31.462618,+46.657631,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +695: [+31.785936,+46.735538,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +696: [+31.172501,+46.974686,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +697: [+31.500378,+47.056252,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +698: [+31.836432,+47.052547,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +699: [+31.194084,+47.409935,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +700: [+31.544813,+47.393333,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +701: [+31.867426,+47.338554,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +702: [+32.120182,+46.639561,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +703: [+32.519775,+46.702705,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +704: [+32.823372,+46.673203,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +705: [+32.139420,+47.091484,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +706: [+32.514462,+47.070103,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +707: [+32.771336,+47.044998,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +708: [+32.101830,+47.395985,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +709: [+32.504032,+47.399143,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +710: [+32.834190,+47.380424,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +711: [+33.130466,+46.673416,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +712: [+33.500599,+46.673904,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +713: [+33.879128,+46.699699,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +714: [+33.187775,+47.007488,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +715: [+33.536949,+46.974079,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +716: [+33.866936,+46.991570,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +717: [+33.183647,+47.389297,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +718: [+33.453434,+47.347847,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +719: [+33.847366,+47.391720,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +720: [+34.122066,+46.663635,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +721: [+34.509968,+46.725441,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +722: [+34.898540,+46.657967,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +723: [+34.131439,+47.064972,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +724: [+34.457664,+47.051094,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +725: [+34.884525,+47.084656,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +726: [+34.152725,+47.400810,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +727: [+34.436584,+47.361977,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +728: [+34.819588,+47.406311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +729: [+35.201206,+46.700272,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +730: [+35.476215,+46.652672,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +731: [+35.855953,+46.695148,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +732: [+35.192253,+46.970657,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +733: [+35.538158,+47.031147,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +734: [+35.864307,+47.046680,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +735: [+35.112125,+47.356312,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +736: [+35.562016,+47.308231,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +737: [+35.849182,+47.364727,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +738: [+36.156960,+46.754742,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +739: [+36.492546,+46.646454,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +740: [+36.849674,+46.661194,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +741: [+36.101673,+47.049770,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +742: [+36.484783,+46.979973,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +743: [+36.882294,+47.062588,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +744: [+36.184345,+47.323372,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +745: [+36.555805,+47.314148,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +746: [+36.843319,+47.351990,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +747: [+37.219425,+46.629601,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +748: [+37.495239,+46.637486,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +749: [+37.877598,+46.647999,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +750: [+37.153622,+47.059242,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +751: [+37.494354,+46.962635,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +752: [+37.899815,+47.059921,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +753: [+37.149654,+47.329849,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +754: [+37.468407,+47.334797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +755: [+37.788860,+47.304337,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +756: [+26.149149,+47.759323,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +757: [+26.460608,+47.630844,+0.500000] , 1. [+0.000000,-1.216001,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +758: [+26.858616,+47.760509,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +759: [+26.125908,+48.051552,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +760: [+26.467369,+48.047878,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +761: [+26.803995,+48.006519,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +762: [+26.134998,+48.374645,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +763: [+26.556503,+48.405876,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +764: [+26.865408,+48.302490,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +765: [+27.145180,+47.716206,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +766: [+27.485327,+47.693871,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +767: [+27.892828,+47.728825,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +768: [+27.137873,+48.025845,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +769: [+27.525503,+48.012875,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +770: [+27.804333,+48.005611,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +771: [+27.218630,+48.393944,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +772: [+27.473864,+48.315380,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +773: [+27.835186,+48.355698,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +774: [+28.105444,+47.745834,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +775: [+28.473755,+47.652306,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +776: [+28.890522,+47.681557,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +777: [+28.141596,+48.028267,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +778: [+28.460203,+48.039383,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +779: [+28.896736,+48.018192,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +780: [+28.131691,+48.340858,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +781: [+28.534880,+48.354622,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +782: [+28.886747,+48.343773,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +783: [+29.132887,+47.669601,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +784: [+29.472742,+47.755032,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +785: [+29.859537,+47.659214,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +786: [+29.194283,+48.028061,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +787: [+29.552193,+48.046360,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +788: [+29.796343,+47.996651,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +789: [+29.205294,+48.303486,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +790: [+29.492943,+48.397102,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +791: [+29.779907,+48.301414,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +792: [+30.160954,+47.639042,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +793: [+30.481503,+47.710709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +794: [+30.850628,+47.695740,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +795: [+30.106712,+48.073967,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +796: [+30.544125,+47.973335,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +797: [+30.853552,+48.034187,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +798: [+30.207396,+48.389545,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +799: [+30.546389,+48.343121,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +800: [+30.827963,+48.424728,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +801: [+31.101477,+47.654697,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +802: [+31.503988,+47.730583,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +803: [+31.771484,+47.723152,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +804: [+31.135811,+48.016685,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +805: [+31.470810,+47.983562,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +806: [+31.848373,+48.031414,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +807: [+31.141485,+48.317848,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +808: [+31.527235,+48.400005,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +809: [+31.894653,+48.307457,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +810: [+32.126587,+47.665691,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +811: [+32.521114,+47.759148,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +812: [+32.859058,+47.721291,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +813: [+32.133949,+48.034828,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +814: [+32.442291,+47.990196,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +815: [+32.892700,+47.964188,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +816: [+32.132492,+48.373043,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +817: [+32.467842,+48.362209,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +818: [+32.840862,+48.342804,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +819: [+33.201447,+47.701927,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +820: [+33.558567,+47.719292,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +821: [+33.804775,+47.745632,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +822: [+33.170921,+47.997772,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +823: [+33.543110,+48.092049,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +824: [+33.833275,+48.019676,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +825: [+33.148209,+48.390945,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +826: [+33.506756,+48.307354,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +827: [+33.801613,+48.413116,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +828: [+34.110409,+47.681065,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +829: [+34.439758,+47.750469,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +830: [+34.875191,+47.696270,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +831: [+34.206432,+48.082363,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +832: [+34.512966,+48.013577,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +833: [+34.885456,+48.060558,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +834: [+34.219738,+48.334896,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +835: [+34.435211,+48.418411,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +836: [+34.812500,+48.376854,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +837: [+35.224815,+47.714767,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +838: [+35.495049,+47.741520,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +839: [+35.896206,+47.668560,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +840: [+35.108387,+47.983799,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +841: [+35.457355,+48.035538,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +842: [+35.881550,+48.080536,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +843: [+35.206078,+48.304527,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +844: [+35.487328,+48.387222,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +845: [+35.890820,+48.346336,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +846: [+36.195946,+47.652412,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +847: [+36.475105,+47.721928,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +848: [+36.828655,+47.711037,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +849: [+36.164520,+48.074966,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +850: [+36.518147,+48.077915,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +851: [+36.776234,+48.023792,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +852: [+36.156277,+48.380169,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +853: [+36.479759,+48.378498,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +854: [+36.840729,+48.404457,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +855: [+37.196606,+47.730755,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +856: [+37.562302,+47.690029,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +857: [+37.870094,+47.646725,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +858: [+37.229519,+47.982281,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +859: [+37.526077,+47.993240,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +860: [+37.855621,+48.025383,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +861: [+37.137138,+48.326267,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +862: [+37.502850,+48.325962,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +863: [+37.832649,+48.377411,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +864: [+26.231997,+48.654938,+0.500000] , 1. [+0.000000,-1.216001,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +865: [+26.542580,+48.723629,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +866: [+26.770626,+48.645855,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +867: [+26.155806,+49.006477,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +868: [+26.529467,+49.030048,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +869: [+26.869736,+49.029354,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +870: [+26.189566,+49.341816,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +871: [+26.510792,+49.343281,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +872: [+26.830320,+49.399952,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +873: [+27.134605,+48.634087,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +874: [+27.443459,+48.663097,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +875: [+27.857122,+48.660587,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +876: [+27.124495,+49.044033,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +877: [+27.471586,+49.085106,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +878: [+27.856516,+48.995541,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +879: [+27.183117,+49.369846,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +880: [+27.474194,+49.301533,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +881: [+27.854292,+49.358524,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +882: [+28.220310,+48.680237,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +883: [+28.461962,+48.753052,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +884: [+28.775028,+48.732712,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +885: [+28.214243,+49.031342,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +886: [+28.509596,+49.073261,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +887: [+28.882565,+49.085003,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +888: [+28.156755,+49.332756,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +889: [+28.457436,+49.322430,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +890: [+28.819435,+49.299698,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +891: [+29.214371,+48.752411,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +892: [+29.446362,+48.655312,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +893: [+29.823751,+48.643250,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +894: [+29.152260,+49.001492,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +895: [+29.516272,+49.055569,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +896: [+29.823322,+49.035908,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +897: [+29.143723,+49.341064,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +898: [+29.539940,+49.426220,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +899: [+29.869474,+49.381012,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +900: [+30.197229,+48.649845,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +901: [+30.497908,+48.672268,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +902: [+30.798164,+48.702557,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +903: [+30.172680,+49.054585,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +904: [+30.519325,+49.001900,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +905: [+30.825335,+49.046005,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +906: [+30.149263,+49.375099,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +907: [+30.548489,+49.330029,+0.500000] , 1. [-0.000000,-1.216001,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +908: [+30.806307,+49.321514,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +909: [+31.164091,+48.671875,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +910: [+31.566116,+48.662258,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +911: [+31.829193,+48.741333,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +912: [+31.196243,+49.078506,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +913: [+31.566414,+48.987484,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +914: [+31.886446,+48.977825,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +915: [+31.119308,+49.329536,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +916: [+31.490938,+49.312984,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +917: [+31.767349,+49.297222,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +918: [+32.184532,+48.724976,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +919: [+32.491871,+48.706966,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +920: [+32.771656,+48.652821,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +921: [+32.218079,+49.044373,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +922: [+32.520073,+49.048206,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +923: [+32.826725,+49.024212,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +924: [+32.141068,+49.346062,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +925: [+32.459621,+49.315605,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +926: [+32.846973,+49.352600,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +927: [+33.139301,+48.695709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +928: [+33.552181,+48.638859,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +929: [+33.828751,+48.641586,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +930: [+33.162834,+49.084965,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +931: [+33.468586,+48.996117,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +932: [+33.883072,+48.962826,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +933: [+33.171394,+49.410919,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +934: [+33.446423,+49.298611,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +935: [+33.829922,+49.392567,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +936: [+34.119251,+48.647243,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +937: [+34.446831,+48.720802,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +938: [+34.860279,+48.698151,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +939: [+34.186932,+49.053577,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +940: [+34.546638,+48.989563,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +941: [+34.862690,+49.034996,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +942: [+34.169643,+49.322220,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +943: [+34.464252,+49.360947,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +944: [+34.899994,+49.416836,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +945: [+35.171719,+48.648201,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +946: [+35.458164,+48.664829,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +947: [+35.834389,+48.723499,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +948: [+35.155464,+49.055649,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +949: [+35.554844,+49.073208,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +950: [+35.814445,+49.059566,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +951: [+35.221390,+49.319336,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +952: [+35.520138,+49.349518,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +953: [+35.816730,+49.407963,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +954: [+36.226353,+48.754608,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +955: [+36.535820,+48.724995,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +956: [+36.861179,+48.714924,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +957: [+36.172020,+49.030499,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +958: [+36.507149,+49.093315,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +959: [+36.879562,+49.017071,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +960: [+36.137104,+49.329395,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +961: [+36.462936,+49.315659,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +962: [+36.834877,+49.375473,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +963: [+37.191387,+48.638634,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +964: [+37.462688,+48.686367,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +965: [+37.818886,+48.698006,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +966: [+37.142696,+49.051399,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +967: [+37.561386,+48.970406,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +968: [+37.776711,+49.047779,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +969: [+37.179211,+49.302071,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +970: [+37.524548,+49.396561,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +971: [+37.833260,+49.399189,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +972: [+26.181145,+49.718952,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +973: [+26.485340,+49.719593,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +974: [+26.885241,+49.656364,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +975: [+26.138872,+50.014843,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +976: [+26.506968,+49.986435,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +977: [+26.884155,+50.035492,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +978: [+26.132893,+50.413460,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +979: [+26.441977,+50.298092,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +980: [+26.780386,+50.330566,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +981: [+27.140551,+49.632282,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +982: [+27.502588,+49.641491,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +983: [+27.892059,+49.752407,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +984: [+27.210596,+49.966301,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +985: [+27.534935,+50.054138,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +986: [+27.859142,+49.979904,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +987: [+27.128996,+50.401897,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +988: [+27.560595,+50.390995,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +989: [+27.867004,+50.373978,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +990: [+28.162024,+49.737858,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +991: [+28.503239,+49.722374,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +992: [+28.851326,+49.735191,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +993: [+28.105694,+49.998947,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +994: [+28.499071,+49.967796,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +995: [+28.873707,+49.998013,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +996: [+28.121435,+50.391129,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +997: [+28.508137,+50.394836,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +998: [+28.854774,+50.347530,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +999: [+29.145632,+49.701359,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1000: [+29.546824,+49.673458,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1001: [+29.800119,+49.650345,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1002: [+29.194515,+50.039764,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1003: [+29.495457,+49.991112,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1004: [+29.807726,+49.981209,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1005: [+29.218763,+50.336571,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1006: [+29.455954,+50.375153,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1007: [+29.840296,+50.342239,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1008: [+30.191797,+49.639885,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1009: [+30.496613,+49.642353,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1010: [+30.767942,+49.680729,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1011: [+30.161652,+50.040165,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1012: [+30.471777,+50.031857,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1013: [+30.857004,+50.026714,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1014: [+30.215729,+50.400570,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1015: [+30.452332,+50.382534,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1016: [+30.858644,+50.320839,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1017: [+31.229849,+49.639542,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1018: [+31.451706,+49.728775,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1019: [+31.899271,+49.710709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1020: [+31.155169,+50.062115,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1021: [+31.525824,+50.039532,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1022: [+31.773676,+50.004105,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1023: [+31.203428,+50.410973,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1024: [+31.444162,+50.395233,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1025: [+31.892878,+50.348869,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1026: [+32.153301,+49.717129,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1027: [+32.482731,+49.642006,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1028: [+32.829216,+49.690334,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1029: [+32.160320,+50.035458,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1030: [+32.453968,+49.989285,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1031: [+32.877758,+49.999241,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1032: [+32.210239,+50.330723,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1033: [+32.520920,+50.393044,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1034: [+32.823597,+50.395473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1035: [+33.123859,+49.743721,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1036: [+33.492268,+49.744476,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1037: [+33.794796,+49.737549,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1038: [+33.100540,+49.991707,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1039: [+33.520931,+50.057041,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1040: [+33.855515,+49.994209,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1041: [+33.137817,+50.340546,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1042: [+33.475304,+50.301117,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1043: [+33.886486,+50.348534,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1044: [+34.164127,+49.744385,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1045: [+34.562622,+49.703903,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1046: [+34.829189,+49.704449,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1047: [+34.230247,+49.973618,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1048: [+34.450653,+50.074024,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1049: [+34.890366,+50.065422,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1050: [+34.195587,+50.307995,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1051: [+34.514618,+50.418335,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1052: [+34.898682,+50.412468,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1053: [+35.187389,+49.736412,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1054: [+35.548561,+49.686298,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1055: [+35.799995,+49.648869,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1056: [+35.135944,+50.054211,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1057: [+35.557716,+50.093315,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1058: [+35.883801,+49.995636,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1059: [+35.196846,+50.399837,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1060: [+35.446491,+50.366737,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1061: [+35.878273,+50.353561,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1062: [+36.208195,+49.761238,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1063: [+36.447163,+49.724354,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1064: [+36.804268,+49.678902,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1065: [+36.182274,+50.086887,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1066: [+36.511421,+50.080406,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1067: [+36.859463,+50.083889,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1068: [+36.166683,+50.341553,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1069: [+36.508068,+50.295593,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1070: [+36.813854,+50.374710,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1071: [+37.106918,+49.673969,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1072: [+37.452469,+49.708443,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1073: [+37.800678,+49.634800,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1074: [+37.142063,+49.984005,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1075: [+37.554890,+50.060539,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1076: [+37.847836,+50.022896,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1077: [+37.146149,+50.391243,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1078: [+37.486485,+50.376995,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1079: [+37.801922,+50.419785,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1080: [+26.145683,+50.694611,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1081: [+26.549934,+50.656429,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1082: [+26.815035,+50.681812,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1083: [+26.116388,+50.967682,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1084: [+26.514036,+50.978569,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1085: [+26.800982,+51.011124,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1086: [+26.111874,+51.414478,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1087: [+26.452007,+51.315048,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1088: [+26.896536,+51.382164,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1089: [+27.150267,+50.638634,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1090: [+27.475077,+50.759628,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1091: [+27.797777,+50.632076,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1092: [+27.132200,+51.024975,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1093: [+27.434484,+51.050827,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1094: [+27.812796,+51.015797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1095: [+27.152418,+51.401226,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1096: [+27.478634,+51.336449,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1097: [+27.815691,+51.405930,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1098: [+28.228899,+50.675968,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1099: [+28.459553,+50.711830,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1100: [+28.874266,+50.736572,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1101: [+28.226063,+51.062000,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1102: [+28.500563,+51.088619,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1103: [+28.814074,+51.033642,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1104: [+28.209883,+51.300556,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1105: [+28.522642,+51.389454,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1106: [+28.791962,+51.406921,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1107: [+29.108419,+50.657944,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1108: [+29.565859,+50.700470,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1109: [+29.771557,+50.740318,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1110: [+29.161978,+51.008698,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1111: [+29.439610,+50.974789,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1112: [+29.853176,+51.052700,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1113: [+29.222317,+51.381203,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1114: [+29.551077,+51.400452,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1115: [+29.788046,+51.297371,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1116: [+30.107138,+50.668758,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1117: [+30.512489,+50.712421,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1118: [+30.859211,+50.715916,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1119: [+30.113325,+50.974209,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1120: [+30.566620,+51.000980,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1121: [+30.864244,+50.972946,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1122: [+30.110962,+51.412540,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1123: [+30.464476,+51.387508,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1124: [+30.832165,+51.395672,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1125: [+31.134148,+50.667740,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1126: [+31.500801,+50.649563,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1127: [+31.868782,+50.670151,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1128: [+31.186663,+51.038376,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1129: [+31.563356,+51.080013,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1130: [+31.833807,+51.003464,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1131: [+31.103012,+51.318001,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1132: [+31.489643,+51.320843,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1133: [+31.847889,+51.407387,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1134: [+32.221802,+50.753372,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1135: [+32.453896,+50.674965,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1136: [+32.853199,+50.719460,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1137: [+32.128700,+51.087982,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1138: [+32.524502,+51.069065,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1139: [+32.843990,+50.965698,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1140: [+32.169987,+51.321186,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1141: [+32.550262,+51.357498,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1142: [+32.802917,+51.380291,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1143: [+33.223553,+50.751495,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1144: [+33.559479,+50.736427,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1145: [+33.827744,+50.673801,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1146: [+33.146255,+51.041569,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1147: [+33.443310,+51.026714,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1148: [+33.777138,+51.079296,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1149: [+33.204704,+51.320900,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1150: [+33.442631,+51.384880,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1151: [+33.824780,+51.320488,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1152: [+34.185558,+50.637081,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1153: [+34.522926,+50.709209,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1154: [+34.777260,+50.695465,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1155: [+34.111656,+51.002892,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1156: [+34.482647,+51.035000,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1157: [+34.768551,+50.980843,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1158: [+34.150738,+51.306892,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1159: [+34.539558,+51.411106,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1160: [+34.895054,+51.305096,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1161: [+35.197632,+50.650902,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1162: [+35.494675,+50.709347,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1163: [+35.893951,+50.656967,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1164: [+35.168327,+51.040340,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1165: [+35.559547,+51.000843,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1166: [+35.789150,+51.029034,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1167: [+35.184204,+51.298374,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1168: [+35.553635,+51.385468,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1169: [+35.819077,+51.349380,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1170: [+36.115459,+50.667446,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1171: [+36.453609,+50.717052,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1172: [+36.816837,+50.672966,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1173: [+36.152802,+50.990734,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1174: [+36.507339,+51.094551,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1175: [+36.834019,+51.057903,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1176: [+36.105309,+51.316654,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1177: [+36.553280,+51.304852,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1178: [+36.785191,+51.415314,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1179: [+37.189362,+50.660915,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1180: [+37.544514,+50.666378,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1181: [+37.815769,+50.637215,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1182: [+37.200932,+51.020805,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1183: [+37.495907,+51.093498,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1184: [+37.887375,+50.969421,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1185: [+37.167770,+51.329918,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1186: [+37.483566,+51.403187,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1187: [+37.850609,+51.396740,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1188: [+26.174967,+51.717922,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1189: [+26.532068,+51.700085,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1190: [+26.836506,+51.637512,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1191: [+26.128227,+51.976681,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1192: [+26.546846,+52.000217,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1193: [+26.815516,+51.986500,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1194: [+26.159708,+52.331734,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1195: [+26.534210,+52.314671,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1196: [+26.799082,+52.420162,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1197: [+27.221546,+51.734447,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1198: [+27.454885,+51.648010,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1199: [+27.783737,+51.650158,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1200: [+27.169382,+52.023293,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1201: [+27.461916,+52.043797,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1202: [+27.782387,+52.081867,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1203: [+27.122646,+52.426094,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1204: [+27.553684,+52.422115,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1205: [+27.825413,+52.372490,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1206: [+28.195730,+51.663479,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1207: [+28.455826,+51.671444,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1208: [+28.770428,+51.675362,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1209: [+28.222630,+52.063423,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1210: [+28.483170,+51.983028,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1211: [+28.800524,+52.057865,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1212: [+28.140997,+52.384235,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1213: [+28.500694,+52.376442,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1214: [+28.808376,+52.300129,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1215: [+29.146938,+51.712532,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1216: [+29.555840,+51.739372,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1217: [+29.785217,+51.666298,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1218: [+29.156393,+52.049831,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1219: [+29.489552,+52.064442,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1220: [+29.831295,+51.973480,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1221: [+29.192970,+52.326263,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1222: [+29.456808,+52.318676,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1223: [+29.899529,+52.294834,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1224: [+30.215548,+51.721054,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1225: [+30.526377,+51.736546,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1226: [+30.895494,+51.745495,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1227: [+30.109131,+52.050171,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1228: [+30.466684,+51.986897,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1229: [+30.863811,+52.080727,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1230: [+30.148386,+52.294903,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1231: [+30.467072,+52.427296,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1232: [+30.813093,+52.405918,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1233: [+31.205460,+51.748920,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1234: [+31.528139,+51.740345,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1235: [+31.778778,+51.732067,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1236: [+31.131807,+52.091358,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1237: [+31.494091,+52.088329,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1238: [+31.896719,+52.058914,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1239: [+31.116276,+52.375191,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1240: [+31.483829,+52.410511,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1241: [+31.848871,+52.339054,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1242: [+32.220638,+51.634525,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1243: [+32.565769,+51.638214,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1244: [+32.793968,+51.678852,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1245: [+32.160706,+52.042660,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1246: [+32.454163,+51.988914,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1247: [+32.848091,+52.011627,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1248: [+32.166317,+52.360489,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1249: [+32.514416,+52.356567,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1250: [+32.829662,+52.299801,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1251: [+33.230198,+51.746460,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1252: [+33.553432,+51.740059,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1253: [+33.867157,+51.731487,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1254: [+33.135555,+52.088867,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1255: [+33.543674,+52.082993,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1256: [+33.852711,+52.007267,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1257: [+33.203182,+52.365948,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1258: [+33.476608,+52.391430,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1259: [+33.856209,+52.403934,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1260: [+34.177761,+51.646004,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1261: [+34.454643,+51.715019,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1262: [+34.869801,+51.697624,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1263: [+34.129253,+52.042862,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1264: [+34.491459,+51.981201,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1265: [+34.869259,+52.052898,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1266: [+34.170040,+52.365742,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1267: [+34.458027,+52.407536,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1268: [+34.869686,+52.395123,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1269: [+35.130779,+51.675808,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1270: [+35.487305,+51.668812,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1271: [+35.873878,+51.636581,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1272: [+35.113514,+52.035435,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1273: [+35.562611,+52.084232,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1274: [+35.863914,+52.093395,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1275: [+35.113007,+52.318199,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1276: [+35.522667,+52.323254,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1277: [+35.891228,+52.346684,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1278: [+36.101475,+51.746059,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1279: [+36.526443,+51.691280,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1280: [+36.780518,+51.716015,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1281: [+36.146862,+52.084530,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1282: [+36.447590,+52.082985,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1283: [+36.863811,+52.057674,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1284: [+36.137531,+52.302570,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1285: [+36.528572,+52.427395,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1286: [+36.792183,+52.313309,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1287: [+37.143024,+51.731285,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1288: [+37.493160,+51.681355,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1289: [+37.821247,+51.709633,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1290: [+37.109638,+52.052937,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1291: [+37.548199,+52.091743,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1292: [+37.896259,+52.019711,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1293: [+37.221291,+52.412106,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1294: [+37.534088,+52.415638,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1295: [+37.836216,+52.419529,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1296: [+26.228083,+52.754059,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1297: [+26.467760,+52.720905,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1298: [+26.895361,+52.715679,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1299: [+26.133934,+53.021248,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1300: [+26.447676,+53.056530,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1301: [+26.874157,+53.047363,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1302: [+26.111940,+53.378498,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1303: [+26.531075,+53.354462,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1304: [+26.825447,+53.395550,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1305: [+27.186844,+52.738979,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1306: [+27.509798,+52.685246,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1307: [+27.771620,+52.692181,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1308: [+27.209784,+53.063320,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1309: [+27.555914,+53.077522,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1310: [+27.842365,+52.984642,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1311: [+27.144770,+53.382587,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1312: [+27.521910,+53.352310,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1313: [+27.870264,+53.323280,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1314: [+28.120066,+52.671219,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1315: [+28.461159,+52.635223,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1316: [+28.783125,+52.642929,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1317: [+28.184456,+53.035881,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1318: [+28.520199,+53.005539,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1319: [+28.799465,+52.983616,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1320: [+28.222631,+53.357342,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1321: [+28.525751,+53.355419,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1322: [+28.816057,+53.403053,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1323: [+29.207741,+52.688705,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1324: [+29.553543,+52.658379,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1325: [+29.860205,+52.700634,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1326: [+29.171360,+53.056450,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1327: [+29.507917,+52.996506,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1328: [+29.813246,+52.984547,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1329: [+29.118004,+53.342224,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1330: [+29.503080,+53.393337,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1331: [+29.897375,+53.326653,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1332: [+30.228010,+52.687588,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1333: [+30.560743,+52.631908,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1334: [+30.855007,+52.686565,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1335: [+30.106350,+53.002819,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1336: [+30.445833,+53.020092,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1337: [+30.823641,+53.043903,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1338: [+30.152920,+53.394993,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1339: [+30.493826,+53.392311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1340: [+30.886061,+53.396778,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1341: [+31.106194,+52.707878,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1342: [+31.546730,+52.742496,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1343: [+31.781065,+52.757229,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1344: [+31.151148,+53.082516,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1345: [+31.546522,+52.975506,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1346: [+31.796497,+52.982838,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1347: [+31.164881,+53.303299,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1348: [+31.458332,+53.407093,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1349: [+31.768354,+53.348728,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1350: [+32.229572,+52.741928,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1351: [+32.509586,+52.646797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1352: [+32.771206,+52.690918,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1353: [+32.102791,+52.961388,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1354: [+32.525040,+52.978535,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1355: [+32.855042,+53.040180,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1356: [+32.132862,+53.409042,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1357: [+32.547379,+53.400295,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1358: [+32.814655,+53.411274,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1359: [+33.107964,+52.677509,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1360: [+33.549393,+52.722252,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1361: [+33.876259,+52.697857,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1362: [+33.140415,+53.069706,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1363: [+33.487480,+53.043488,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1364: [+33.823132,+52.998482,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1365: [+33.153606,+53.320538,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1366: [+33.555695,+53.360081,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1367: [+33.886368,+53.347603,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1368: [+34.167728,+52.708282,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1369: [+34.507839,+52.646992,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1370: [+34.799141,+52.688072,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1371: [+34.207287,+53.065674,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1372: [+34.440655,+53.074883,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1373: [+34.880047,+52.983032,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1374: [+34.200047,+53.384205,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1375: [+34.479080,+53.368061,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1376: [+34.784893,+53.357353,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1377: [+35.163120,+52.738113,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1378: [+35.491985,+52.724525,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1379: [+35.817135,+52.683510,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1380: [+35.180683,+53.080269,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1381: [+35.544750,+53.020599,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1382: [+35.782356,+53.034622,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1383: [+35.175343,+53.378330,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1384: [+35.450748,+53.405251,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1385: [+35.802040,+53.363426,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1386: [+36.128307,+52.640739,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1387: [+36.551025,+52.721851,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1388: [+36.895729,+52.733440,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1389: [+36.115437,+52.982918,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1390: [+36.439560,+53.006939,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1391: [+36.811745,+53.040195,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1392: [+36.152557,+53.351212,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1393: [+36.459557,+53.311703,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1394: [+36.861771,+53.368286,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1395: [+37.141144,+52.748165,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1396: [+37.544632,+52.717861,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1397: [+37.810879,+52.690613,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1398: [+37.144939,+52.971882,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1399: [+37.534695,+53.053185,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1400: [+37.884216,+52.977135,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1401: [+37.140438,+53.393883,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1402: [+37.530254,+53.383579,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] +1403: [+37.851040,+53.412434,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/view-particles.ipynb b/scenes/cell-growth/view-particles.ipynb new file mode 100644 index 00000000..05ea3695 --- /dev/null +++ b/scenes/cell-growth/view-particles.ipynb @@ -0,0 +1,111 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import k3d\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": " position\n \n0 [+26.122849,+46.122944,+0.500000] , 1. [+0.00...\n1 [+26.453651,+46.224190,+0.500000] , 1. [+0.00...\n2 [+26.898863,+46.196522,+0.500000] , 1. [+0.00...\n3 [+26.112722,+46.452816,+0.500000] , 1. [+0.00...\n4 [+26.557117,+46.541092,+0.500000] , 1. [+0.00...\n... ...\n1399 [+37.534695,+58.525185,+0.500000] , 1. [+0.00...\n1400 [+37.884216,+58.449135,+0.500000] , 1. [+0.00...\n1401 [+37.140438,+58.865883,+0.500000] , 1. [+0.00...\n1402 [+37.530254,+58.855579,+0.500000] , 1. [+0.00...\n1403 [+37.851040,+58.884434,+0.500000] , 1. [+0.00...\n\n[1404 rows x 1 columns]", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
position
0[+26.122849,+46.122944,+0.500000] , 1. [+0.00...
1[+26.453651,+46.224190,+0.500000] , 1. [+0.00...
2[+26.898863,+46.196522,+0.500000] , 1. [+0.00...
3[+26.112722,+46.452816,+0.500000] , 1. [+0.00...
4[+26.557117,+46.541092,+0.500000] , 1. [+0.00...
......
1399[+37.534695,+58.525185,+0.500000] , 1. [+0.00...
1400[+37.884216,+58.449135,+0.500000] , 1. [+0.00...
1401[+37.140438,+58.865883,+0.500000] , 1. [+0.00...
1402[+37.530254,+58.855579,+0.500000] , 1. [+0.00...
1403[+37.851040,+58.884434,+0.500000] , 1. [+0.00...
\n

1404 rows × 1 columns

\n
" + }, + "metadata": {}, + "execution_count": 27 + } + ], + "source": [ + "df = pd.read_table('./results/particles-frame-0.txt', skiprows=1, usecols=[0,1], names=['','position'], sep=':', index_col=0)\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "Plot(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camera=[2, -3, 0.2, 0.0, 0…", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "bbed7b1ed2124cf29489bae49f656fde" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": "Output()", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "984bfc3ac728407e8362a7ae19e20f18" + } + }, + "metadata": {} + } + ], + "source": [ + "Nx, Ny = 34, 33\n", + "xmin, xmax = -3, 4\n", + "ymin, ymax = -0, 3\n", + "\n", + "x = np.linspace(xmin, xmax, Nx)\n", + "y = np.linspace(ymin, ymax, Ny)\n", + "x, y = np.meshgrid(x, y)\n", + "f = np.sin(x**2 + y**2)\n", + "\n", + "plot = k3d.plot()\n", + "plt_surface = k3d.surface(f.astype(np.float32), bounds=[xmin,xmax,ymin,ymax])\n", + "plot += plt_surface\n", + "plot.display()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6-final" + }, + "orig_nbformat": 2, + "kernelspec": { + "name": "python37664bitanaconda3condab615bb15fcd4464bb2000e7d9e446dfd", + "display_name": "Python 3.7.6 64-bit ('anaconda3': conda)" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file From 7c686db076e0e2a994bea9b52707cc8ab8b0412e Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Wed, 1 Jul 2020 23:33:19 -0400 Subject: [PATCH 03/15] In the scene "view_particles_in_notebook.py" there is code for converting mantaflow .txt files to numpy text files. The code is commented out and will be deleted in the next commit so I wanted to keep a copy of the code in git in case I needed it in the future. --- .gitignore | 2 + scenes/cell-growth/points.ipynb | 140 -- .../cell-growth/results/particles-frame-0.txt | 1405 ----------------- .../cell-growth/results/particles-frame-1.txt | 1405 ----------------- .../results/particles-frame-10.txt | 1405 ----------------- .../cell-growth/results/particles-frame-2.txt | 1405 ----------------- .../cell-growth/results/particles-frame-3.txt | 1405 ----------------- .../cell-growth/results/particles-frame-4.txt | 1405 ----------------- .../cell-growth/results/particles-frame-5.txt | 1405 ----------------- .../cell-growth/results/particles-frame-6.txt | 1405 ----------------- .../cell-growth/results/particles-frame-7.txt | 1405 ----------------- .../cell-growth/results/particles-frame-8.txt | 1405 ----------------- .../cell-growth/results/particles-frame-9.txt | 1405 ----------------- scenes/cell-growth/view-particles.ipynb | 111 -- scenes/view_particles_in_notebook.ipynb | 132 ++ ...icles.py => view_particles_in_notebook.py} | 32 +- source/particle.cpp | 18 + source/particle.h | 4 + 18 files changed, 184 insertions(+), 15710 deletions(-) delete mode 100644 scenes/cell-growth/points.ipynb delete mode 100644 scenes/cell-growth/results/particles-frame-0.txt delete mode 100644 scenes/cell-growth/results/particles-frame-1.txt delete mode 100644 scenes/cell-growth/results/particles-frame-10.txt delete mode 100644 scenes/cell-growth/results/particles-frame-2.txt delete mode 100644 scenes/cell-growth/results/particles-frame-3.txt delete mode 100644 scenes/cell-growth/results/particles-frame-4.txt delete mode 100644 scenes/cell-growth/results/particles-frame-5.txt delete mode 100644 scenes/cell-growth/results/particles-frame-6.txt delete mode 100644 scenes/cell-growth/results/particles-frame-7.txt delete mode 100644 scenes/cell-growth/results/particles-frame-8.txt delete mode 100644 scenes/cell-growth/results/particles-frame-9.txt delete mode 100644 scenes/cell-growth/view-particles.ipynb create mode 100644 scenes/view_particles_in_notebook.ipynb rename scenes/{cell-growth/export-particles.py => view_particles_in_notebook.py} (73%) diff --git a/.gitignore b/.gitignore index ca6fa304..727f6f1d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ waveletNoiseTileDouble.bin build doc tensorflow/mantaGen/datasets +scenes/results +scenes/frames # macos .DS_Store diff --git a/scenes/cell-growth/points.ipynb b/scenes/cell-growth/points.ipynb deleted file mode 100644 index 190faeca..00000000 --- a/scenes/cell-growth/points.ipynb +++ /dev/null @@ -1,140 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": "Plot(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camera=[2, -3, 0.2, 0.0, 0…", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "22fb85219bd643b395894b32d40222ec" - } - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { - "text/plain": "Output()", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "d274bcf3a0c948ad8a8fd935edcfa6cc" - } - }, - "metadata": {} - } - ], - "source": [ - "import k3d\n", - "import numpy as np\n", - "\n", - "points_number = 500\n", - "positions = 50 * np.random.random_sample((points_number,3)) - 25\n", - "colors = np.random.randint(0, 0xFFFFFF, points_number)\n", - "\n", - "plot = k3d.plot()\n", - "points = k3d.points(positions.astype(np.float32), colors.astype(np.uint32), point_size=3.0, shader='flat')\n", - "plot += points\n", - "plot.display()" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "plot.camera_auto_fit = False" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "points.shader = '3d'" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "points.shader = '3dSpecular'" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "points.shader = 'mesh'" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "points.mesh_detail = 5" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "tmp = points.colors\n", - "points.colors = []" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "points.colors = tmp" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.7.6 64-bit ('anaconda3': conda)", - "language": "python", - "name": "python37664bitanaconda3condab615bb15fcd4464bb2000e7d9e446dfd" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6-final" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} \ No newline at end of file diff --git a/scenes/cell-growth/results/particles-frame-0.txt b/scenes/cell-growth/results/particles-frame-0.txt deleted file mode 100644 index 996fde26..00000000 --- a/scenes/cell-growth/results/particles-frame-0.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+46.122944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+46.224190,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+46.196522,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+46.452816,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+46.541092,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+46.452572,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+46.800152,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+46.888466,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+46.853466,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+46.207771,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+46.176586,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+46.226772,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+46.441101,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+46.473625,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+46.477360,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+46.892422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+46.792229,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+46.857471,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+46.206802,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+46.169518,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+46.206985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+46.450970,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+46.500599,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+46.442928,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+46.795616,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+46.858963,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+46.834446,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+46.197285,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+46.202263,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+46.150383,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+46.493103,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+46.549870,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+46.486687,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+46.824116,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+46.816715,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+46.787060,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+46.172230,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+46.194016,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+46.123440,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+46.491394,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+46.534767,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+46.483269,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+46.899994,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+46.831276,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+46.818508,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+46.117393,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+46.112106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+46.180454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+46.435593,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+46.493061,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+46.446278,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+46.826477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+46.776901,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+46.792454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+46.233032,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+46.153206,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+46.139019,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+46.541412,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+46.535339,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+46.507298,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+46.875801,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+46.811890,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+46.851540,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+46.107635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+46.193913,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+46.224499,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+46.463787,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+46.465145,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+46.443615,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+46.890518,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+46.786201,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+46.853771,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+46.156303,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+46.134682,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+46.138504,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+46.444492,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+46.434052,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+46.457832,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+46.848034,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+46.823112,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+46.837921,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+46.125908,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+46.140656,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+46.122890,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+46.561375,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+46.436268,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+46.542309,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+46.847363,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+46.801311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+46.817127,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+46.171368,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+46.214226,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+46.225922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+46.530819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+46.447945,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+46.477554,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+46.880821,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+46.866276,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+46.778492,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+46.186802,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+46.201874,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+46.171841,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+46.533688,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+46.496178,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+46.474491,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+46.845936,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+46.882908,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+46.773361,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+47.130924,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+47.198879,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+47.144337,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+47.492691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+47.536110,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+47.463451,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+47.790478,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+47.847279,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+47.767593,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+47.180180,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+47.141026,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+47.103714,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+47.493225,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+47.499550,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+47.545895,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+47.816639,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+47.855515,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+47.825241,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+47.107990,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+47.143990,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+47.193726,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+47.551411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+47.466866,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+47.439705,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+47.864902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+47.794430,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+47.783440,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+47.138252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+47.117508,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+47.117512,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+47.456318,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+47.540882,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+47.447998,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+47.825691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+47.859486,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+47.840477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+47.175446,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+47.112045,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+47.231552,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+47.487579,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+47.480812,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+47.464241,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+47.799389,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+47.892891,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+47.860435,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+47.122341,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+47.116993,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+47.138935,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+47.494602,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+47.562840,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+47.541824,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+47.881767,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+47.822147,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+47.773201,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+47.127670,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+47.147614,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+47.123856,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+47.457779,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+47.438080,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+47.552200,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+47.859417,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+47.867279,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+47.851830,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+47.151440,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+47.115253,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+47.110172,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+47.553574,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+47.557205,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+47.458061,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+47.861832,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+47.802292,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+47.862774,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+47.138985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+47.206238,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+47.158653,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+47.454506,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+47.441410,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+47.565647,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+47.776230,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+47.769707,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+47.821865,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+47.127430,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+47.115665,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+47.226070,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+47.540215,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+47.502472,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+47.536961,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+47.800453,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+47.817509,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+47.835381,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+47.213562,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+47.151264,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+47.107273,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+47.498653,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+47.478733,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+47.494473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+47.818954,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+47.877613,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+47.791481,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+47.132786,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+47.203644,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+47.163212,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+47.526203,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+47.530907,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+47.468208,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+47.832623,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+47.821846,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+47.794727,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+48.162819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+48.122746,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+48.184875,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+48.524178,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+48.503078,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+48.558643,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+48.885143,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+48.770775,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+48.792240,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+48.152676,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+48.223042,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+48.226700,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+48.500191,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+48.443211,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+48.478241,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+48.846165,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+48.876045,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+48.795280,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+48.136925,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+48.120323,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+48.142944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+48.552448,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+48.459564,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+48.434582,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+48.772369,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+48.778793,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+48.828930,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+48.124149,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+48.203358,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+48.203484,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+48.456120,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+48.563362,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+48.514709,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+48.878979,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+48.787392,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+48.843460,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+48.130749,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+48.217804,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+48.188919,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+48.545727,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+48.525551,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+48.454155,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+48.830444,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+48.842342,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+48.860344,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+48.130043,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+48.169102,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+48.109261,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+48.493717,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+48.448051,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+48.489204,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+48.821278,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+48.836338,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+48.784985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+48.183819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+48.124477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+48.127293,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+48.472088,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+48.487774,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+48.554245,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+48.811420,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+48.898449,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+48.878315,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+48.215515,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+48.100548,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+48.232243,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+48.547329,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+48.445316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+48.521065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+48.815155,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+48.810654,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+48.849613,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+48.114967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+48.143124,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+48.166748,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+48.556465,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+48.548050,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+48.475307,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+48.858185,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+48.854488,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+48.884144,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+48.230209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+48.145618,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+48.167213,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+48.566494,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+48.446346,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+48.513115,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+48.801189,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+48.868793,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+48.769199,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+48.166424,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+48.100346,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+48.122284,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+48.478394,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+48.550964,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+48.523506,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+48.813107,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+48.840477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+48.821457,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+48.138535,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+48.105461,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+48.217754,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+48.442669,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+48.564213,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+48.519207,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+48.817734,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+48.813370,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+48.825165,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+49.202103,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+49.163143,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+49.166683,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+49.552029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+49.524918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+49.442142,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+49.867897,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+49.779678,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+49.824280,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+49.230736,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+49.212597,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+49.115387,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+49.492374,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+49.475067,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+49.541771,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+49.854855,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+49.767208,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+49.773506,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+49.141655,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+49.166550,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+49.143246,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+49.510719,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+49.506069,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+49.508682,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+49.860325,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+49.877026,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+49.816914,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+49.155785,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+49.205273,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+49.189606,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+49.499485,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+49.530827,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+49.517147,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+49.769176,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+49.818939,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+49.878613,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+49.203922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+49.157734,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+49.226635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+49.498215,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+49.514057,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+49.436665,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+49.777390,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+49.881252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+49.803135,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+49.157795,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+49.136421,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+49.182316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+49.565594,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+49.450089,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+49.478622,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+49.775696,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+49.857548,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+49.784420,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+49.139820,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+49.179340,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+49.136276,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+49.498322,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+49.438992,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+49.488842,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+49.838089,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+49.810341,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+49.825653,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+49.169926,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+49.117641,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+49.117680,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+49.533028,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+49.493832,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+49.544617,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+49.781712,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+49.889004,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+49.820114,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+49.122581,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+49.188370,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+49.199558,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+49.507679,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+49.446754,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+49.543892,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+49.895473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+49.867825,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+49.873478,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+49.223022,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+49.191330,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+49.183571,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+49.551300,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+49.550819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+49.490288,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+49.811741,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+49.813148,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+49.859333,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+49.172478,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+49.221176,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+49.131027,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+49.446529,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+49.478481,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+49.494972,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+49.829544,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+49.774754,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+49.862598,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+49.209225,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+49.199562,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+49.133720,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+49.505276,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+49.552635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+49.507278,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+49.789017,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+49.859245,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+49.782764,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+50.183064,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+50.145409,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+50.209473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+50.482197,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+50.491390,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+50.451611,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+50.843063,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+50.836662,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+50.793930,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+50.155064,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+50.130524,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+50.217449,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+50.530888,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+50.489056,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+50.475971,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+50.842472,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+50.826546,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+50.879635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+50.182354,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+50.165077,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+50.113247,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+50.540634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+50.517036,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+50.564110,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+50.795979,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+50.792553,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+50.869400,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+50.122650,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+50.137768,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+50.198353,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+50.565006,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+50.455639,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+50.516422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+50.786854,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+50.835384,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+50.814068,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+50.138065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+50.211956,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+50.232567,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+50.530842,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+50.478767,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+50.565922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+50.784203,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+50.772270,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+50.840473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+50.137760,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+50.190605,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+50.217079,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+50.469555,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+50.521660,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+50.493416,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+50.837616,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+50.840675,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+50.785957,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+50.114864,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+50.162525,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+50.171364,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+50.513115,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+50.456764,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+50.563717,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+50.787773,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+50.862270,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+50.804058,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+50.180367,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+50.231037,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+50.168205,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+50.471008,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+50.555965,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+50.477463,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+50.893330,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+50.883392,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+50.798264,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+50.115837,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+50.157944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+50.105915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+50.478657,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+50.458664,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+50.511162,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+50.791592,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+50.806126,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+50.876019,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+50.144432,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+50.178291,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+50.157726,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+50.506977,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+50.495487,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+50.469410,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+50.803497,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+50.846725,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+50.857590,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+50.186737,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+50.101227,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+50.127178,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+50.486153,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+50.508110,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+50.488964,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+50.846489,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+50.788963,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+50.875538,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+50.194801,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+50.198669,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+50.122669,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+50.451824,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+50.505611,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+50.433949,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+50.831543,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+50.886799,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+50.827473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+51.212479,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+51.119820,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+51.229984,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+51.456913,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+51.441242,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+51.474503,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+51.842892,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+51.884521,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+51.806511,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+51.127316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+51.231087,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+51.148312,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+51.484535,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+51.434856,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+51.437729,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+51.812798,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+51.796360,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+51.866627,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+51.169094,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+51.187752,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+51.193810,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+51.508575,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+51.520473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+51.454010,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+51.783710,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+51.809311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+51.863316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+51.110603,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+51.174797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+51.152206,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+51.441967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+51.485523,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+51.553894,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+51.778976,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+51.771351,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+51.798801,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+51.107647,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+51.120171,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+51.133881,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+51.489902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+51.457382,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+51.443806,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+51.769897,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+51.788017,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+51.813477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+51.152462,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+51.209915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+51.230831,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+51.433800,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+51.525654,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+51.543316,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+51.888504,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+51.891106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+51.893661,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+51.114311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+51.111439,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+51.186913,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+51.467495,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+51.484039,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+51.496227,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+51.809231,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+51.882591,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+51.833691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+51.157902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+51.101677,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+51.189293,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+51.488350,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+51.437691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+51.527332,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+51.847668,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+51.770237,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+51.812683,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+51.216698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+51.178844,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+51.176270,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+51.529053,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+51.442665,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+51.445690,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+51.804413,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+51.840858,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+51.829212,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+51.213093,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+51.221287,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+51.134514,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+51.437584,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+51.449322,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+51.464119,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+51.776512,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+51.788975,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+51.839237,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+51.222633,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+51.149940,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+51.209564,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+51.536930,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+51.554569,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+51.539631,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+51.774082,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+51.884289,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+51.875710,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+51.212509,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+51.225719,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+51.171730,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+51.536446,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+51.529957,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+51.459774,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+51.801308,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+51.885639,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+51.868095,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+52.201496,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+52.191963,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+52.116505,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+52.555477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+52.443497,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+52.487549,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+52.845779,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+52.878605,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+52.852318,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+52.173634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+52.181454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+52.186192,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+52.489048,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+52.494987,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+52.478252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+52.785732,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+52.771797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+52.822681,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+52.154320,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+52.170040,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+52.106773,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+52.474590,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+52.515087,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+52.559731,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+52.801979,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+52.785904,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+52.860226,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+52.126991,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+52.203888,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+52.216824,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+52.484264,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+52.487984,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+52.538609,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+52.855507,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+52.782753,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+52.830940,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+52.168411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+52.103199,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+52.198643,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+52.560841,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+52.502464,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+52.560619,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+52.801659,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+52.873013,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+52.809872,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+52.196171,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+52.129631,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+52.207539,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+52.446686,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+52.528252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+52.524548,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+52.881935,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+52.865334,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+52.810555,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+52.111561,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+52.174706,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+52.145203,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+52.563484,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+52.542103,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+52.516998,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+52.867985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+52.871143,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+52.852425,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+52.145416,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+52.145905,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+52.171700,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+52.479488,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+52.446079,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+52.463570,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+52.861298,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+52.819847,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+52.863720,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+52.135635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+52.197441,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+52.129967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+52.536972,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+52.523094,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+52.556656,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+52.872810,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+52.833977,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+52.878311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+52.172272,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+52.124672,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+52.167149,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+52.442657,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+52.503147,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+52.518681,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+52.828312,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+52.780231,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+52.836727,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+52.226742,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+52.118454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+52.133194,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+52.521770,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+52.451973,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+52.534588,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+52.795372,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+52.786148,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+52.823990,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+52.101601,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+52.109486,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+52.119999,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+52.531242,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+52.434635,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+52.531921,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+52.801849,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+52.806797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+52.776337,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+53.231323,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+53.102844,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+53.232510,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+53.523552,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+53.519878,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+53.478519,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+53.846645,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+53.877876,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+53.774490,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+53.188206,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+53.165871,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+53.200825,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+53.497845,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+53.484875,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+53.477612,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+53.865944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+53.787380,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+53.827698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+53.217834,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+53.124306,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+53.153557,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+53.500267,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+53.511383,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+53.490192,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+53.812859,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+53.826622,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+53.815773,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+53.141602,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+53.227032,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+53.131214,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+53.500061,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+53.518360,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+53.468651,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+53.775486,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+53.869102,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+53.773415,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+53.111042,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+53.182709,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+53.167740,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+53.545967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+53.445335,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+53.506187,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+53.861546,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+53.815121,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+53.896729,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+53.126698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+53.202583,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+53.195152,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+53.488686,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+53.455563,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+53.503414,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+53.789848,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+53.872005,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+53.779457,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+53.137691,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+53.231148,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+53.193291,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+53.506828,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+53.462196,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+53.436188,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+53.845043,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+53.834209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+53.814804,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+53.173927,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+53.191292,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+53.217632,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+53.469772,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+53.564049,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+53.491676,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+53.862946,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+53.779354,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+53.885117,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+53.153065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+53.222469,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+53.168270,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+53.554363,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+53.485577,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+53.532558,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+53.806896,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+53.890411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+53.848854,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+53.186768,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+53.213520,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+53.140560,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+53.455799,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+53.507538,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+53.552536,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+53.776527,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+53.859222,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+53.818336,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+53.124413,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+53.193928,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+53.183037,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+53.546967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+53.549915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+53.495792,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+53.852169,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+53.850498,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+53.876457,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+53.202755,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+53.162029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+53.118725,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+53.454281,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+53.465240,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+53.497383,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+53.798267,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+53.797962,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+53.849411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+54.126938,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+54.195629,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+54.117855,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+54.478477,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+54.502048,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+54.501354,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+54.813816,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+54.815281,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+54.871952,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+54.106087,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+54.135098,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+54.132587,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+54.516033,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+54.557106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+54.467541,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+54.841846,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+54.773533,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+54.830524,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+54.152237,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+54.225052,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+54.204712,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+54.503342,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+54.545261,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+54.557003,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+54.804756,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+54.794430,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+54.771698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+54.224411,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+54.127312,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+54.115250,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+54.473492,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+54.527569,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+54.507908,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+54.813065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+54.898220,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+54.853012,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+54.121845,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+54.144268,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+54.174557,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+54.526585,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+54.473900,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+54.518005,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+54.847099,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+54.802029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+54.793514,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+54.143875,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+54.134258,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+54.213333,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+54.550507,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+54.459484,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+54.449825,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+54.801537,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+54.784985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+54.769222,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+54.196976,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+54.178967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+54.124821,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+54.516373,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+54.520206,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+54.496212,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+54.818062,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+54.787605,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+54.824600,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+54.167709,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+54.110859,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+54.113586,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+54.556965,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+54.468117,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+54.434826,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+54.882919,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+54.770611,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+54.864567,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+54.119244,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+54.192802,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+54.170151,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+54.525578,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+54.461563,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+54.506996,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+54.794220,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+54.832947,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+54.888836,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+54.120201,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+54.136829,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+54.195499,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+54.527649,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+54.545208,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+54.531567,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+54.791336,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+54.821518,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+54.879963,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+54.226608,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+54.196995,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+54.186924,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+54.502499,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+54.565315,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+54.489071,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+54.801395,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+54.787659,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+54.847473,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+54.110634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+54.158367,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+54.170006,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+54.523399,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+54.442406,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+54.519779,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+54.774071,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+54.868561,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+54.871189,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+55.190952,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+55.191593,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+55.128365,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+55.486843,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+55.458435,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+55.507492,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+55.885456,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+55.770092,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+55.802567,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+55.104282,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+55.113491,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+55.224407,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+55.438301,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+55.526138,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+55.451904,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+55.873898,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+55.862995,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+55.845978,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+55.209858,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+55.194374,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+55.207191,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+55.470947,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+55.439796,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+55.470013,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+55.863129,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+55.866837,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+55.819530,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+55.173359,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+55.145458,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+55.122345,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+55.511765,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+55.463112,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+55.453209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+55.808571,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+55.847153,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+55.814240,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+55.111885,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+55.114353,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+55.152729,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+55.512165,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+55.503857,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+55.498714,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+55.872570,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+55.854534,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+55.792839,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+55.111542,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+55.200775,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+55.182709,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+55.534115,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+55.511532,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+55.476105,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+55.882973,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+55.867233,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+55.820869,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+55.189129,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+55.114006,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+55.162334,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+55.507458,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+55.461285,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+55.471241,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+55.802723,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+55.865044,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+55.867474,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+55.215721,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+55.216476,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+55.209549,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+55.463707,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+55.529041,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+55.466209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+55.812546,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+55.773117,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+55.820534,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+55.216385,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+55.175903,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+55.176449,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+55.445618,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+55.546024,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+55.537422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+55.779995,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+55.890335,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+55.884468,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+55.208412,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+55.158298,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+55.120869,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+55.526211,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+55.565315,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+55.467636,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+55.871838,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+55.838737,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+55.825562,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+55.233238,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+55.196354,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+55.150902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+55.558887,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+55.552406,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+55.555889,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+55.813553,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+55.767593,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+55.846710,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+55.145969,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+55.180443,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+55.106800,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+55.456005,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+55.532539,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+55.494896,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+55.863243,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+55.848995,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+55.891785,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+56.166611,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+56.128429,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+56.153812,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+56.439682,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+56.450569,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+56.483124,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+56.886478,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+56.787045,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+56.854164,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+56.110634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+56.231628,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+56.104076,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+56.496975,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+56.522827,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+56.487797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+56.873226,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+56.808449,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+56.877930,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+56.147968,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+56.183830,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+56.208572,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+56.534000,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+56.560619,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+56.505642,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+56.772556,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+56.861454,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+56.878922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+56.129944,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+56.172470,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+56.212318,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+56.480698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+56.446789,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+56.524704,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+56.853203,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+56.872452,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+56.769371,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+56.140759,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+56.184422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+56.187916,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+56.446209,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+56.472984,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+56.444946,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+56.884541,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+56.859509,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+56.867672,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+56.139740,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+56.121563,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+56.142151,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+56.510376,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+56.552013,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+56.475464,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+56.790001,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+56.792843,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+56.879387,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+56.225372,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+56.146965,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+56.191460,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+56.559982,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+56.541065,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+56.437698,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+56.793186,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+56.829498,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+56.852291,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+56.223495,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+56.208427,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+56.145802,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+56.513569,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+56.498714,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+56.551296,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+56.792900,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+56.856880,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+56.792488,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+56.109081,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+56.181210,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+56.167465,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+56.474892,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+56.507000,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+56.452843,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+56.778893,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+56.883106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+56.777096,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+56.122902,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+56.181347,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+56.128967,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+56.512341,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+56.472843,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+56.501034,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+56.770374,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+56.857468,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+56.821381,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+56.139446,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+56.189053,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+56.144966,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+56.462734,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+56.566551,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+56.529903,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+56.788654,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+56.776852,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+56.887314,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+56.132915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+56.138378,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+56.109215,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+56.492805,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+56.565498,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+56.441422,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+56.801918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+56.875187,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+56.868740,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+57.189922,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+57.172085,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+57.109512,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+57.448681,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+57.472218,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+57.458500,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+57.803734,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+57.786671,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+57.892162,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+57.206448,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+57.120010,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+57.122158,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+57.495293,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+57.515797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+57.553867,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+57.898094,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+57.894115,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+57.844490,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+57.135479,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+57.143444,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+57.147362,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+57.535423,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+57.455029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+57.529865,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+57.856236,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+57.848442,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+57.772129,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+57.184532,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+57.211372,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+57.138298,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+57.521832,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+57.536442,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+57.445480,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+57.798264,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+57.790676,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+57.766834,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+57.193054,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+57.208549,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+57.217495,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+57.522171,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+57.458897,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+57.552727,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+57.766903,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+57.899296,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+57.877918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+57.220924,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+57.212345,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+57.204071,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+57.563358,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+57.560333,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+57.530914,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+57.847191,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+57.882515,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+57.811054,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+57.106525,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+57.110214,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+57.150852,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+57.514660,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+57.460915,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+57.483627,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+57.832489,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+57.828568,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+57.771801,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+57.218460,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+57.212059,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+57.203487,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+57.560867,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+57.554993,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+57.479267,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+57.837948,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+57.863430,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+57.875935,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+57.118004,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+57.187019,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+57.169624,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+57.514862,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+57.453201,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+57.524899,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+57.837742,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+57.879536,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+57.867123,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+57.147808,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+57.140812,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+57.108582,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+57.507435,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+57.556232,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+57.565395,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+57.790199,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+57.795254,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+57.818684,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+57.218060,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+57.163280,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+57.188015,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+57.556530,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+57.554985,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+57.529675,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+57.774570,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+57.899395,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+57.785309,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+57.203285,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+57.153355,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+57.181633,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+57.524937,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+57.563744,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+57.491711,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+57.884106,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+57.887638,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+57.891529,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+58.226059,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+58.192905,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+58.187679,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+58.493248,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+58.528530,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+58.519363,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+58.850498,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+58.826462,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+58.867550,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+58.210979,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+58.157246,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+58.164181,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+58.535320,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+58.549522,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+58.456642,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+58.854588,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+58.824310,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+58.795280,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+58.143219,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+58.107224,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+58.114929,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+58.507881,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+58.477539,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+58.455616,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+58.829342,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+58.827419,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+58.875053,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+58.160706,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+58.130379,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+58.172634,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+58.528450,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+58.468506,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+58.456547,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+58.814224,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+58.865337,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+58.798653,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+58.159588,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+58.103909,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+58.158566,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+58.474819,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+58.492092,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+58.515903,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+58.866993,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+58.864311,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+58.868778,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+58.179878,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+58.214497,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+58.229233,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+58.554516,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+58.447510,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+58.454838,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+58.775299,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+58.879093,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+58.820732,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+58.213928,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+58.118797,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+58.162918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+58.433388,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+58.450535,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+58.512180,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+58.881042,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+58.872295,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+58.883274,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+58.149509,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+58.194252,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+58.169857,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+58.541706,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+58.515488,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+58.470482,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+58.792538,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+58.832081,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+58.819603,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+58.180283,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+58.118992,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+58.160069,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+58.537674,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+58.546883,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+58.455029,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+58.856205,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+58.840061,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+58.829350,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+58.210114,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+58.196522,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+58.155510,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+58.552269,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+58.492599,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+58.506622,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+58.850330,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+58.877251,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+58.835426,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+58.112740,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+58.193851,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+58.205441,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+58.454918,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+58.478939,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+58.512196,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+58.823212,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+58.783703,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+58.840286,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+58.220165,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+58.189861,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+58.162613,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+58.443882,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+58.525185,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+58.449135,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+58.865883,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+58.855579,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+58.884434,+0.500000] , 1. [+0.000000,-0.064000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-1.txt b/scenes/cell-growth/results/particles-frame-1.txt deleted file mode 100644 index 3e6fa063..00000000 --- a/scenes/cell-growth/results/particles-frame-1.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+46.026943,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+46.128189,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+46.100521,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+46.356815,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+46.445091,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+46.356571,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+46.704151,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+46.792465,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+46.757465,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+46.111771,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+46.080585,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+46.130772,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+46.345100,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+46.377625,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+46.381359,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+46.796421,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+46.696228,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+46.761471,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+46.110802,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+46.073517,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+46.110985,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+46.354969,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+46.404598,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+46.346928,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+46.699615,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+46.762962,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+46.738445,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+46.101284,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+46.106262,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+46.054382,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+46.397102,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+46.453869,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+46.390686,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+46.728115,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+46.720715,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+46.691059,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+46.076229,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+46.098015,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+46.027439,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+46.395393,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+46.438766,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+46.387268,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+46.803993,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+46.735275,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+46.722507,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+46.021393,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+46.016106,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+46.084454,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+46.339592,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+46.397060,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+46.350277,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+46.730476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+46.680901,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+46.696453,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+46.137032,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+46.057205,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+46.043018,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+46.445412,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+46.439339,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+46.411297,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+46.779800,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+46.715889,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+46.755539,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+46.011635,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+46.097912,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+46.128498,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+46.367786,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+46.369144,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+46.347614,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+46.794518,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+46.690201,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+46.757771,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+46.060303,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+46.038681,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+46.042503,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+46.348492,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+46.338051,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+46.361832,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+46.752033,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+46.727112,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+46.741920,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+46.029907,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+46.044655,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+46.026890,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+46.465374,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+46.340267,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+46.446308,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+46.751362,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+46.705311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+46.721127,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+46.075367,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+46.118225,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+46.129921,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+46.434818,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+46.351944,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+46.381554,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+46.784821,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+46.770275,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+46.682491,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+46.090801,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+46.105873,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+46.075840,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+46.437687,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+46.400177,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+46.378490,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+46.749935,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+46.786907,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+46.677361,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+47.034924,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+47.102879,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+47.048336,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+47.396690,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+47.440109,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+47.367451,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+47.694477,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+47.751278,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+47.671593,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+47.084179,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+47.045025,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+47.007713,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+47.397224,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+47.403549,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+47.449894,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+47.720638,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+47.759514,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+47.729240,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+47.011990,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+47.047989,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+47.097725,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+47.455410,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+47.370865,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+47.343704,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+47.768902,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+47.698429,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+47.687439,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+47.042252,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+47.021507,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+47.021511,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+47.360317,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+47.444881,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+47.351997,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+47.729691,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+47.763485,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+47.744476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+47.079445,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+47.016045,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+47.135551,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+47.391579,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+47.384811,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+47.368240,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+47.703388,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+47.796890,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+47.764435,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+47.026340,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+47.020992,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+47.042934,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+47.398602,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+47.466839,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+47.445824,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+47.785767,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+47.726147,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+47.677200,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+47.031670,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+47.051613,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+47.027855,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+47.361778,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+47.342079,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+47.456200,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+47.763416,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+47.771278,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+47.755829,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+47.055439,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+47.019253,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+47.014172,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+47.457573,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+47.461205,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+47.362061,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+47.765831,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+47.706291,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+47.766773,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+47.042984,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+47.110237,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+47.062653,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+47.358505,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+47.345409,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+47.469646,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+47.680229,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+47.673706,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+47.725864,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+47.031429,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+47.019665,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+47.130070,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+47.444214,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+47.406471,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+47.440960,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+47.704453,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+47.721508,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+47.739380,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+47.117561,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+47.055264,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+47.011272,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+47.402653,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+47.382732,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+47.398472,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+47.722954,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+47.781612,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+47.695480,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+47.036785,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+47.107643,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+47.067211,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+47.430202,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+47.434906,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+47.372208,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+47.736622,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+47.725845,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+47.698727,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+48.066818,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+48.026745,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+48.088875,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+48.428177,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+48.407078,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+48.462643,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+48.789143,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+48.674774,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+48.696239,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+48.056675,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+48.127041,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+48.130699,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+48.404190,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+48.347210,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+48.382240,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+48.750164,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+48.780045,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+48.699280,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+48.040924,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+48.024323,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+48.046944,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+48.456448,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+48.363564,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+48.338581,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+48.676369,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+48.682793,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+48.732929,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+48.028149,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+48.107357,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+48.107483,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+48.360119,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+48.467361,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+48.418709,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+48.782978,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+48.691391,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+48.747459,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+48.034748,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+48.121803,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+48.092918,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+48.449726,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+48.429550,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+48.358154,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+48.734444,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+48.746342,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+48.764343,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+48.034042,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+48.073101,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+48.013260,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+48.397717,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+48.352051,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+48.393204,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+48.725277,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+48.740337,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+48.688984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+48.087818,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+48.028477,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+48.031292,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+48.376087,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+48.391773,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+48.458244,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+48.715420,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+48.802448,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+48.782314,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+48.119514,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+48.004547,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+48.136242,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+48.451328,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+48.349316,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+48.425064,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+48.719154,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+48.714653,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+48.753613,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+48.018967,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+48.047123,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+48.070747,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+48.460464,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+48.452049,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+48.379307,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+48.762184,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+48.758488,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+48.788143,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+48.134209,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+48.049618,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+48.071213,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+48.470493,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+48.350346,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+48.417114,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+48.705189,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+48.772793,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+48.673199,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+48.070423,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+48.004345,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+48.026283,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+48.382393,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+48.454964,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+48.427505,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+48.717106,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+48.744476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+48.725456,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+48.042534,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+48.009460,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+48.121754,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+48.346668,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+48.468212,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+48.423206,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+48.721733,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+48.717369,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+48.729164,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+49.106102,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+49.067142,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+49.070683,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+49.456028,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+49.428917,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+49.346142,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+49.771896,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+49.683678,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+49.728279,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+49.134735,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+49.116596,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+49.019386,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+49.396374,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+49.379066,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+49.445770,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+49.758854,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+49.671207,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+49.677505,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+49.045654,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+49.070549,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+49.047245,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+49.414719,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+49.410069,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+49.412682,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+49.764324,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+49.781025,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+49.720913,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+49.059784,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+49.109272,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+49.093605,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+49.403484,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+49.434826,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+49.421146,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+49.673176,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+49.722939,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+49.782612,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+49.107922,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+49.061733,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+49.130634,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+49.402214,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+49.418056,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+49.340664,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+49.681389,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+49.785252,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+49.707134,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+49.061794,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+49.040421,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+49.086315,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+49.469593,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+49.354088,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+49.382622,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+49.679695,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+49.761547,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+49.688419,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+49.043819,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+49.083340,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+49.040276,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+49.402321,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+49.342991,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+49.392841,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+49.742088,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+49.714340,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+49.729652,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+49.073925,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+49.021641,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+49.021679,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+49.437027,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+49.397831,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+49.448616,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+49.685711,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+49.793003,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+49.724113,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+49.026581,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+49.092369,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+49.103558,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+49.411678,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+49.350754,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+49.447891,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+49.799473,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+49.771824,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+49.777477,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+49.127022,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+49.095329,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+49.087570,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+49.455299,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+49.454819,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+49.394287,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+49.715740,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+49.717148,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+49.763332,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+49.076477,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+49.125175,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+49.035027,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+49.350529,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+49.382481,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+49.398972,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+49.733543,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+49.678753,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+49.766598,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+49.113224,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+49.103561,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+49.037720,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+49.409275,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+49.456635,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+49.411278,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+49.693016,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+49.763245,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+49.686764,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+50.087063,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+50.049408,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+50.113472,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+50.386196,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+50.395390,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+50.355610,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+50.747063,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+50.740662,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+50.697929,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+50.059063,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+50.034523,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+50.121449,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+50.434887,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+50.393055,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+50.379971,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+50.746471,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+50.730545,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+50.783634,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+50.086353,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+50.069077,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+50.017246,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+50.444633,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+50.421036,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+50.468109,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+50.699978,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+50.696552,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+50.773399,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+50.026649,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+50.041767,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+50.102352,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+50.469006,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+50.359638,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+50.420422,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+50.690853,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+50.739384,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+50.718067,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+50.042065,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+50.115955,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+50.136566,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+50.434841,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+50.382767,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+50.469921,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+50.688202,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+50.676270,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+50.744473,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+50.041759,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+50.094604,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+50.121078,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+50.373554,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+50.425659,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+50.397415,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+50.741615,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+50.744675,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+50.689957,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+50.018864,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+50.066525,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+50.075363,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+50.417114,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+50.360764,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+50.467716,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+50.691772,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+50.766270,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+50.708057,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+50.084366,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+50.135036,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+50.072205,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+50.375008,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+50.459965,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+50.381462,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+50.797329,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+50.787392,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+50.702263,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+50.019836,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+50.061943,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+50.009914,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+50.382656,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+50.362663,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+50.415161,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+50.695591,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+50.710125,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+50.780018,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+50.048431,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+50.082291,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+50.061726,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+50.410976,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+50.399487,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+50.373409,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+50.707497,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+50.750725,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+50.761589,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+50.090736,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+50.005226,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+50.031178,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+50.390152,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+50.412109,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+50.392963,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+50.750488,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+50.692963,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+50.779537,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+50.098801,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+50.102669,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+50.026669,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+50.355824,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+50.409611,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+50.337948,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+50.735542,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+50.790798,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+50.731472,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+51.116478,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+51.023819,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+51.133984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+51.360912,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+51.345242,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+51.378502,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+51.746891,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+51.788521,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+51.710510,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+51.031315,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+51.135086,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+51.052311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+51.388535,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+51.338856,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+51.341728,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+51.716797,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+51.700359,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+51.770626,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+51.073093,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+51.091751,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+51.097809,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+51.412575,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+51.424473,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+51.358009,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+51.687710,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+51.713310,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+51.767315,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+51.014603,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+51.078796,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+51.056206,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+51.345966,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+51.389523,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+51.457893,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+51.682976,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+51.675350,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+51.702801,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+51.011646,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+51.024170,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+51.037880,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+51.393902,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+51.361382,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+51.347805,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+51.673897,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+51.692017,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+51.717476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+51.056461,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+51.113914,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+51.134830,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+51.337799,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+51.429653,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+51.447315,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+51.792503,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+51.795105,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+51.797661,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+51.018311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+51.015438,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+51.090912,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+51.371494,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+51.388039,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+51.400227,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+51.713230,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+51.786591,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+51.737690,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+51.061901,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+51.005676,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+51.093292,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+51.392349,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+51.341690,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+51.431332,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+51.751667,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+51.674236,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+51.716682,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+51.120697,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+51.082844,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+51.080269,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+51.433052,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+51.346664,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+51.349689,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+51.708412,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+51.744858,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+51.733212,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+51.117092,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+51.125286,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+51.038513,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+51.341583,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+51.353321,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+51.368118,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+51.680511,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+51.692974,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+51.743237,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+51.126633,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+51.053940,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+51.113564,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+51.440929,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+51.458569,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+51.443630,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+51.678082,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+51.788288,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+51.779709,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+51.116508,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+51.129719,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+51.075729,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+51.440445,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+51.433956,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+51.363773,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+51.705307,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+51.789639,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+51.772095,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+52.105495,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+52.095963,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+52.020504,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+52.459476,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+52.347496,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+52.391548,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+52.749779,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+52.782604,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+52.756317,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+52.077633,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+52.085453,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+52.090191,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+52.393047,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+52.398987,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+52.382252,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+52.689732,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+52.675797,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+52.726681,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+52.058319,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+52.074039,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+52.010773,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+52.378590,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+52.419086,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+52.463730,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+52.705978,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+52.689903,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+52.764225,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+52.030991,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+52.107887,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+52.120823,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+52.388264,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+52.391983,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+52.442608,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+52.759506,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+52.686752,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+52.734940,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+52.072411,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+52.007198,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+52.102642,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+52.464840,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+52.406464,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+52.464619,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+52.705658,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+52.777012,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+52.713871,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+52.100170,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+52.033630,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+52.111538,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+52.350685,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+52.432251,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+52.428547,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+52.785934,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+52.769333,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+52.714554,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+52.015560,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+52.078705,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+52.049202,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+52.467484,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+52.446102,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+52.420998,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+52.771984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+52.775143,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+52.756424,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+52.049416,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+52.049904,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+52.075699,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+52.383488,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+52.350079,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+52.367569,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+52.765297,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+52.723846,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+52.767719,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+52.039635,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+52.101440,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+52.033966,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+52.440971,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+52.427094,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+52.460655,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+52.776810,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+52.737976,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+52.782310,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+52.076271,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+52.028671,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+52.071148,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+52.346657,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+52.407146,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+52.422680,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+52.732311,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+52.684231,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+52.740726,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+52.130741,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+52.022453,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+52.037193,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+52.425770,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+52.355972,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+52.438587,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+52.699371,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+52.690147,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+52.727989,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+52.005600,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+52.013485,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+52.023998,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+52.435242,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+52.338634,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+52.435921,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+52.705849,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+52.710796,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+52.680336,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+53.135323,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+53.006844,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+53.136509,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+53.427551,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+53.423878,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+53.382519,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+53.750645,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+53.781876,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+53.678490,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+53.092205,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+53.069870,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+53.104824,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+53.401844,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+53.388874,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+53.381611,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+53.769943,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+53.691380,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+53.731697,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+53.121834,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+53.028305,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+53.057556,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+53.404266,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+53.415382,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+53.394192,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+53.716858,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+53.730621,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+53.719772,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+53.045601,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+53.131031,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+53.035213,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+53.404060,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+53.422359,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+53.372650,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+53.679485,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+53.773102,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+53.677414,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+53.015041,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+53.086708,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+53.071739,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+53.449966,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+53.349335,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+53.410187,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+53.765545,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+53.719120,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+53.800728,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+53.030697,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+53.106583,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+53.099152,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+53.392685,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+53.359562,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+53.407413,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+53.693848,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+53.776005,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+53.683456,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+53.041691,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+53.135147,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+53.097290,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+53.410828,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+53.366196,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+53.340187,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+53.749043,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+53.738209,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+53.718803,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+53.077927,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+53.095291,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+53.121632,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+53.373772,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+53.468048,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+53.395676,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+53.766945,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+53.683353,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+53.789116,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+53.057064,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+53.126469,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+53.072269,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+53.458363,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+53.389576,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+53.436558,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+53.710896,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+53.794411,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+53.752853,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+53.090767,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+53.117519,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+53.044559,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+53.359798,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+53.411537,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+53.456535,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+53.680527,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+53.763222,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+53.722336,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+53.028412,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+53.097927,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+53.087036,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+53.450966,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+53.453915,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+53.399792,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+53.756168,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+53.754498,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+53.780457,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+53.106754,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+53.066029,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+53.022724,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+53.358280,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+53.369240,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+53.401382,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+53.702267,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+53.701962,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+53.753410,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+54.030937,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+54.099628,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+54.021854,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+54.382477,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+54.406048,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+54.405354,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+54.717815,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+54.719280,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+54.775951,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+54.010086,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+54.039097,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+54.036587,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+54.420033,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+54.461105,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+54.371540,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+54.745846,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+54.677532,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+54.734524,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+54.056236,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+54.129051,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+54.108711,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+54.407341,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+54.449261,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+54.461002,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+54.708755,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+54.698429,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+54.675697,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+54.128410,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+54.031311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+54.019249,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+54.377491,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+54.431568,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+54.411907,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+54.717064,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+54.802219,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+54.757011,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+54.025845,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+54.048267,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+54.078556,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+54.430584,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+54.377899,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+54.422005,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+54.751099,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+54.706028,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+54.697514,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+54.047874,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+54.038258,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+54.117332,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+54.454506,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+54.363483,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+54.353825,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+54.705536,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+54.688984,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+54.673222,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+54.100975,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+54.082966,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+54.028820,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+54.420372,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+54.424206,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+54.400211,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+54.722061,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+54.691605,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+54.728600,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+54.071709,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+54.014858,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+54.017586,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+54.460964,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+54.372116,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+54.338825,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+54.786919,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+54.674610,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+54.768566,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+54.023243,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+54.096802,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+54.074150,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+54.429577,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+54.365562,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+54.410995,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+54.698219,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+54.736946,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+54.792835,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+54.024200,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+54.040829,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+54.099499,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+54.431648,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+54.449207,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+54.435566,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+54.695335,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+54.725517,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+54.783962,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+54.130608,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+54.100994,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+54.090923,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+54.406498,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+54.469315,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+54.393070,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+54.705395,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+54.691658,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+54.751472,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+54.014633,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+54.062366,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+54.074005,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+54.427399,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+54.346405,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+54.423779,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+54.678070,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+54.772560,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+54.775188,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+55.094952,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+55.095592,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+55.032364,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+55.390842,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+55.362434,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+55.411491,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+55.789455,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+55.674091,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+55.706566,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+55.008282,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+55.017490,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+55.128407,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+55.342300,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+55.430138,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+55.355904,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+55.777897,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+55.766994,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+55.749977,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+55.113857,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+55.098373,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+55.111191,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+55.374947,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+55.343796,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+55.374012,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+55.767128,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+55.770836,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+55.723530,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+55.077358,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+55.049458,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+55.026344,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+55.415764,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+55.367111,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+55.357208,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+55.712570,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+55.751152,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+55.718239,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+55.015884,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+55.018353,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+55.056728,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+55.416164,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+55.407856,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+55.402714,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+55.776569,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+55.758533,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+55.696838,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+55.015541,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+55.104774,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+55.086708,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+55.438114,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+55.415531,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+55.380104,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+55.786972,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+55.771233,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+55.724869,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+55.093128,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+55.018005,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+55.066334,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+55.411457,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+55.365284,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+55.375240,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+55.706722,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+55.769043,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+55.771473,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+55.119720,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+55.120476,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+55.113548,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+55.367706,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+55.433041,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+55.370209,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+55.716545,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+55.677116,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+55.724533,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+55.120384,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+55.079903,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+55.080448,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+55.349617,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+55.450024,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+55.441422,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+55.683994,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+55.794334,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+55.788467,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+55.112411,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+55.062298,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+55.024868,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+55.430210,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+55.469315,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+55.371635,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+55.775837,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+55.742737,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+55.729561,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+55.137238,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+55.100353,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+55.054901,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+55.462887,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+55.456406,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+55.459888,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+55.717552,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+55.671593,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+55.750710,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+55.049969,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+55.084442,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+55.010799,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+55.360004,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+55.436539,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+55.398895,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+55.767242,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+55.752995,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+55.795784,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+56.070610,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+56.032429,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+56.057812,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+56.343681,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+56.354568,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+56.387123,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+56.790478,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+56.691044,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+56.758163,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+56.014633,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+56.135628,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+56.008076,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+56.400974,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+56.426826,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+56.391796,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+56.777225,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+56.712448,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+56.781929,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+56.051968,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+56.087830,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+56.112572,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+56.438000,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+56.464619,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+56.409641,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+56.676556,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+56.765453,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+56.782921,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+56.033943,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+56.076469,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+56.116318,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+56.384697,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+56.350788,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+56.428703,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+56.757202,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+56.776451,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+56.673370,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+56.044758,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+56.088421,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+56.091915,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+56.350208,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+56.376984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+56.348946,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+56.788540,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+56.763508,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+56.771671,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+56.043739,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+56.025562,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+56.046150,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+56.414375,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+56.456013,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+56.379463,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+56.694000,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+56.696842,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+56.783386,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+56.129372,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+56.050964,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+56.095459,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+56.463982,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+56.445065,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+56.341698,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+56.697186,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+56.733498,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+56.756290,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+56.127495,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+56.112427,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+56.049801,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+56.417568,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+56.402714,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+56.455296,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+56.696899,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+56.760880,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+56.696487,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+56.013081,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+56.085209,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+56.071465,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+56.378891,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+56.410999,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+56.356842,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+56.682892,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+56.787106,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+56.681095,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+56.026901,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+56.085346,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+56.032967,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+56.416340,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+56.376842,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+56.405033,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+56.674374,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+56.761467,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+56.725380,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+56.043446,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+56.093052,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+56.048965,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+56.366734,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+56.470551,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+56.433903,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+56.692654,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+56.680851,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+56.791313,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+56.036915,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+56.042377,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+56.013214,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+56.396805,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+56.469498,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+56.345421,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+56.705917,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+56.779186,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+56.772739,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+57.093922,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+57.076084,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+57.013512,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+57.352680,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+57.376217,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+57.362499,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+57.707733,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+57.690670,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+57.796162,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+57.110447,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+57.024010,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+57.026157,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+57.399292,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+57.419796,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+57.457867,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+57.802094,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+57.798115,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+57.748489,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+57.039478,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+57.047443,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+57.051361,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+57.439423,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+57.359028,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+57.433865,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+57.760235,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+57.752441,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+57.676128,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+57.088531,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+57.115372,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+57.042297,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+57.425831,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+57.440441,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+57.349480,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+57.702263,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+57.694675,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+57.670834,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+57.097054,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+57.112549,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+57.121494,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+57.426170,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+57.362896,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+57.456726,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+57.670902,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+57.803295,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+57.781918,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+57.124924,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+57.116344,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+57.108070,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+57.467358,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+57.464333,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+57.434914,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+57.751190,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+57.786514,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+57.715054,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+57.010525,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+57.014214,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+57.054852,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+57.418659,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+57.364914,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+57.387627,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+57.736488,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+57.732567,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+57.675800,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+57.122459,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+57.116058,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+57.107487,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+57.464867,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+57.458992,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+57.383266,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+57.741947,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+57.767429,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+57.779934,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+57.022003,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+57.091019,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+57.073624,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+57.418861,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+57.357201,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+57.428898,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+57.741741,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+57.783535,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+57.771122,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+57.051807,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+57.044811,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+57.012581,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+57.411434,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+57.460232,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+57.469395,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+57.694199,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+57.699253,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+57.722683,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+57.122059,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+57.067280,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+57.092014,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+57.460529,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+57.458984,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+57.433674,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+57.678570,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+57.803394,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+57.689308,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+57.107285,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+57.057354,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+57.085632,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+57.428936,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+57.467743,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+57.395710,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+57.788105,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+57.791637,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+57.795528,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+58.130058,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+58.096905,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+58.091679,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+58.397247,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+58.432529,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+58.423363,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+58.754498,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+58.730461,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+58.771549,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+58.114979,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+58.061245,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+58.068180,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+58.439320,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+58.453522,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+58.360641,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+58.758587,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+58.728310,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+58.699280,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+58.047218,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+58.011223,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+58.018929,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+58.411880,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+58.381538,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+58.359615,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+58.733341,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+58.731419,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+58.779053,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+58.064705,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+58.034378,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+58.076633,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+58.432449,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+58.372505,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+58.360546,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+58.718224,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+58.769337,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+58.702652,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+58.063587,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+58.007908,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+58.062565,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+58.378819,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+58.396091,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+58.419903,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+58.770992,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+58.768311,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+58.772778,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+58.083878,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+58.118496,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+58.133232,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+58.458515,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+58.351509,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+58.358837,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+58.679298,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+58.783092,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+58.724731,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+58.117928,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+58.022797,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+58.066917,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+58.337387,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+58.354534,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+58.416180,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+58.785042,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+58.776295,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+58.787273,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+58.053509,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+58.098251,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+58.073856,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+58.445705,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+58.419487,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+58.374481,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+58.696537,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+58.736080,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+58.723602,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+58.084282,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+58.022991,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+58.064068,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+58.441673,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+58.450882,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+58.359028,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+58.760204,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+58.744061,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+58.733349,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+58.114113,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+58.100521,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+58.059509,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+58.456268,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+58.396599,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+58.410622,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+58.754330,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+58.781250,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+58.739426,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+58.016739,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+58.097851,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+58.109440,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+58.358917,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+58.382938,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+58.416195,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+58.727211,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+58.687702,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+58.744286,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+58.124165,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+58.093861,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+58.066612,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+58.347881,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+58.429184,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+58.353134,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+58.769882,+0.500000] , 1. [+0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+58.759579,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+58.788433,+0.500000] , 1. [-0.000000,-0.192000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-10.txt b/scenes/cell-growth/results/particles-frame-10.txt deleted file mode 100644 index 1473635e..00000000 --- a/scenes/cell-growth/results/particles-frame-10.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+40.042942,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+40.144188,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+40.116520,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+40.372814,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+40.461090,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+40.372570,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+40.720150,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+40.808464,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+40.773464,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+40.127769,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+40.096584,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+40.146770,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+40.361099,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+40.393623,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+40.397358,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+40.812420,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+40.712227,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+40.777470,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+40.126801,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+40.089516,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+40.126984,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+40.370968,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+40.420597,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+40.362926,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+40.715614,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+40.778961,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+40.754444,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+40.117283,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+40.122261,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+40.070381,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+40.413101,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+40.469868,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+40.406685,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+40.744114,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+40.736713,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+40.707058,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+40.092228,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+40.114014,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+40.043438,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+40.411392,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+40.454765,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+40.403267,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+40.819992,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+40.751274,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+40.738506,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+40.037392,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+40.032104,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+40.100452,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+40.355591,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+40.413059,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+40.366276,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+40.746475,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+40.696899,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+40.712452,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+40.153030,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+40.073204,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+40.059017,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+40.461411,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+40.455338,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+40.427296,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+40.795799,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+40.731888,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+40.771538,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+40.027634,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+40.113911,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+40.144497,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+40.383785,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+40.385143,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+40.363613,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+40.810516,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+40.706200,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+40.773769,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+40.076302,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+40.054680,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+40.058502,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+40.364491,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+40.354050,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+40.377831,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+40.768032,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+40.743111,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+40.757919,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+40.045906,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+40.060654,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+40.042889,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+40.481373,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+40.356266,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+40.462307,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+40.767361,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+40.721310,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+40.737125,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+40.091366,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+40.134224,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+40.145920,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+40.450817,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+40.367943,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+40.397552,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+40.800819,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+40.786274,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+40.698490,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+40.106800,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+40.121872,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+40.091839,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+40.453686,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+40.416176,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+40.394489,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+40.765934,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+40.802906,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+40.693359,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+41.050922,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+41.118877,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+41.064335,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+41.412689,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+41.456108,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+41.383450,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+41.710476,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+41.767277,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+41.687592,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+41.100178,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+41.061024,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+41.023712,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+41.413223,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+41.419548,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+41.465893,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+41.736641,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+41.775513,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+41.745239,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+41.027988,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+41.063988,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+41.113724,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+41.471409,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+41.386864,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+41.359703,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+41.784901,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+41.714428,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+41.703438,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+41.058250,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+41.037506,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+41.037510,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+41.376316,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+41.460880,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+41.367996,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+41.745689,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+41.779484,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+41.760475,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+41.095444,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+41.032043,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+41.151550,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+41.407578,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+41.400810,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+41.384239,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+41.719387,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+41.812889,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+41.780434,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+41.042339,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+41.036991,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+41.058933,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+41.414600,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+41.482838,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+41.461823,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+41.801765,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+41.742146,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+41.693199,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+41.047668,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+41.067612,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+41.043854,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+41.377777,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+41.358078,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+41.472198,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+41.779415,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+41.787277,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+41.771828,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+41.071438,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+41.035252,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+41.030170,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+41.473572,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+41.477203,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+41.378059,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+41.781830,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+41.722290,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+41.782772,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+41.058983,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+41.126236,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+41.078651,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+41.374504,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+41.361408,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+41.485645,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+41.696228,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+41.689705,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+41.741863,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+41.047428,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+41.035664,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+41.146069,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+41.460213,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+41.422470,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+41.456959,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+41.720451,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+41.737507,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+41.755379,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+41.133560,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+41.071262,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+41.027271,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+41.418652,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+41.398731,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+41.414471,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+41.738953,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+41.797611,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+41.711479,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+41.052784,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+41.123642,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+41.083210,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+41.446201,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+41.450905,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+41.388206,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+41.752621,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+41.741844,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+41.714725,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+42.082817,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+42.042747,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+42.104874,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+42.444176,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+42.423080,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+42.478645,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+42.805145,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+42.690773,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+42.712242,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+42.072678,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+42.143044,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+42.146698,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+42.420189,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+42.363209,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+42.398239,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+42.766163,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+42.796043,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+42.715279,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+42.056923,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+42.040321,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+42.062943,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+42.472446,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+42.379562,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+42.354580,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+42.692368,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+42.698792,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+42.748928,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+42.044147,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+42.123356,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+42.123482,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+42.376118,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+42.483360,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+42.434708,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+42.798977,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+42.707390,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+42.763458,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+42.050747,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+42.137802,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+42.108917,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+42.465725,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+42.445549,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+42.374153,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+42.750443,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+42.762341,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+42.780342,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+42.050041,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+42.089100,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+42.029259,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+42.413715,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+42.368050,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+42.409203,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+42.741276,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+42.756336,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+42.704983,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+42.103817,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+42.044476,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+42.047291,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+42.392086,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+42.407772,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+42.474243,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+42.731419,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+42.818447,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+42.798313,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+42.135513,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+42.020546,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+42.152241,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+42.467327,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+42.365314,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+42.441063,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+42.735153,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+42.730652,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+42.769611,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+42.034966,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+42.063122,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+42.086746,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+42.476463,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+42.468048,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+42.395306,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+42.778183,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+42.774487,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+42.804142,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+42.150208,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+42.065617,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+42.087212,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+42.486492,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+42.366344,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+42.433113,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+42.721188,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+42.788792,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+42.689198,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+42.086422,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+42.020344,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+42.042282,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+42.398392,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+42.470963,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+42.443504,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+42.733105,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+42.760475,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+42.741455,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+42.058533,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+42.025459,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+42.137753,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+42.362667,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+42.484211,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+42.439205,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+42.737732,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+42.733368,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+42.745163,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+43.122101,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+43.083145,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+43.086681,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+43.472031,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+43.444916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+43.362144,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+43.787895,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+43.699677,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+43.744278,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+43.150734,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+43.132595,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+43.035385,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+43.412373,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+43.395065,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+43.461769,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+43.774853,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+43.687206,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+43.693504,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+43.061653,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+43.086548,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+43.063244,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+43.430717,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+43.426067,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+43.428680,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+43.780323,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+43.797024,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+43.736912,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+43.075783,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+43.125271,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+43.109604,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+43.419483,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+43.450825,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+43.437145,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+43.689175,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+43.738934,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+43.798611,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+43.123920,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+43.077732,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+43.146633,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+43.418213,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+43.434055,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+43.356663,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+43.697388,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+43.801250,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+43.723133,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+43.077793,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+43.056419,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+43.102314,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+43.485592,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+43.370087,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+43.398621,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+43.695694,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+43.777546,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+43.704418,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+43.059818,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+43.099339,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+43.056274,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+43.418320,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+43.358990,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+43.408840,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000001,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+43.758087,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+43.730339,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+43.745651,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000001,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+43.089924,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+43.037640,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+43.037678,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+43.453026,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+43.413830,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+43.464615,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+43.701710,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+43.809002,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+43.740112,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+43.042580,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+43.108368,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+43.119556,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+43.427677,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+43.366753,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+43.463890,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+43.815472,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+43.787823,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+43.793476,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+43.143021,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+43.111328,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+43.103569,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+43.471298,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+43.470818,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+43.410286,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+43.731739,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+43.733147,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+43.779331,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+43.092476,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+43.141174,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+43.051025,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+43.366528,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+43.398479,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+43.414970,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+43.749542,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+43.694752,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+43.782597,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+43.129223,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+43.119560,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+43.053719,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+43.425274,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+43.472633,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+43.427277,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+43.709015,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+43.779243,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+43.702763,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+44.103062,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+44.065407,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+44.129471,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+44.402195,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+44.411388,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+44.371609,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+44.763062,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+44.756660,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+44.713928,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+44.075062,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+44.050522,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+44.137447,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+44.450886,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+44.409054,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+44.395969,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+44.762470,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+44.746544,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+44.799633,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+44.102352,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+44.085075,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+44.033245,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+44.460632,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+44.437035,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+44.484108,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+44.715977,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+44.712551,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+44.789398,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+44.042648,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+44.057766,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+44.118351,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+44.485004,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+44.375637,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+44.436420,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+44.706852,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+44.755383,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+44.734066,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+44.058064,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+44.131954,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+44.152565,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+44.450840,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+44.398766,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+44.485920,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+44.704201,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+44.692268,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+44.760471,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+44.057758,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+44.110603,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+44.137077,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+44.389553,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+44.441658,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+44.413414,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+44.757614,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+44.760674,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+44.705956,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+44.034863,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+44.082523,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+44.091362,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+44.433113,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+44.376762,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+44.483715,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+44.707771,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+44.782272,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+44.724056,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+44.100365,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000001,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+44.151035,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+44.088203,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+44.391006,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+44.475964,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+44.397461,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+44.813328,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+44.803391,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+44.718262,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+44.035835,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+44.077942,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+44.025913,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+44.398655,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+44.378662,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+44.431160,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+44.711590,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+44.726124,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+44.796017,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+44.064430,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+44.098289,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+44.077724,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+44.426975,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+44.415485,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+44.389408,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+44.723495,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+44.766724,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+44.777588,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+44.106735,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+44.021225,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+44.047176,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+44.406151,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+44.428108,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+44.408962,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+44.766487,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+44.708961,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+44.795536,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+44.114799,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+44.118668,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+44.042667,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+44.371822,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+44.425610,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+44.353947,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+44.751541,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+44.806797,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+44.747471,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+45.132477,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+45.039818,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+45.149982,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+45.376911,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+45.361240,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+45.394501,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+45.762890,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+45.804520,+0.500000] , 1. [-0.000000,-1.280001,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+45.726509,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+45.047314,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+45.151085,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+45.068310,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+45.404533,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+45.354855,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+45.357727,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+45.732796,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+45.716358,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+45.786625,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+45.089092,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+45.107750,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+45.113808,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+45.428574,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+45.440472,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+45.374008,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+45.703709,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+45.729309,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+45.783314,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+45.030602,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+45.094795,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+45.072205,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+45.361965,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+45.405521,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+45.473892,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+45.698975,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+45.691349,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+45.718800,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+45.027645,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+45.040169,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+45.053879,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+45.409901,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+45.377380,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+45.363804,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+45.689896,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+45.708015,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+45.733475,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+45.072460,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+45.129913,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+45.150829,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+45.353798,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+45.445652,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+45.463314,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+45.808502,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+45.811104,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+45.813660,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+45.034309,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+45.031441,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+45.106911,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+45.387493,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+45.404037,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+45.416225,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+45.729229,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+45.802589,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+45.753689,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+45.077900,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+45.021675,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+45.109291,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+45.408348,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+45.357689,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+45.447330,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+45.767666,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+45.690235,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+45.732681,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+45.136696,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+45.098843,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+45.096268,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+45.449051,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+45.362663,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+45.365688,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+45.724411,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+45.760857,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+45.749210,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+45.133091,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+45.141285,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+45.054512,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+45.357582,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+45.369320,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+45.384117,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+45.696510,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+45.708973,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+45.759235,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+45.142632,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+45.069939,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+45.129562,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+45.456928,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+45.474567,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+45.459629,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+45.694080,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+45.804287,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+45.795708,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+45.132507,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+45.145718,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+45.091728,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+45.456444,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+45.449955,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+45.379772,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+45.721306,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+45.805637,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+45.788094,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+46.121494,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+46.111961,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+46.036503,+0.500000] , 1. [-0.000000,-1.280001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+46.475475,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+46.363495,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+46.407547,+0.500000] , 1. [-0.000000,-1.280001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+46.765778,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+46.798603,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+46.772316,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+46.093632,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+46.101452,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+46.106190,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+46.409046,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+46.414986,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+46.398251,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+46.705730,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+46.691795,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+46.742680,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+46.074318,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+46.090038,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+46.026772,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+46.394588,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+46.435085,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+46.479729,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+46.721977,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+46.705902,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+46.780224,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+46.046989,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+46.123886,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+46.136822,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+46.404263,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+46.407982,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+46.458607,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+46.775505,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+46.702751,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+46.750938,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+46.088409,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+46.023197,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+46.118641,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+46.480839,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+46.422462,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+46.480618,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+46.721657,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+46.793011,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+46.729870,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+46.116169,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+46.049629,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+46.127537,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+46.366684,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+46.448250,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+46.444546,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+46.801933,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+46.785332,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+46.730553,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+46.031559,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+46.094704,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+46.065201,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+46.483482,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+46.462101,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+46.436996,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+46.787983,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+46.791142,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+46.772423,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+46.065414,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+46.065903,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+46.091698,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+46.399487,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+46.366077,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+46.383568,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+46.781296,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+46.739845,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+46.783718,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+46.055634,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+46.117439,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+46.049965,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+46.456970,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+46.443092,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+46.476654,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+46.792809,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+46.753975,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+46.798309,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+46.092270,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+46.044670,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+46.087147,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+46.362656,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+46.423145,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+46.438679,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+46.748310,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+46.700230,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+46.756725,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+46.146740,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+46.038452,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+46.053192,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+46.441769,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+46.371971,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+46.454586,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+46.715370,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+46.706146,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+46.743988,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+46.021599,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+46.029484,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+46.039997,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+46.451241,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+46.354633,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+46.451920,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+46.721848,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+46.726795,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+46.696335,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+47.151321,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+47.022842,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+47.152508,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+47.443550,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+47.439877,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+47.398518,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+47.766644,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+47.797874,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+47.694489,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+47.108204,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+47.085869,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+47.120823,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+47.417843,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+47.404873,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+47.397610,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+47.785942,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+47.707378,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+47.747696,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+47.137833,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+47.044304,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+47.073555,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+47.420265,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+47.431381,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+47.410191,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+47.732857,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+47.746620,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+47.735771,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+47.061600,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+47.147030,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+47.051212,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+47.420059,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+47.438358,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+47.388649,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+47.695484,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+47.789101,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+47.693413,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+47.031040,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+47.102707,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+47.087738,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+47.465965,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+47.365334,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+47.426186,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+47.781544,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+47.735119,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+47.816727,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+47.046696,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+47.122581,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+47.115150,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+47.408684,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+47.375561,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+47.423412,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+47.709846,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+47.792004,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+47.699455,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+47.057690,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+47.151146,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+47.113289,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+47.426826,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+47.382195,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+47.356186,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+47.765041,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+47.754208,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+47.734802,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+47.093925,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+47.111290,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+47.137630,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+47.389771,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+47.484047,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+47.411674,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+47.782944,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+47.699352,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+47.805115,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+47.073063,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+47.142467,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+47.088268,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+47.474361,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+47.405575,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+47.452557,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+47.726894,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+47.810410,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+47.768852,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+47.106766,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+47.133518,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+47.060558,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+47.375797,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+47.427536,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+47.472534,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+47.696526,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+47.779221,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+47.738335,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+47.044411,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+47.113926,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+47.103035,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+47.466965,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+47.469913,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+47.415791,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+47.772167,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+47.770496,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+47.796455,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+47.122753,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+47.082027,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+47.038723,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+47.374279,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+47.385239,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+47.417381,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+47.718266,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+47.717960,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+47.769409,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+48.046936,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+48.115627,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+48.037853,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+48.398476,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+48.422047,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+48.421352,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+48.733814,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+48.735279,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+48.791950,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+48.026085,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+48.055096,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+48.052586,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+48.436031,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+48.477104,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+48.387539,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+48.761845,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+48.693531,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+48.750523,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+48.072235,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+48.145050,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+48.124710,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+48.423340,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+48.465260,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+48.477001,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+48.724754,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+48.714428,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+48.691696,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+48.144409,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+48.047310,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+48.035248,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+48.393490,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+48.447567,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+48.427906,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+48.733063,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+48.818218,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+48.773010,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+48.041843,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+48.064266,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+48.094555,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+48.446583,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+48.393898,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+48.438004,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+48.767097,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+48.722027,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+48.713512,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+48.063873,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+48.054256,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+48.133331,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+48.470505,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+48.379482,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+48.369823,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+48.721535,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+48.704983,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+48.689220,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+48.116974,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+48.098965,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+48.044819,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+48.436371,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+48.440205,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+48.416210,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+48.738060,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+48.707603,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+48.744598,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+48.087708,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+48.030857,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+48.033585,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+48.476963,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+48.388115,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+48.354824,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+48.802917,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+48.690609,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+48.784565,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+48.039242,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+48.112801,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+48.090149,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+48.445576,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+48.381561,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+48.426994,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+48.714218,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+48.752945,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+48.808834,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+48.040199,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+48.056828,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+48.115498,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+48.447647,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+48.465206,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+48.451565,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+48.711334,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+48.741516,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+48.799961,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+48.146606,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+48.116993,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+48.106922,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+48.422497,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+48.485313,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+48.409069,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+48.721394,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+48.707657,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+48.767471,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+48.030632,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+48.078365,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+48.090004,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+48.443398,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+48.362404,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+48.439777,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+48.694069,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+48.788559,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+48.791187,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+49.110950,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+49.111591,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+49.048363,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+49.406841,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+49.378433,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+49.427490,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+49.805458,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+49.690090,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+49.722565,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+49.024281,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+49.033489,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+49.144405,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+49.358299,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+49.446136,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+49.371902,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+49.793896,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+49.782993,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+49.765976,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+49.129856,+0.500000] , 1. [-0.000000,-1.280001,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+49.114372,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+49.127190,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+49.390945,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+49.359795,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+49.390011,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+49.783127,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+49.786835,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+49.739529,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+49.093357,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+49.065456,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+49.042343,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+49.431763,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+49.383110,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+49.373207,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+49.728569,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+49.767151,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+49.734238,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+49.031883,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+49.034351,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+49.072727,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+49.432163,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+49.423855,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+49.418713,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+49.792568,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+49.774532,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+49.712837,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+49.031540,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+49.120773,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+49.102707,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+49.454113,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+49.431530,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+49.396103,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+49.802971,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+49.787231,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+49.740868,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+49.109127,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+49.034004,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+49.082333,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+49.427456,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+49.381283,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+49.391239,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+49.722721,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+49.785042,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+49.787472,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+49.135719,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+49.136475,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+49.129547,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+49.383705,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+49.449039,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+49.386208,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+49.732544,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+49.693115,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+49.740532,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+49.136383,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+49.095901,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+49.096447,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+49.365616,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+49.466022,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+49.457420,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+49.699993,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+49.810333,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+49.804466,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+49.128410,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+49.078297,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+49.040867,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+49.446209,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+49.485313,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+49.387634,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+49.791836,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+49.758736,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+49.745560,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+49.153236,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+49.116352,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+49.070900,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+49.478886,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+49.472404,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+49.475887,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+49.733551,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+49.687592,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+49.766708,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+49.065968,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+49.100441,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+49.026798,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+49.376003,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+49.452538,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+49.414894,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+49.783241,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+49.768993,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+49.811783,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+50.086609,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+50.048428,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+50.073811,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+50.359680,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+50.370567,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+50.403122,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+50.806477,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+50.707047,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+50.774162,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+50.030632,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+50.151627,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+50.024075,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+50.416973,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+50.442825,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+50.407795,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+50.793224,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+50.728447,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+50.797928,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+50.067966,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+50.103828,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+50.128571,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+50.453999,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+50.480618,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+50.425640,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+50.692554,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+50.781452,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+50.798920,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+50.049942,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+50.092468,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+50.132317,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+50.400696,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+50.366787,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+50.444698,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+50.773201,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+50.792450,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+50.689369,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+50.060757,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+50.104420,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+50.107914,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+50.366207,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+50.392979,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+50.364944,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+50.804539,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+50.779507,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+50.787670,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+50.059738,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+50.041561,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+50.062149,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+50.430374,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+50.472012,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+50.395462,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+50.709999,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+50.712841,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+50.799385,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+50.145370,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+50.066963,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+50.111458,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+50.479980,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+50.461063,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+50.357697,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+50.713184,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+50.749496,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+50.772289,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+50.143494,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+50.128426,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+50.065800,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+50.433567,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+50.418713,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+50.471294,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+50.712898,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+50.776878,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+50.712486,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+50.029079,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+50.101208,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+50.087463,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+50.394890,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+50.426998,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+50.372841,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+50.698891,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+50.803104,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+50.697094,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+50.042900,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+50.101345,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+50.048965,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+50.432339,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+50.392841,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+50.421032,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+50.690372,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+50.777466,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+50.741379,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+50.059444,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+50.109051,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+50.064964,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+50.382732,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+50.486549,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+50.449902,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+50.708652,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+50.696850,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+50.807312,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+50.052914,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+50.058376,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+50.029213,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+50.412804,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+50.485497,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+50.361420,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+50.721916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+50.795185,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+50.788738,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+51.109921,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+51.092083,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+51.029510,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+51.368679,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+51.392216,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+51.378498,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+51.723732,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+51.706669,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+51.812160,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+51.126446,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+51.040009,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+51.042156,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+51.415291,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+51.435795,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+51.473866,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+51.818092,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+51.814114,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+51.764488,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+51.055477,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+51.063442,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+51.067360,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+51.455421,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+51.375027,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+51.449863,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+51.776234,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+51.768440,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+51.692127,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+51.104530,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+51.131371,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+51.058296,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+51.441830,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+51.456440,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+51.365479,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+51.718262,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+51.710674,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+51.686832,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+51.113052,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+51.128544,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+51.137493,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+51.442169,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+51.378895,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+51.472725,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+51.686901,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+51.819294,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+51.797916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+51.140919,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+51.132343,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+51.124065,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+51.483356,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+51.480328,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+51.450912,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+51.767189,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+51.802509,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+51.731052,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+51.026524,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+51.030212,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+51.070850,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+51.434658,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+51.380913,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+51.403625,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+51.752487,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+51.748566,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+51.691799,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+51.138458,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+51.132057,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+51.123486,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+51.480865,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+51.474991,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+51.399265,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+51.757946,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+51.783428,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+51.795933,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+51.038002,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+51.107018,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+51.089622,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+51.434860,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+51.373199,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+51.444897,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+51.757740,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+51.799534,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+51.787121,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+51.067806,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+51.060810,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+51.028580,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+51.427433,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+51.476231,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+51.485394,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+51.710197,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+51.715252,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+51.738682,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+51.138058,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+51.083279,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+51.108013,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+51.476528,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+51.474983,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+51.449673,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+51.694569,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+51.819393,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+51.705307,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+51.123283,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+51.073353,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+51.101631,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+51.444935,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+51.483742,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+51.411709,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+51.804104,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+51.807636,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+51.811527,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+52.146057,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+52.112904,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+52.107677,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+52.413246,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+52.448528,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+52.439362,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+52.770496,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+52.746460,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+52.787548,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+52.130978,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+52.077244,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+52.084179,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+52.455318,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+52.469521,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+52.376640,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+52.774586,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+52.744308,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+52.715279,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+52.063217,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+52.027222,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+52.034927,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+52.427879,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+52.397537,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+52.375614,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+52.749340,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+52.747417,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+52.795052,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+52.080704,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+52.050377,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+52.092632,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+52.448448,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+52.388504,+0.500000] , 1. [+0.000000,-1.280001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+52.376545,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+52.734222,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+52.785336,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+52.718651,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+52.079586,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+52.023907,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+52.078564,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+52.394817,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+52.412090,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+52.435902,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+52.786991,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+52.784309,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+52.788776,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+52.099876,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+52.134495,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+52.149227,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+52.474514,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+52.367504,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+52.374836,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+52.695297,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+52.799091,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+52.740726,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+52.133926,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+52.038795,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+52.082916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+52.353386,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+52.370533,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+52.432178,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+52.801041,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+52.792294,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+52.803272,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+52.069508,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+52.114250,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+52.089855,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+52.461704,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+52.435486,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+52.390480,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+52.712536,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+52.752079,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+52.739601,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+52.100281,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+52.038990,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+52.080070,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+52.457672,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+52.466881,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+52.375031,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+52.776203,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+52.760059,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+52.749352,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+52.130112,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+52.116524,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+52.075508,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+52.472267,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+52.412598,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+52.426620,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+52.770329,+0.500000] , 1. [+0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+52.797249,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+52.755424,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+52.032738,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+52.113850,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+52.125439,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+52.374916,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+52.398937,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+52.432194,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+52.743210,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+52.703701,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+52.760284,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+52.140163,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+52.109859,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+52.082611,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+52.363880,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+52.445183,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+52.369133,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+52.785881,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+52.775578,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+52.804432,+0.500000] , 1. [-0.000000,-1.280000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-2.txt b/scenes/cell-growth/results/particles-frame-2.txt deleted file mode 100644 index 7aeaef96..00000000 --- a/scenes/cell-growth/results/particles-frame-2.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+45.802944,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+45.904190,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+45.876522,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+46.132816,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+46.221092,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+46.132572,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+46.480152,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+46.568466,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+46.533466,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+45.887772,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+45.856586,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+45.906773,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+46.121101,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+46.153625,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+46.157360,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+46.572422,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+46.472229,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+46.537472,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+45.886803,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+45.849518,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+45.886986,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+46.130970,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+46.180599,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+46.122929,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+46.475616,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+46.538963,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+46.514446,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+45.877285,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+45.882263,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+45.830383,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+46.173103,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+46.229870,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+46.166687,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+46.504116,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+46.496716,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+46.467060,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+45.852230,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+45.874016,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+45.803440,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+46.171394,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+46.214767,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+46.163269,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+46.579994,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+46.511276,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+46.498508,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+45.797394,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+45.792107,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+45.860455,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+46.115593,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+46.173061,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+46.126278,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+46.506477,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+46.456902,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+46.472454,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+45.913033,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+45.833206,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+45.819019,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+46.221413,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+46.215340,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+46.187298,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+46.555801,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+46.491890,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+46.531540,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+45.787636,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+45.873913,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+45.904499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+46.143787,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+46.145145,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+46.123615,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+46.570518,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+46.466202,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+46.533772,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+45.836304,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+45.814682,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+45.818504,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+46.124493,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+46.114052,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+46.137833,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+46.528034,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+46.503113,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+46.517921,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+45.805908,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+45.820656,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+45.802891,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+46.241375,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+46.116268,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+46.222309,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+46.527363,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+46.481312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+46.497128,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+45.851368,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+45.894226,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+45.905922,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+46.210819,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+46.127945,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+46.157555,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+46.560822,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+46.546276,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+46.458492,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+45.866802,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+45.881874,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+45.851841,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+46.213688,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+46.176178,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+46.154491,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+46.525936,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+46.562908,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+46.453362,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+46.810925,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+46.878880,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+46.824337,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+47.172691,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+47.216110,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+47.143452,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+47.470478,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+47.527279,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+47.447594,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+46.860180,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+46.821026,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+46.783714,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+47.173225,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+47.179550,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+47.225895,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+47.496639,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+47.535515,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+47.505241,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+46.787991,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+46.823990,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+46.873726,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+47.231411,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+47.146866,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+47.119705,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+47.544903,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+47.474430,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+47.463440,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+46.818253,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+46.797508,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+46.797512,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+47.136318,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+47.220882,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+47.127998,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+47.505692,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+47.539486,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+47.520477,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+46.855446,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+46.792046,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+46.911552,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+47.167580,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+47.160812,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+47.144241,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+47.479389,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+47.572891,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+47.540436,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+46.802341,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+46.796993,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+46.818935,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+47.174603,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+47.242840,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+47.221825,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+47.561768,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+47.502148,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+47.453201,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+46.807671,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+46.827614,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+46.803856,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+47.137779,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+47.118080,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+47.232201,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+47.539417,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+47.547279,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+47.531830,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+46.831440,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+46.795254,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+46.790173,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+47.233574,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+47.237206,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+47.138062,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+47.541832,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+47.482292,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+47.542774,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+46.818985,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+46.886238,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+46.838654,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+47.134506,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+47.121410,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+47.245647,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+47.456230,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+47.449707,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+47.501865,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+46.807430,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+46.795666,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+46.906071,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+47.220215,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+47.182472,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+47.216961,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+47.480453,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+47.497509,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+47.515381,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+46.893562,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+46.831264,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+46.787273,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+47.178654,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+47.158733,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+47.174473,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+47.498955,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+47.557613,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+47.471481,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+46.812786,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+46.883644,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+46.843212,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+47.206203,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+47.210907,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+47.148209,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+47.512623,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+47.501846,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+47.474728,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+47.842819,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+47.802746,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+47.864876,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+48.204178,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+48.183079,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+48.238644,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+48.565144,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+48.450775,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+48.472240,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+47.832676,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+47.903042,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+47.906700,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+48.180191,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+48.123211,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+48.158241,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+48.526165,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+48.556046,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+48.475281,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+47.816925,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+47.800323,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+47.822945,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+48.232449,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+48.139565,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+48.114582,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+48.452370,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+48.458794,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+48.508930,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+47.804150,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+47.883358,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+47.883484,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+48.136120,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+48.243362,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+48.194710,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+48.558979,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+48.467392,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+48.523460,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+47.810749,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+47.897804,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+47.868919,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+48.225727,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+48.205551,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+48.134155,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+48.510445,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+48.522343,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+48.540344,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+47.810043,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+47.849102,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+47.789261,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+48.173717,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+48.128052,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+48.169205,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+48.501278,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+48.516338,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+48.464985,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+47.863819,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+47.804478,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+47.807293,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+48.152088,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+48.167774,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+48.234245,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+48.491421,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+48.578449,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+48.558315,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+47.895515,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+47.780548,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+47.912243,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+48.227329,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+48.125317,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+48.201065,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+48.495155,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+48.490654,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+48.529613,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+47.794968,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+47.823124,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+47.846748,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+48.236465,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+48.228050,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+48.155308,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+48.538185,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+48.534489,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+48.564144,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+47.910210,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+47.825619,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+47.847214,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+48.246494,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+48.126347,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+48.193115,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+48.481190,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+48.548794,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+48.449200,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+47.846424,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+47.780346,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+47.802284,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+48.158394,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+48.230965,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+48.203506,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+48.493107,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+48.520477,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+48.501457,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+47.818535,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+47.785461,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+47.897755,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+48.122669,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+48.244213,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+48.199207,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+48.497734,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+48.493370,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+48.505165,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+48.882103,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+48.843143,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+48.846684,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+49.232029,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+49.204918,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+49.122143,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+49.547897,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+49.459679,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+49.504280,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+48.910736,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+48.892597,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+48.795387,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+49.172375,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+49.155067,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+49.221771,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+49.534855,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+49.447208,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+49.453506,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+48.821655,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+48.846550,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+48.823246,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+49.190720,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+49.186069,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+49.188683,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+49.540325,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+49.557026,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+49.496914,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+48.835785,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+48.885273,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+48.869606,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+49.179485,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+49.210827,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+49.197147,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+49.449177,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+49.498940,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+49.558613,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+48.883923,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+48.837734,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+48.906635,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+49.178215,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+49.194057,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+49.116665,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+49.457390,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+49.561253,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+49.483135,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+48.837795,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+48.816422,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+48.862316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+49.245594,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+49.130089,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+49.158623,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+49.455696,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+49.537548,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+49.464420,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+48.819820,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+48.859341,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+48.816277,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+49.178322,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+49.118992,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+49.168842,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+49.518089,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+49.490341,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+49.505653,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+48.849926,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+48.797642,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+48.797680,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+49.213028,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+49.173832,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+49.224617,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+49.461712,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+49.569004,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+49.500114,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+48.802582,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+48.868370,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+48.879559,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+49.187679,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+49.126755,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+49.223892,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+49.575474,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+49.547825,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+49.553478,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+48.903023,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+48.871330,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+48.863571,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+49.231300,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+49.230820,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+49.170288,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+49.491741,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+49.493149,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+49.539333,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+48.852478,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+48.901176,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+48.811028,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+49.126530,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+49.158482,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+49.174973,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+49.509544,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+49.454754,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+49.542599,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+48.889225,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+48.879562,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+48.813721,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+49.185276,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+49.232635,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+49.187279,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+49.469017,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+49.539246,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+49.462765,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+49.863064,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+49.825409,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+49.889473,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+50.162197,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+50.171391,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+50.131611,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+50.523064,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+50.516663,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+50.473930,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+49.835064,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+49.810524,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+49.897449,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+50.210888,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+50.169056,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+50.155972,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+50.522472,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+50.506546,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+50.559635,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+49.862354,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+49.845078,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+49.793247,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+50.220634,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+50.197037,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+50.244110,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+50.475979,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+50.472553,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+50.549400,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+49.802650,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+49.817768,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+49.878353,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+50.245007,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+50.135639,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+50.196423,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+50.466854,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+50.515385,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+50.494068,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+49.818066,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+49.891956,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+49.912567,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+50.210842,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+50.158768,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+50.245922,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+50.464203,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+50.452271,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+50.520473,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+49.817760,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+49.870605,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+49.897079,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+50.149555,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+50.201660,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+50.173416,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+50.517616,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+50.520676,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+50.465958,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+49.794865,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+49.842525,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+49.851364,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+50.193115,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+50.136765,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+50.243717,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+50.467773,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+50.542271,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+50.484058,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+49.860367,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+49.911037,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+49.848206,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+50.151009,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+50.235966,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+50.157463,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+50.573330,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+50.563393,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+50.478264,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+49.795837,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+49.837944,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+49.785915,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+50.158657,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+50.138664,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+50.191162,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+50.471592,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+50.486126,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+50.556019,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+49.824432,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+49.858292,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+49.837727,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+50.186977,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+50.175488,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+50.149410,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+50.483498,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+50.526726,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+50.537590,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+49.866737,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+49.781227,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+49.807178,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+50.166153,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+50.188110,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+50.168964,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+50.526489,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+50.468964,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+50.555538,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+49.874802,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+49.878670,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+49.802670,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+50.131824,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+50.185612,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+50.113949,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+50.511543,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+50.566799,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+50.507473,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+50.892479,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+50.799820,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+50.909985,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+51.136913,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+51.121243,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+51.154503,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+51.522892,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+51.564522,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+51.486511,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+50.807316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+50.911087,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+50.828312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+51.164536,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+51.114857,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+51.117729,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+51.492798,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+51.476360,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+51.546627,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+50.849094,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+50.867752,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+50.873810,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+51.188576,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+51.200474,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+51.134010,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+51.463711,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+51.489311,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+51.543316,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+50.790604,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+50.854797,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+50.832207,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+51.121967,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+51.165524,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+51.233894,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+51.458977,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+51.451351,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+51.478802,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+50.787647,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+50.800171,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+50.813881,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+51.169903,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+51.137383,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+51.123806,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+51.449898,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+51.468018,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+51.493477,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+50.832462,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+50.889915,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+50.910831,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+51.113800,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+51.205654,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+51.223316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+51.568504,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+51.571106,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+51.573662,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+50.794312,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+50.791439,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+50.866913,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+51.147495,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+51.164040,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+51.176228,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+51.489231,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+51.562592,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+51.513691,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+50.837902,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+50.781677,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+50.869293,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+51.168350,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+51.117691,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+51.207333,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+51.527668,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+51.450237,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+51.492683,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+50.896698,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+50.858845,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+50.856270,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+51.209053,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+51.122665,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+51.125690,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+51.484413,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+51.520859,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+51.509212,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+50.893093,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+50.901287,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+50.814514,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+51.117584,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+51.129322,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+51.144119,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+51.456512,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+51.468975,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+51.519238,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+50.902634,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+50.829941,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+50.889565,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+51.216930,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+51.234570,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+51.219631,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+51.454082,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+51.564289,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+51.555710,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+50.892509,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+50.905720,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+50.851730,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+51.216446,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+51.209957,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+51.139774,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+51.481308,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+51.565639,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+51.548096,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+51.881496,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+51.871964,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+51.796505,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+52.235477,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+52.123497,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+52.167549,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+52.525780,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+52.558605,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+52.532318,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+51.853634,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+51.861454,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+51.866192,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+52.169048,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+52.174988,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+52.158253,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+52.465733,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+52.451797,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+52.502682,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+51.834320,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+51.850040,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+51.786774,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+52.154591,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+52.195087,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+52.239731,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+52.481979,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+52.465904,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+52.540226,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+51.806992,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+51.883888,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+51.896824,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+52.164265,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+52.167984,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+52.218609,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+52.535507,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+52.462753,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+52.510941,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+51.848412,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+51.783199,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+51.878643,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+52.240841,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+52.182465,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+52.240620,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+52.481659,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+52.553013,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+52.489872,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+51.876171,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+51.809631,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+51.887539,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+52.126686,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+52.208252,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+52.204548,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+52.561935,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+52.545334,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+52.490555,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+51.791561,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+51.854706,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+51.825203,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+52.243484,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+52.222103,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+52.196999,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+52.547985,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+52.551144,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+52.532425,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+51.825417,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+51.825905,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+51.851700,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+52.159489,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+52.126080,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+52.143570,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+52.541298,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+52.499847,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+52.543720,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+51.815636,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+51.877441,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+51.809967,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+52.216972,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+52.203094,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+52.236656,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+52.552811,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+52.513977,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+52.558311,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+51.852272,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+51.804672,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+51.847149,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+52.122658,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+52.183147,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+52.198681,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+52.508312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+52.460232,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+52.516727,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+51.906742,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+51.798454,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+51.813194,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+52.201771,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+52.131973,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+52.214588,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+52.475372,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+52.466148,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+52.503990,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+51.781601,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+51.789486,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+51.799999,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+52.211243,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+52.114635,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+52.211922,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+52.481850,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+52.486797,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+52.456337,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+52.911324,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+52.782845,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+52.912510,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+53.203552,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+53.199879,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+53.158520,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+53.526646,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+53.557877,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+53.454491,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+52.868206,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+52.845871,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+52.880825,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+53.177845,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+53.164875,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+53.157612,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+53.545944,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+53.467381,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+53.507698,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+52.897835,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+52.804306,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+52.833557,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+53.180267,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+53.191383,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+53.170193,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+53.492859,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+53.506622,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+53.495773,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+52.821602,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+52.907032,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+52.811214,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+53.180061,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+53.198360,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+53.148651,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+53.455486,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+53.549103,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+53.453415,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+52.791042,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+52.862709,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+52.847740,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+53.225967,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+53.125336,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+53.186188,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+53.541546,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+53.495121,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+53.576729,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+52.806698,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+52.882584,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+52.875153,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+53.168686,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+53.135563,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+53.183414,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+53.469849,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+53.552006,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+53.459457,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+52.817692,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+52.911148,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+52.873291,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+53.186829,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+53.142197,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+53.116188,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+53.525043,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+53.514210,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+53.494804,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+52.853928,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+52.871292,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+52.897633,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+53.149773,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+53.244049,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+53.171677,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+53.542946,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+53.459354,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+53.565117,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+52.833065,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+52.902470,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+52.848270,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+53.234364,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+53.165577,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+53.212559,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+53.486897,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+53.570412,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+53.528854,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+52.866768,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+52.893520,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+52.820560,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+53.135799,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+53.187538,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+53.232536,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+53.456528,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+53.539223,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+53.498337,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+52.804413,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+52.873928,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+52.863037,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+53.226967,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+53.229916,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+53.175793,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+53.532169,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+53.530499,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+53.556458,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+52.882755,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+52.842030,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+52.798725,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+53.134281,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+53.145241,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+53.177383,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+53.478268,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+53.477962,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+53.529411,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+53.806938,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+53.875629,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+53.797855,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+54.158478,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+54.182049,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+54.181355,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+54.493816,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+54.495281,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+54.551952,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+53.786087,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+53.815098,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+53.812588,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+54.196033,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+54.237106,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+54.147541,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+54.521847,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+54.453533,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+54.510525,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+53.832237,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+53.905052,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+53.884712,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+54.183342,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+54.225262,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+54.237003,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+54.484756,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+54.474430,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+54.451698,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+53.904411,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+53.807312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+53.795250,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+54.153492,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+54.207569,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+54.187908,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+54.493065,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+54.578220,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+54.533012,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+53.801846,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+53.824268,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+53.854557,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+54.206585,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+54.153900,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+54.198006,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+54.527100,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+54.482029,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+54.473515,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+53.823875,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+53.814259,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+53.893333,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+54.230507,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+54.139484,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+54.129826,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+54.481537,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+54.464985,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+54.449223,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+53.876976,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+53.858967,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+53.804821,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+54.196373,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+54.200207,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+54.176212,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+54.498062,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+54.467606,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+54.504601,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+53.847710,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+53.790859,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+53.793587,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+54.236965,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+54.148117,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+54.114826,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+54.562920,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+54.450611,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+54.544567,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+53.799244,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+53.872803,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+53.850151,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+54.205578,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+54.141563,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+54.186996,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+54.474220,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+54.512947,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+54.568836,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+53.800201,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+53.816830,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+53.875500,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+54.207649,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+54.225208,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+54.211567,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+54.471336,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+54.501518,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+54.559963,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+53.906609,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+53.876995,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+53.866924,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+54.182499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+54.245316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+54.169071,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+54.481396,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+54.467659,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+54.527473,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+53.790634,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+53.838367,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+53.850006,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+54.203400,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+54.122406,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+54.199780,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+54.454071,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+54.548561,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+54.551189,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+54.870953,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+54.871593,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+54.808365,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+55.166843,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+55.138435,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+55.187492,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+55.565456,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+55.450092,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+55.482567,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+54.784283,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+54.793491,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+54.904408,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+55.118301,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+55.206139,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+55.131905,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+55.553898,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+55.542995,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+55.525978,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+54.889858,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+54.874374,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+54.887192,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+55.150948,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+55.119797,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+55.150013,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+55.543129,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+55.546837,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+55.499531,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+54.853359,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+54.825459,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+54.802345,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+55.191765,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+55.143112,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+55.133209,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+55.488571,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+55.527153,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+55.494240,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+54.791885,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+54.794353,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+54.832729,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+55.192165,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+55.183857,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+55.178715,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+55.552570,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+55.534534,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+55.472839,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+54.791542,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+54.880775,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+54.862709,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+55.214115,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+55.191532,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+55.156105,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+55.562973,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+55.547234,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+55.500870,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+54.869129,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+54.794006,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+54.842335,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+55.187458,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+55.141285,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+55.151241,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+55.482723,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+55.545044,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+55.547474,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+54.895721,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+54.896477,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+54.889549,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+55.143707,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+55.209042,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+55.146210,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+55.492546,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+55.453117,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+55.500534,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+54.896385,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+54.855904,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+54.856449,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+55.125618,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+55.226025,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+55.217422,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+55.459995,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+55.570335,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+55.564468,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+54.888412,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+54.838299,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+54.800869,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+55.206211,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+55.245316,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+55.147636,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+55.551838,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+55.518738,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+55.505562,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+54.913239,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+54.876354,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+54.830902,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+55.238888,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+55.232407,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+55.235889,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+55.493553,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+55.447594,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+55.526711,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+54.825970,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+54.860443,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+54.786800,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+55.136005,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+55.212540,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+55.174896,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+55.543243,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+55.528996,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+55.571785,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+55.846611,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+55.808430,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+55.833813,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+56.119682,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+56.130569,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+56.163124,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+56.566479,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+56.467045,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+56.534164,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+55.790634,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+55.911629,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+55.784077,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+56.176975,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+56.202827,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+56.167797,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+56.553226,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+56.488449,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+56.557930,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+55.827969,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+55.863831,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+55.888573,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+56.214001,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+56.240620,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+56.185642,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+56.452557,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+56.541454,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+56.558922,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+55.809944,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+55.852470,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+55.892319,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+56.160698,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+56.126789,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+56.204704,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+56.533203,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+56.552452,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+56.449371,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+55.820759,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+55.864422,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+55.867916,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+56.126209,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+56.152985,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+56.124947,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+56.564541,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+56.539509,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+56.547672,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+55.819740,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+55.801563,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+55.822151,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+56.190376,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+56.232014,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+56.155464,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+56.470001,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+56.472843,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+56.559387,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+55.905373,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+55.826965,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+55.871460,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+56.239983,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+56.221066,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+56.117699,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+56.473186,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+56.509499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+56.532291,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+55.903496,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+55.888428,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+55.825802,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+56.193569,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+56.178715,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+56.231297,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+56.472900,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+56.536880,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+56.472488,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+55.789082,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+55.861210,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+55.847466,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+56.154892,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+56.187000,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+56.132843,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+56.458893,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+56.563107,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+56.457096,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+55.802902,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+55.861347,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+55.808968,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+56.192341,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+56.152843,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+56.181034,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+56.450375,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+56.537468,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+56.501381,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+55.819447,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+55.869053,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+55.824966,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+56.142735,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+56.246552,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+56.209904,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+56.468655,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+56.456852,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+56.567314,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+55.812916,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+55.818378,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+55.789215,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+56.172806,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+56.245499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+56.121422,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+56.481918,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+56.555187,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+56.548740,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+56.869923,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+56.852085,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+56.789513,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+57.128681,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+57.152218,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+57.138500,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+57.483734,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+57.466671,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+57.572163,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+56.886448,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+56.800011,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+56.802158,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+57.175293,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+57.195797,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+57.233868,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+57.578094,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+57.574116,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+57.524490,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+56.815479,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+56.823444,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+56.827362,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+57.215424,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+57.135029,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+57.209866,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+57.536236,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+57.528442,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+57.452129,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+56.864532,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+56.891373,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+56.818298,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+57.201832,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+57.216442,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+57.125481,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+57.478264,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+57.470676,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+57.446835,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+56.873055,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+56.888550,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+56.897495,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+57.202171,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+57.138897,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+57.232727,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+57.446903,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+57.579296,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+57.557919,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+56.900925,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+56.892345,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+56.884071,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+57.243359,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+57.240334,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+57.210915,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+57.527191,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+57.562515,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+57.491055,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+56.786526,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+56.790215,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+56.830853,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+57.194660,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+57.140915,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+57.163628,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+57.512489,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+57.508568,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+57.451801,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+56.898460,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+56.892059,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+56.883488,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+57.240868,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+57.234993,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+57.159267,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+57.517948,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+57.543430,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+57.555935,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+56.798004,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+56.867020,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+56.849625,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+57.194862,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+57.133202,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+57.204899,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+57.517742,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+57.559536,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+57.547123,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+56.827808,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+56.820812,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+56.788582,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+57.187435,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+57.236233,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+57.245396,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+57.470200,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+57.475254,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+57.498684,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+56.898060,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+56.843281,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+56.868015,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+57.236530,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+57.234985,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+57.209675,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+57.454571,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+57.579395,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+57.465309,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+56.883286,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+56.833355,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+56.861633,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+57.204937,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+57.243744,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+57.171711,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+57.564106,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+57.567638,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+57.571529,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+57.906059,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+57.872906,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+57.867680,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+58.173248,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+58.208530,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+58.199364,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+58.530499,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+58.506462,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+58.547550,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+57.890980,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+57.837246,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+57.844181,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+58.215321,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+58.229523,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+58.136642,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+58.534588,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+58.504311,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+58.475281,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+57.823219,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+57.787224,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+57.794930,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+58.187881,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+58.157539,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+58.135616,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+58.509342,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+58.507420,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+58.555054,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+57.840706,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+57.810379,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+57.852634,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+58.208450,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+58.148506,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+58.136547,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+58.494225,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+58.545338,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+58.478653,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+57.839588,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+57.783909,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+57.838566,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+58.154819,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+58.172092,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+58.195904,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+58.546993,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+58.544312,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+58.548779,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+57.859879,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+57.894497,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+57.909233,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+58.234516,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+58.127510,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+58.134838,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+58.455299,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+58.559093,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+58.500732,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+57.893929,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+57.798798,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+57.842918,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+58.113388,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+58.130535,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+58.192181,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+58.561043,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+58.552296,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+58.563274,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+57.829510,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+57.874252,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+57.849857,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+58.221706,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+58.195488,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+58.150482,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+58.472538,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+58.512081,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+58.499603,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+57.860283,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+57.798992,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+57.840069,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+58.217674,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+58.226883,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+58.135029,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+58.536205,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+58.520061,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+58.509350,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+57.890114,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+57.876522,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+57.835510,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+58.232269,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+58.172600,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+58.186623,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+58.530331,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+58.557251,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+58.515427,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+57.792740,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+57.873852,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+57.885441,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+58.134918,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+58.158939,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+58.192196,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+58.503212,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+58.463703,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+58.520287,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+57.900166,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+57.869862,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+57.842613,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+58.123882,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+58.205185,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+58.129135,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+58.545883,+0.500000] , 1. [+0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+58.535580,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+58.564434,+0.500000] , 1. [-0.000000,-0.320000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-3.txt b/scenes/cell-growth/results/particles-frame-3.txt deleted file mode 100644 index 2c1c0bc1..00000000 --- a/scenes/cell-growth/results/particles-frame-3.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+45.450943,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+45.552189,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+45.524521,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+45.780815,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+45.869091,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+45.780571,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+46.128151,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+46.216465,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+46.181465,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+45.535770,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+45.504585,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+45.554771,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+45.769100,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+45.801624,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+45.805359,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+46.220421,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+46.120228,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+46.185471,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+45.534801,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+45.497517,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+45.534985,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+45.778969,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+45.828598,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+45.770927,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+46.123615,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+46.186962,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+46.162445,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+45.525284,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+45.530262,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+45.478382,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+45.821102,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+45.877869,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+45.814686,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+46.152115,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+46.144714,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+46.115059,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+45.500229,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+45.522015,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+45.451439,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+45.819393,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+45.862766,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+45.811268,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+46.227993,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+46.159275,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+46.146507,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+45.445393,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+45.440105,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+45.508453,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+45.763592,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+45.821060,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+45.774277,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+46.154476,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+46.104900,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+46.120453,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+45.561031,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+45.481205,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+45.467018,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+45.869411,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+45.863338,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+45.835297,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+46.203800,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+46.139889,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+46.179539,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+45.435635,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+45.521912,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+45.552498,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+45.791786,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+45.793144,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+45.771614,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+46.218517,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+46.114201,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+46.181770,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+45.484303,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+45.462681,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+45.466503,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+45.772491,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+45.762051,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+45.785831,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+46.176033,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+46.151112,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+46.165920,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+45.453907,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+45.468655,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+45.450890,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+45.889374,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+45.764267,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+45.870308,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+46.175362,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+46.129311,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+46.145126,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+45.499367,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+45.542225,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+45.553921,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+45.858818,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+45.775944,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+45.805553,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+46.208820,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+46.194275,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+46.106491,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+45.514801,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+45.529873,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+45.499840,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+45.861687,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+45.824177,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+45.802490,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+46.173935,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+46.210907,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+46.101360,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+46.458923,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+46.526878,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+46.472336,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+46.820690,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+46.864109,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+46.791451,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+47.118477,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+47.175278,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+47.095592,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+46.508179,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+46.469025,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+46.431713,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+46.821224,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+46.827549,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+46.873894,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+47.144638,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+47.183514,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+47.153240,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+46.435989,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+46.471989,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+46.521725,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+46.879410,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+46.794865,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+46.767704,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+47.192902,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+47.122429,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+47.111439,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+46.466251,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+46.445507,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+46.445511,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+46.784317,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+46.868881,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+46.775997,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+47.153690,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+47.187485,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+47.168476,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+46.503445,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+46.440044,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+46.559551,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+46.815578,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+46.808811,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+46.792240,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+47.127388,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+47.220890,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+47.188435,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+46.450340,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+46.444992,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+46.466934,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+46.822601,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+46.890839,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+46.869823,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+47.209766,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+47.150146,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+47.101200,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+46.455669,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+46.475613,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+46.451855,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+46.785778,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+46.766079,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+46.880199,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+47.187416,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+47.195278,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+47.179829,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+46.479439,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+46.443253,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+46.438171,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+46.881573,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+46.885204,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+46.786060,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+47.189831,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+47.130291,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+47.190773,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+46.466984,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+46.534237,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+46.486652,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+46.782505,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+46.769409,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+46.893646,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+47.104229,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+47.097706,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+47.149864,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+46.455429,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+46.443665,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+46.554070,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+46.868214,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+46.830471,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+46.864960,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+47.128452,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+47.145508,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+47.163380,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+46.541561,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+46.479263,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+46.435272,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+46.826653,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+46.806732,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+46.822472,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+47.146954,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+47.205612,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+47.119480,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+46.460785,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+46.531643,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+46.491211,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+46.854202,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+46.858906,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+46.796207,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+47.160622,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+47.149845,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+47.122726,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+47.490818,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+47.450745,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+47.512875,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+47.852177,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+47.831078,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+47.886642,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+48.213142,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+48.098774,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+48.120239,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+47.480675,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+47.551041,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+47.554699,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+47.828190,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+47.771210,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+47.806240,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+48.174164,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+48.204044,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+48.123280,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+47.464924,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+47.448322,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+47.470943,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+47.880447,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+47.787563,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+47.762581,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+48.100368,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+48.106792,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+48.156929,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+47.452148,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+47.531357,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+47.531483,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+47.784119,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+47.891361,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+47.842709,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+48.206978,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+48.115391,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+48.171459,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+47.458748,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+47.545803,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+47.516918,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+47.873726,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+47.853550,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+47.782154,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+48.158443,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+48.170341,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+48.188343,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+47.458042,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+47.497101,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+47.437260,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+47.821716,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+47.776051,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+47.817204,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+48.149277,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+48.164337,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+48.112984,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+47.511818,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+47.452477,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+47.455292,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+47.800087,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+47.815773,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+47.882244,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+48.139420,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+48.226448,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+48.206314,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+47.543514,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+47.428547,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+47.560242,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+47.875328,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+47.773315,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+47.849064,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+48.143154,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+48.138653,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+48.177612,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+47.442966,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+47.471123,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+47.494747,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+47.884464,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+47.876049,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+47.803307,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+48.186184,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+48.182487,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+48.212143,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+47.558208,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+47.473618,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+47.495213,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+47.894493,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+47.774345,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+47.841114,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+48.129189,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+48.196793,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+48.097198,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+47.494423,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+47.428345,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+47.450283,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+47.806393,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+47.878963,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+47.851505,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+48.141106,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+48.168476,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+48.149456,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+47.466534,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+47.433460,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+47.545753,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+47.770668,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+47.892212,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+47.847206,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+48.145733,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+48.141369,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+48.153164,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+48.530102,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+48.491142,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+48.494682,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+48.880028,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+48.852917,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+48.770142,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+49.195896,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+49.107677,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+49.152279,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+48.558735,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+48.540596,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+48.443386,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+48.820374,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+48.803066,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+48.869770,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+49.182854,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+49.095207,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+49.101505,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+48.469654,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+48.494549,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+48.471245,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+48.838718,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+48.834068,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+48.836681,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+49.188324,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+49.205025,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+49.144913,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+48.483784,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+48.533272,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+48.517605,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+48.827484,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+48.858826,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+48.845146,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+49.097176,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+49.146938,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+49.206612,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+48.531921,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+48.485733,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+48.554634,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+48.826214,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+48.842056,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+48.764664,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+49.105389,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+49.209251,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+49.131134,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+48.485794,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+48.464420,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+48.510315,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+48.893593,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+48.778088,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+48.806622,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+49.103695,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+49.185547,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+49.112419,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+48.467819,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+48.507339,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+48.464275,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+48.826321,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+48.766991,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+48.816841,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+49.166088,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+49.138340,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+49.153652,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+48.497925,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+48.445641,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+48.445679,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+48.861027,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+48.821831,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+48.872616,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+49.109711,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+49.217003,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+49.148113,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+48.450581,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+48.516369,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+48.527557,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+48.835678,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+48.774754,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+48.871891,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+49.223473,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+49.195824,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+49.201477,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+48.551022,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+48.519329,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+48.511570,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+48.879299,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+48.878819,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+48.818287,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+49.139740,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+49.141148,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+49.187332,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+48.500477,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+48.549175,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+48.459026,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+48.774529,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+48.806480,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+48.822971,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+49.157543,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+49.102753,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+49.190598,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+48.537224,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+48.527561,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+48.461720,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+48.833275,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+48.880634,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+48.835278,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+49.117016,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+49.187244,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+49.110764,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+49.511063,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+49.473408,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+49.537472,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+49.810196,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+49.819389,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+49.779610,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+50.171062,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+50.164661,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+50.121929,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+49.483063,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+49.458523,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+49.545448,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+49.858887,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+49.817055,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+49.803970,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+50.170471,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+50.154545,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+50.207634,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+49.510353,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+49.493076,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+49.441246,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+49.868633,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+49.845036,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+49.892109,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+50.123978,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+50.120552,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+50.197399,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+49.450649,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+49.465767,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+49.526352,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+49.893005,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+49.783638,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+49.844421,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+50.114853,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+50.163383,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+50.142067,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+49.466064,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+49.539955,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+49.560566,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+49.858841,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+49.806767,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+49.893921,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+50.112202,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+50.100269,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+50.168472,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+49.465759,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+49.518604,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+49.545078,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+49.797554,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+49.849659,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+49.821415,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+50.165615,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+50.168674,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+50.113956,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+49.442863,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+49.490524,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+49.499363,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+49.841114,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+49.784763,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+49.891716,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+50.115772,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+50.190269,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+50.132057,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+49.508366,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+49.559036,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+49.496204,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+49.799007,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+49.883965,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+49.805462,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+50.221329,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+50.211391,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+50.126263,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+49.443836,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+49.485943,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+49.433914,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+49.806656,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+49.786663,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+49.839161,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+50.119591,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+50.134125,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+50.204018,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+49.472431,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+49.506290,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+49.485725,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+49.834976,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+49.823486,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+49.797409,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+50.131496,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+50.174725,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+50.185589,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+49.514736,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+49.429226,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+49.455177,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+49.814152,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+49.836109,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+49.816963,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+50.174488,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+50.116962,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+50.203537,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+49.522800,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+49.526669,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+49.450668,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+49.779823,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+49.833611,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+49.761948,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+50.159542,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+50.214798,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+50.155472,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+50.540478,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+50.447819,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+50.557983,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+50.784912,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+50.769241,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+50.802502,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+51.170891,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+51.212521,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+51.134510,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+50.455315,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+50.559086,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+50.476311,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+50.812534,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+50.762856,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+50.765728,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+51.140797,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+51.124359,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+51.194626,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+50.497093,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+50.515751,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+50.521809,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+50.836575,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+50.848473,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+50.782009,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+51.111710,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+51.137310,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+51.191315,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+50.438602,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+50.502796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+50.480206,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+50.769966,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+50.813522,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+50.881893,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+51.106976,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+51.099350,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+51.126801,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+50.435646,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+50.448170,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+50.461880,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+50.817902,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+50.785381,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+50.771805,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+51.097897,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+51.116016,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+51.141476,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+50.480461,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+50.537914,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+50.558830,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+50.761799,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+50.853653,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+50.871315,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+51.216503,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+51.219105,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+51.221661,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+50.442310,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+50.439438,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+50.514912,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+50.795494,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+50.812038,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+50.824226,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+51.137230,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+51.210590,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+51.161690,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+50.485901,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+50.429676,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+50.517292,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+50.816349,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+50.765690,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+50.855331,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+51.175667,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+51.098236,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+51.140682,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+50.544697,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+50.506844,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+50.504269,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+50.857052,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+50.770664,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+50.773689,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+51.132412,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+51.168858,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+51.157211,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+50.541092,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+50.549286,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+50.462513,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+50.765583,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+50.777321,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+50.792118,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+51.104511,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+51.116974,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+51.167236,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+50.550632,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+50.477940,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+50.537563,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+50.864929,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+50.882568,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+50.867630,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+51.102081,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+51.212288,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+51.203709,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+50.540508,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+50.553719,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+50.499729,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+50.864445,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+50.857956,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+50.787773,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+51.129307,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+51.213638,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+51.196095,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+51.529495,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+51.519962,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+51.444504,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+51.883476,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+51.771496,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+51.815548,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+52.173779,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+52.206604,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+52.180317,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+51.501633,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+51.509453,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+51.514191,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+51.817047,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+51.822987,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+51.806252,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+52.113731,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+52.099796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+52.150681,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+51.482319,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+51.498039,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+51.434772,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+51.802589,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+51.843086,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+51.887730,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+52.129978,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+52.113903,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+52.188225,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+51.454990,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+51.531887,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+51.544823,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+51.812263,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+51.815983,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+51.866608,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+52.183506,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+52.110752,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+52.158939,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+51.496410,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+51.431198,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+51.526642,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+51.888840,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+51.830463,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+51.888618,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+52.129658,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+52.201012,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+52.137871,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+51.524170,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+51.457630,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+51.535538,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+51.774685,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+51.856251,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+51.852547,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+52.209934,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+52.193333,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+52.138554,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+51.439560,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+51.502705,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+51.473202,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+51.891483,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+51.870102,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+51.844997,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+52.195984,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+52.199142,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+52.180424,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+51.473415,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+51.473904,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+51.499699,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+51.807487,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+51.774078,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+51.791569,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+52.189297,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+52.147846,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+52.191719,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+51.463634,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+51.525440,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+51.457966,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+51.864971,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+51.851093,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+51.884655,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+52.200809,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+52.161976,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+52.206310,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+51.500271,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+51.452671,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+51.495148,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+51.770657,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+51.831146,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+51.846680,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+52.156311,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+52.108231,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+52.164726,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+51.554741,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+51.446453,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+51.461193,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+51.849770,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+51.779972,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+51.862587,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+52.123371,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+52.114147,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+52.151989,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+51.429600,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+51.437485,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+51.447998,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+51.859241,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+51.762634,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+51.859921,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+52.129848,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+52.134796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+52.104336,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+52.559322,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+52.430843,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+52.560509,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+52.851551,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+52.847878,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+52.806519,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+53.174644,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+53.205875,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+53.102489,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+52.516205,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+52.493870,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+52.528824,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+52.825844,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+52.812874,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+52.805611,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+53.193943,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+53.115379,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+53.155697,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+52.545834,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+52.452305,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+52.481556,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+52.828266,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+52.839382,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+52.818192,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+53.140858,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+53.154621,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+53.143772,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+52.469601,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+52.555031,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+52.459213,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+52.828060,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+52.846359,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+52.796650,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+53.103485,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+53.197102,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+53.101414,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+52.439041,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+52.510708,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+52.495739,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+52.873966,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+52.773335,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+52.834187,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+53.189545,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+53.143120,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+53.224728,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+52.454697,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+52.530582,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+52.523151,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+52.816685,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+52.783562,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+52.831413,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+53.117847,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+53.200005,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+53.107456,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+52.465691,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+52.559147,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+52.521290,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+52.834827,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+52.790195,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+52.764187,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+53.173042,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+53.162209,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+53.142803,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+52.501926,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+52.519291,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+52.545631,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+52.797771,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+52.892048,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+52.819675,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+53.190945,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+53.107353,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+53.213116,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+52.481064,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+52.550468,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+52.496269,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+52.882362,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+52.813576,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+52.860558,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+53.134895,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+53.218410,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+53.176853,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+52.514767,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+52.541519,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+52.468559,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+52.783798,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+52.835537,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+52.880535,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+53.104527,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+53.187222,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+53.146336,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+52.452412,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+52.521927,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+52.511036,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+52.874966,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+52.877914,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+52.823792,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+53.180168,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+53.178497,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+53.204456,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+52.530754,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+52.490028,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+52.446724,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+52.782280,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+52.793240,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+52.825382,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+53.126266,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+53.125961,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+53.177410,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+53.454937,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+53.523628,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+53.445854,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+53.806477,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+53.830048,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+53.829353,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+54.141815,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+54.143280,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+54.199951,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+53.434086,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+53.463097,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+53.460587,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+53.844032,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+53.885105,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+53.795540,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+54.169846,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+54.101532,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+54.158524,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+53.480236,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+53.553051,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+53.532711,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+53.831341,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+53.873260,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+53.885002,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+54.132755,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+54.122429,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+54.099697,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+53.552410,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+53.455311,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+53.443249,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+53.801491,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+53.855568,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+53.835907,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+54.141064,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+54.226219,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+54.181011,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+53.449844,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+53.472267,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+53.502556,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+53.854584,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+53.801899,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+53.846004,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+54.175098,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+54.130028,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+54.121513,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+53.471874,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+53.462257,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+53.541332,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+53.878506,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+53.787483,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+53.777824,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+54.129536,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+54.112984,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+54.097221,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+53.524975,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+53.506966,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+53.452820,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+53.844372,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+53.848206,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+53.824211,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+54.146061,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+54.115604,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+54.152599,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+53.495708,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+53.438858,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+53.441586,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+53.884964,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+53.796116,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+53.762825,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+54.210918,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+54.098610,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+54.192566,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+53.447243,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+53.520802,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+53.498150,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+53.853577,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+53.789562,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+53.834995,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+54.122219,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+54.160946,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+54.216835,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+53.448200,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+53.464828,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+53.523499,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+53.855648,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+53.873207,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+53.859566,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+54.119335,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+54.149517,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+54.207962,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+53.554607,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+53.524994,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+53.514923,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+53.830498,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+53.893314,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+53.817070,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+54.129395,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+54.115658,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+54.175472,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+53.438633,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+53.486366,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+53.498005,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+53.851398,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+53.770405,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+53.847778,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+54.102070,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+54.196560,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+54.199188,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+54.518951,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+54.519592,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+54.456364,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+54.814842,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+54.786434,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+54.835491,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+55.213455,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+55.098091,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+55.130566,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+54.432281,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+54.441490,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+54.552406,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+54.766300,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+54.854137,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+54.779903,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+55.201897,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+55.190994,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+55.173977,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+54.537857,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+54.522373,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+54.535191,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+54.798946,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+54.767796,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+54.798012,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+55.191128,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+55.194836,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+55.147530,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+54.501358,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+54.473457,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+54.450344,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+54.839764,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+54.791111,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+54.781208,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+55.136570,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+55.175152,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+55.142239,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+54.439884,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+54.442352,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+54.480728,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+54.840164,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+54.831856,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+54.826714,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+55.200569,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+55.182533,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+55.120838,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+54.439541,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+54.528774,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+54.510708,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+54.862114,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+54.839531,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+54.804104,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+55.210972,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+55.195232,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+55.148869,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+54.517128,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+54.442005,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+54.490334,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+54.835457,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+54.789284,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+54.799240,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+55.130722,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+55.193043,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+55.195473,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+54.543720,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+54.544476,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+54.537548,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+54.791706,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+54.857040,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+54.794209,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+55.140545,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+55.101116,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+55.148533,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+54.544384,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+54.503902,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+54.504448,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+54.773617,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+54.874023,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+54.865421,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+55.107994,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+55.218334,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+55.212467,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+54.536411,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+54.486298,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+54.448868,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+54.854210,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+54.893314,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+54.795635,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+55.199837,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+55.166737,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+55.153561,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+54.561237,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+54.524353,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+54.478901,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+54.886887,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+54.880405,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+54.883888,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+55.141552,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+55.095592,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+55.174709,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+54.473969,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+54.508442,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+54.434799,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+54.784004,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+54.860538,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+54.822895,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+55.191242,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+55.176994,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+55.219784,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+55.494610,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+55.456429,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+55.481812,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+55.767681,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+55.778568,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+55.811123,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+56.214478,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+56.115044,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+56.182163,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+55.438633,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+55.559628,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+55.432076,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+55.824974,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+55.850826,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+55.815796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+56.201225,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+56.136448,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+56.205929,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+55.475967,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+55.511829,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+55.536572,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+55.862000,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+55.888618,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+55.833641,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+56.100555,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+56.189453,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+56.206921,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+55.457943,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+55.500469,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+55.540318,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+55.808697,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+55.774788,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+55.852703,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+56.181202,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+56.200451,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+56.097370,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+55.468758,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+55.512421,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+55.515915,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+55.774208,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+55.800983,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+55.772945,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+56.212540,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+56.187508,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+56.195671,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+55.467739,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+55.449562,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+55.470150,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+55.838375,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+55.880013,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+55.803463,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+56.118000,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+56.120842,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+56.207386,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+55.553371,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+55.474964,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+55.519459,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+55.887981,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+55.869064,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+55.765697,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+56.121185,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+56.157497,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+56.180290,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+55.551495,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+55.536427,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+55.473801,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+55.841568,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+55.826714,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+55.879295,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+56.120899,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+56.184879,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+56.120487,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+55.437080,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+55.509209,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+55.495464,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+55.802891,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+55.834999,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+55.780842,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+56.106892,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+56.211105,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+56.105095,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+55.450901,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+55.509346,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+55.456966,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+55.840340,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+55.800842,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+55.829033,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+56.098373,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+56.185467,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+56.149380,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+55.467445,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+55.517052,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+55.472965,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+55.790733,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+55.894550,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+55.857903,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+56.116653,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+56.104851,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+56.215313,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+55.460915,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+55.466377,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+55.437214,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+55.820805,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+55.893497,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+55.769421,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+56.129917,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+56.203186,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+56.196739,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+56.517921,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+56.500084,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+56.437511,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+56.776680,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+56.800217,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+56.786499,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+57.131733,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+57.114670,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+57.220161,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+56.534447,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+56.448009,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+56.450157,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+56.823292,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+56.843796,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+56.881866,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+57.226093,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+57.222115,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+57.172489,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+56.463478,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+56.471443,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+56.475361,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+56.863422,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+56.783028,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+56.857864,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+57.184235,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+57.176441,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+57.100128,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+56.512531,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+56.539371,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+56.466297,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+56.849831,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+56.864441,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+56.773479,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+57.126263,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+57.118675,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+57.094833,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+56.521053,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+56.536549,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+56.545494,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+56.850170,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+56.786896,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+56.880726,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+57.094902,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+57.227295,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+57.205917,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+56.548923,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+56.540344,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+56.532070,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+56.891357,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+56.888332,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+56.858913,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+57.175190,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+57.210514,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+57.139053,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+56.434525,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+56.438213,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+56.478851,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+56.842659,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+56.788914,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+56.811626,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+57.160488,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+57.156567,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+57.099800,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+56.546459,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+56.540058,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+56.531487,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+56.888866,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+56.882992,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+56.807266,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+57.165947,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+57.191429,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+57.203934,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+56.446003,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+56.515018,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+56.497623,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+56.842861,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+56.781200,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+56.852898,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+57.165741,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+57.207535,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+57.195122,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+56.475807,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+56.468811,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+56.436581,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+56.835434,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+56.884232,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+56.893394,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+57.118198,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+57.123253,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+57.146683,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+56.546059,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+56.491280,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+56.516014,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+56.884529,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+56.882984,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+56.857674,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+57.102570,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+57.227394,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+57.113308,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+56.531284,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+56.481354,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+56.509632,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+56.852936,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+56.891743,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+56.819710,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+57.212105,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+57.215637,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+57.219528,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+57.554058,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+57.520905,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+57.515678,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+57.821247,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+57.856529,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+57.847363,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+58.178497,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+58.154461,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+58.195549,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+57.538979,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+57.485245,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+57.492180,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+57.863319,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+57.877522,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+57.784641,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+58.182587,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+58.152309,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+58.123280,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+57.471218,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+57.435223,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+57.442928,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+57.835880,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+57.805538,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+57.783615,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+58.157341,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+58.155418,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+58.203053,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+57.488705,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+57.458378,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+57.500633,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+57.856449,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+57.796505,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+57.784546,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+58.142223,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+58.193336,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+58.126652,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+57.487587,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+57.431908,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+57.486565,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+57.802818,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+57.820091,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+57.843903,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+58.194992,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+58.192310,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+58.196777,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+57.507877,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+57.542496,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+57.557232,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+57.882515,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+57.775509,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+57.782837,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+58.103298,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+58.207092,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+58.148731,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+57.541927,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+57.446796,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+57.490917,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+57.761387,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+57.778534,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+57.840179,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+58.209042,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+58.200294,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+58.211273,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+57.477509,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+57.522251,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+57.497856,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+57.869705,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+57.843487,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+57.798481,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+58.120537,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+58.160080,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+58.147602,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+57.508282,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+57.446991,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+57.488068,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+57.865673,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+57.874882,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+57.783028,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+58.184204,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+58.168060,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+58.157349,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+57.538113,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+57.524521,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+57.483509,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+57.880268,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+57.820599,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+57.834621,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+58.178329,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+58.205250,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+58.163425,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+57.440739,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+57.521851,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+57.533440,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+57.782917,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+57.806938,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+57.840195,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+58.151211,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+58.111702,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+58.168285,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+57.548164,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+57.517860,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+57.490612,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+57.771881,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+57.853184,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+57.777134,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+58.193882,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+58.183578,+0.500000] , 1. [+0.000000,-0.448000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+58.212433,+0.500000] , 1. [-0.000000,-0.448000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-4.txt b/scenes/cell-growth/results/particles-frame-4.txt deleted file mode 100644 index 7178f2d4..00000000 --- a/scenes/cell-growth/results/particles-frame-4.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+44.970943,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+45.072189,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+45.044521,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+45.300816,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+45.389091,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+45.300571,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+45.648151,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+45.736465,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+45.701466,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+45.055771,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+45.024586,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+45.074772,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+45.289101,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+45.321625,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+45.325359,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+45.740421,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+45.640228,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+45.705471,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+45.054802,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+45.017517,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+45.054985,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+45.298969,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+45.348598,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+45.290928,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+45.643616,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+45.706963,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+45.682446,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+45.045284,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+45.050262,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+44.998383,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+45.341103,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+45.397869,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+45.334686,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+45.672115,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+45.664715,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+45.635059,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+45.020229,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+45.042015,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+44.971439,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+45.339394,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+45.382767,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+45.331268,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+45.747993,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+45.679276,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+45.666508,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+44.965393,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+44.960106,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+45.028454,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+45.283592,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+45.341061,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+45.294277,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+45.674477,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+45.624901,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+45.640453,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+45.081032,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+45.001205,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+44.987019,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+45.389412,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+45.383339,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+45.355297,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+45.723801,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+45.659889,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+45.699539,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+44.955635,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+45.041912,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+45.072498,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+45.311787,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+45.313145,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+45.291615,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+45.738518,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+45.634201,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+45.701771,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+45.004303,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+44.982681,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+44.986504,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+45.292492,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+45.282051,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+45.305832,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+45.696033,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+45.671112,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+45.685921,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+44.973907,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+44.988655,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+44.970890,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+45.409374,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+45.284267,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+45.390308,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+45.695362,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+45.649311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+45.665127,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+45.019367,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+45.062225,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+45.073921,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+45.378819,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+45.295944,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+45.325554,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+45.728821,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+45.714275,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+45.626492,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+45.034801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+45.049873,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+45.019840,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+45.381687,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+45.344177,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+45.322491,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+45.693935,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+45.730907,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+45.621361,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+45.978924,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+46.046879,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+45.992336,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+46.340691,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+46.384109,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+46.311451,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+46.638477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+46.695278,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+46.615593,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+46.028179,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+45.989025,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+45.951714,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+46.341225,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+46.347549,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+46.393894,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+46.664639,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+46.703514,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+46.673241,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+45.955990,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+45.991989,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+46.041725,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+46.399410,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+46.314865,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+46.287704,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+46.712902,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+46.642429,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+46.631439,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+45.986252,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+45.965508,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+45.965511,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+46.304317,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+46.388882,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+46.295998,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+46.673691,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+46.707485,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+46.688477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+46.023445,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+45.960045,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+46.079552,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+46.335579,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+46.328812,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+46.312241,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+46.647388,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+46.740891,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+46.708435,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+45.970341,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+45.964993,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+45.986935,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+46.342602,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+46.410839,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+46.389824,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+46.729767,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+46.670147,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+46.621201,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+45.975670,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+45.995613,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+45.971855,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+46.305779,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+46.286079,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+46.400200,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+46.707417,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+46.715279,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+46.699829,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+45.999439,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+45.963253,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+45.958172,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+46.401573,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+46.405205,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+46.306061,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+46.709831,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+46.650291,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+46.710773,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+45.986984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+46.054237,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+46.006653,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+46.302505,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+46.289410,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+46.413647,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+46.624229,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+46.617706,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+46.669865,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+45.975430,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+45.963665,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+46.074070,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+46.388214,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+46.350471,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+46.384960,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+46.648453,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+46.665508,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+46.683380,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+46.061562,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+45.999264,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+45.955273,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+46.346653,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+46.326733,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+46.342472,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+46.666954,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+46.725613,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+46.639481,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+45.980785,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+46.051643,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+46.011211,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+46.374203,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+46.378906,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+46.316208,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+46.680622,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+46.669846,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+46.642727,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+47.010818,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+46.970745,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+47.032875,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+47.372177,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+47.351078,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+47.406643,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+47.733143,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+47.618774,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+47.640240,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+47.000675,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+47.071041,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+47.074699,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+47.348190,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+47.291210,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+47.326241,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+47.694164,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+47.724045,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+47.643280,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+46.984924,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+46.968323,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+46.990944,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+47.400448,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+47.307564,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+47.282581,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+47.620369,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+47.626793,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+47.676929,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+46.972149,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+47.051357,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+47.051483,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+47.304119,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+47.411362,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+47.362709,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+47.726978,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+47.635391,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+47.691460,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+46.978748,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+47.065804,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+47.036919,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+47.393726,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+47.373550,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+47.302155,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+47.678444,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+47.690342,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+47.708344,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+46.978043,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+47.017101,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+46.957260,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+47.341717,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+47.296051,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+47.337204,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+47.669277,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+47.684338,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+47.632984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+47.031818,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+46.972477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+46.975292,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+47.320087,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+47.335773,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+47.402245,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+47.659420,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+47.746449,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+47.726315,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+47.063515,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+46.948547,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+47.080242,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+47.395329,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+47.293316,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+47.369064,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+47.663155,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+47.658653,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+47.697613,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+46.962967,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+46.991123,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+47.014748,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+47.404465,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+47.396049,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+47.323307,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+47.706184,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+47.702488,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+47.732143,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+47.078209,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+46.993618,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+47.015213,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+47.414494,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+47.294346,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+47.361115,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+47.649189,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+47.716793,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+47.617199,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+47.014423,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+46.948345,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+46.970284,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+47.326393,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+47.398964,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+47.371506,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+47.661106,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+47.688477,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+47.669456,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+46.986534,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+46.953461,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+47.065754,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+47.290668,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+47.412212,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+47.367207,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+47.665733,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+47.661369,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+47.673164,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+48.050102,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+48.011143,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+48.014683,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+48.400028,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+48.372917,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+48.290142,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+48.715897,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+48.627678,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+48.672279,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+48.078735,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+48.060596,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+47.963387,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+48.340374,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+48.323067,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+48.389771,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+48.702854,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+48.615208,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+48.621506,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+47.989655,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+48.014549,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+47.991245,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+48.358719,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+48.354069,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+48.356682,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+48.708324,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+48.725025,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+48.664913,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+48.003784,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+48.053272,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+48.037605,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+48.347485,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+48.378826,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+48.365147,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+48.617176,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+48.666939,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+48.726612,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+48.051922,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+48.005733,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+48.074635,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+48.346214,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+48.362057,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+48.284664,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+48.625389,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+48.729252,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+48.651134,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+48.005795,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+47.984421,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+48.030315,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+48.413593,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+48.298088,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+48.326622,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+48.623695,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+48.705547,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+48.632420,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+47.987820,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+48.027340,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+47.984276,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+48.346321,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+48.286991,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+48.336842,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+48.686089,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+48.658340,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+48.673653,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+48.017925,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+47.965641,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+47.965679,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+48.381027,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+48.341831,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+48.392616,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+48.629711,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+48.737003,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+48.668114,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+47.970581,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+48.036369,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+48.047558,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+48.355679,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+48.294754,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+48.391891,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+48.743473,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+48.715824,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+48.721478,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+48.071022,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+48.039330,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+48.031570,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+48.399300,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+48.398819,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+48.338287,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+48.659740,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+48.661148,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+48.707333,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+48.020477,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+48.069176,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+47.979027,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+48.294529,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+48.326481,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+48.342972,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+48.677544,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+48.622753,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+48.710598,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+48.057224,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+48.047562,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+47.981720,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+48.353275,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+48.400635,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+48.355278,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+48.637016,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+48.707245,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+48.630764,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+49.031063,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+48.993408,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+49.057472,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+49.330196,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+49.339390,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+49.299610,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+49.691063,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+49.684662,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+49.641930,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+49.003063,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+48.978523,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+49.065449,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+49.378887,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+49.337055,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+49.323971,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+49.690472,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+49.674545,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+49.727634,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+49.030354,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+49.013077,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+48.961246,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+49.388634,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+49.365036,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+49.412109,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+49.643978,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+49.640553,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+49.717400,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+48.970650,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+48.985767,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+49.046352,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+49.413006,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+49.303638,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+49.364422,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+49.634853,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+49.683384,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+49.662067,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+48.986065,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+49.059956,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+49.080566,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+49.378841,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+49.326767,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+49.413921,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+49.632202,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+49.620270,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+49.688473,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+48.985760,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+49.038605,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+49.065079,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+49.317554,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+49.369659,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+49.341415,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+49.685616,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+49.688675,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+49.633957,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+48.962864,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+49.010525,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+49.019363,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+49.361115,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+49.304764,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+49.411716,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+49.635773,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+49.710270,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+49.652058,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+49.028366,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+49.079037,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+49.016205,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+49.319008,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+49.403965,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+49.325462,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+49.741329,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+49.731392,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+49.646263,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+48.963837,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+49.005943,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+48.953915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+49.326656,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+49.306664,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+49.359161,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+49.639591,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+49.654125,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+49.724018,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+48.992432,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+49.026291,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+49.005726,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+49.354977,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+49.343487,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+49.317410,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+49.651497,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+49.694725,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+49.705589,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+49.034737,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+48.949226,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+48.975178,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+49.334152,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+49.356110,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+49.336964,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+49.694489,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+49.636963,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+49.723537,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+49.042801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+49.046669,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+48.970669,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+49.299824,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+49.353611,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+49.281948,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+49.679543,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+49.734798,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+49.675472,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+50.060478,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+49.967819,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+50.077984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+50.304913,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+50.289242,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+50.322502,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+50.690891,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+50.732521,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+50.654510,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+49.975315,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+50.079086,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+49.996311,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+50.332535,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+50.282856,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+50.285728,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+50.660797,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+50.644360,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+50.714626,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+50.017094,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+50.035751,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+50.041809,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+50.356575,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+50.368473,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+50.302010,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+50.631710,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+50.657310,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+50.711315,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+49.958603,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+50.022797,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+50.000206,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+50.289967,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+50.333523,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+50.401894,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+50.626976,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+50.619350,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+50.646801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+49.955647,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+49.968170,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+49.981880,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+50.337902,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+50.305382,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+50.291805,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+50.617897,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+50.636017,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+50.661476,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+50.000462,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+50.057915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+50.078831,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+50.281799,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+50.373653,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+50.391315,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+50.736504,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+50.739105,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+50.741661,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+49.962311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+49.959438,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+50.034912,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+50.315495,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+50.332039,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+50.344227,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+50.657230,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+50.730591,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+50.681690,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+50.005901,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+49.949677,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+50.037292,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+50.336349,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+50.285690,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+50.375332,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+50.695667,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+50.618237,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+50.660683,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+50.064697,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+50.026844,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+50.024269,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+50.377052,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+50.290665,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+50.293690,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+50.652412,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+50.688858,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+50.677212,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+50.061092,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+50.069286,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+49.982513,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+50.285583,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+50.297321,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+50.312119,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+50.624512,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+50.636974,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+50.687237,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+50.070633,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+49.997940,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+50.057564,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+50.384930,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+50.402569,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+50.387630,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+50.622082,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+50.732288,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+50.723709,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+50.060509,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+50.073719,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+50.019730,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+50.384445,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+50.377956,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+50.307774,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+50.649307,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+50.733639,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+50.716095,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+51.049496,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+51.039963,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+50.964504,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+51.403477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+51.291496,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+51.335548,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+51.693779,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+51.726604,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+51.700317,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+51.021633,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+51.029453,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+51.034191,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+51.337048,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+51.342987,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+51.326252,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+51.633732,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+51.619797,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+51.670681,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+51.002319,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+51.018040,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+50.954773,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+51.322590,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+51.363087,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+51.407730,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+51.649979,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+51.633904,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+51.708225,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+50.974991,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+51.051888,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+51.064823,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+51.332264,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+51.335983,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+51.386608,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+51.703506,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+51.630753,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+51.678940,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+51.016411,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+50.951199,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+51.046642,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+51.408840,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+51.350464,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+51.408619,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+51.649658,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+51.721012,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+51.657871,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+51.044170,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+50.977631,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+51.055538,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+51.294685,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+51.376251,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+51.372547,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+51.729935,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+51.713333,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+51.658554,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+50.959560,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+51.022705,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+50.993202,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+51.411484,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+51.390102,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+51.364998,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+51.715984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+51.719143,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+51.700424,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+50.993416,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+50.993904,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+51.019699,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+51.327488,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+51.294079,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+51.311569,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+51.709297,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+51.667847,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+51.711720,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+50.983635,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+51.045441,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+50.977966,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+51.384972,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+51.371094,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+51.404655,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+51.720810,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+51.681976,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+51.726311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+51.020271,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+50.972672,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+51.015148,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+51.290657,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+51.351147,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+51.366680,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+51.676311,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+51.628231,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+51.684727,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+51.074741,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+50.966454,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+50.981194,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+51.369770,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+51.299973,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+51.382587,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+51.643372,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+51.634148,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+51.671989,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+50.949600,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+50.957485,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+50.967999,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+51.379242,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+51.282635,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+51.379921,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+51.649849,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+51.654797,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+51.624336,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+52.079323,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+51.950844,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+52.080509,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+52.371552,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+52.367878,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+52.326519,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+52.694645,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+52.725876,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+52.622490,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+52.036205,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+52.013870,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+52.048824,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+52.345844,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+52.332874,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+52.325611,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+52.713943,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+52.635380,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+52.675697,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+52.065834,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+51.972305,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+52.001556,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+52.348267,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+52.359383,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+52.338192,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+52.660858,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+52.674622,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+52.663773,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+51.989601,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+52.075031,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+51.979214,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+52.348061,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+52.366360,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+52.316650,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+52.623486,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+52.717102,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+52.621414,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+51.959042,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+52.030708,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+52.015739,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+52.393967,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+52.293335,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+52.354187,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+52.709545,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+52.663120,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+52.744728,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+51.974697,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+52.050583,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+52.043152,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+52.336685,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+52.303562,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+52.351414,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+52.637848,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+52.720005,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+52.627457,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+51.985691,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+52.079147,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+52.041290,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+52.354828,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+52.310196,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+52.284187,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+52.693043,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+52.682209,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+52.662804,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+52.021927,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+52.039291,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+52.065632,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+52.317772,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+52.412048,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+52.339676,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+52.710945,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+52.627354,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+52.733116,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+52.001064,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+52.070469,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+52.016270,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+52.402363,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+52.333576,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+52.380558,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+52.654896,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+52.738411,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+52.696854,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+52.034767,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+52.061520,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+51.988560,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+52.303799,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+52.355537,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+52.400536,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+52.624527,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+52.707222,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+52.666336,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+51.972412,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+52.041927,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+52.031036,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+52.394966,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+52.397915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+52.343792,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+52.700169,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+52.698498,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+52.724457,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+52.050755,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+52.010029,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+51.966724,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+52.302280,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+52.313240,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+52.345383,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+52.646267,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+52.645962,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+52.697411,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+52.974937,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+53.043629,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+52.965855,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+53.326477,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+53.350048,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+53.349354,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+53.661816,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+53.663280,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+53.719952,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+52.954086,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+52.983097,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+52.980587,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+53.364033,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+53.405106,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+53.315540,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+53.689846,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+53.621532,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+53.678524,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+53.000237,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+53.073051,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+53.052711,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+53.351341,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+53.393261,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+53.405003,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+53.652756,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+53.642429,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+53.619698,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+53.072411,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+52.975311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+52.963249,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+53.321491,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+53.375568,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+53.355907,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+53.661064,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+53.746220,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+53.701012,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+52.969845,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+52.992268,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+53.022556,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+53.374584,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+53.321899,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+53.366005,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+53.695099,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+53.650028,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+53.641514,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+52.991875,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+52.982258,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+53.061333,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+53.398506,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+53.307484,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+53.297825,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+53.649536,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+53.632984,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+53.617222,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+53.044975,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+53.026966,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+52.972820,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+53.364372,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+53.368206,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+53.344212,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+53.666061,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+53.635605,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+53.672600,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+53.015709,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+52.958858,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+52.961586,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+53.404964,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+53.316116,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+53.282825,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+53.730919,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+53.618610,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+53.712566,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+52.967243,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+53.040802,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+53.018150,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+53.373577,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+53.309563,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+53.354996,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+53.642220,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+53.680946,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+53.736835,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+52.968201,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+52.984829,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+53.043499,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+53.375648,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+53.393208,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+53.379566,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+53.639336,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+53.669518,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+53.727962,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+53.074608,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+53.044994,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+53.034924,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+53.350498,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+53.413315,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+53.337070,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+53.649395,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+53.635658,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+53.695473,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+52.958633,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+53.006367,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+53.018005,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+53.371399,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+53.290405,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+53.367779,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+53.622070,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+53.716560,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+53.719189,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+54.038952,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+54.039593,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+53.976364,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+54.334843,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+54.306435,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+54.355492,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+54.733456,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+54.618092,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+54.650566,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+53.952282,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+53.961491,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+54.072407,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+54.286301,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+54.374138,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+54.299904,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+54.721897,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+54.710995,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+54.693977,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+54.057858,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+54.042374,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+54.055191,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+54.318947,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+54.287796,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+54.318012,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+54.711128,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+54.714836,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+54.667530,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+54.021358,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+53.993458,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+53.970345,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+54.359764,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+54.311111,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+54.301208,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+54.656570,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+54.695152,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+54.662239,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+53.959885,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+53.962353,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+54.000729,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+54.360165,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+54.351856,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+54.346714,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+54.720570,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+54.702534,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+54.640839,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+53.959541,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+54.048775,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+54.030708,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+54.382114,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+54.359531,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+54.324104,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+54.730972,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+54.715233,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+54.668869,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+54.037128,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+53.962006,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+54.010334,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+54.355457,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+54.309284,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+54.319241,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+54.650723,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+54.713043,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+54.715473,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+54.063721,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+54.064476,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+54.057549,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+54.311707,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+54.377041,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+54.314209,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+54.660545,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+54.621117,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+54.668533,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+54.064384,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+54.023903,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+54.024448,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+54.293617,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+54.394024,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+54.385422,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+54.627995,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+54.738335,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+54.732468,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+54.056412,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+54.006298,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+53.968868,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+54.374210,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+54.413315,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+54.315636,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+54.719837,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+54.686737,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+54.673561,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+54.081238,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+54.044353,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+53.998901,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+54.406887,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+54.400406,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+54.403889,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+54.661552,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+54.615593,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+54.694710,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+53.993969,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+54.028442,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+53.954800,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+54.304005,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+54.380539,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+54.342896,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+54.711243,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+54.696995,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+54.739784,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+55.014610,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+54.976429,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+55.001812,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+55.287682,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+55.298569,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+55.331123,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+55.734478,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+55.635044,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+55.702164,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+54.958633,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+55.079628,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+54.952076,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+55.344975,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+55.370827,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+55.335796,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+55.721226,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+55.656448,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+55.725929,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+54.995968,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+55.031830,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+55.056572,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+55.382000,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+55.408619,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+55.353642,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+55.620556,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+55.709454,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+55.726921,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+54.977943,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+55.020470,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+55.060318,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+55.328697,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+55.294788,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+55.372704,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+55.701202,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+55.720451,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+55.617371,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+54.988758,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+55.032421,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+55.035915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+55.294209,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+55.320984,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+55.292946,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+55.732540,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+55.707508,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+55.715672,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+54.987740,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+54.969563,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+54.990150,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+55.358376,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+55.400013,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+55.323463,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+55.638000,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+55.640842,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+55.727386,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+55.073372,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+54.994965,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+55.039459,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+55.407982,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+55.389065,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+55.285698,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+55.641186,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+55.677498,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+55.700291,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+55.071495,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+55.056427,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+54.993801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+55.361568,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+55.346714,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+55.399296,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+55.640900,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+55.704880,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+55.640488,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+54.957081,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+55.029209,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+55.015465,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+55.322891,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+55.355000,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+55.300842,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+55.626892,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+55.731106,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+55.625095,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+54.970901,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+55.029346,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+54.976967,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+55.360340,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+55.320843,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+55.349033,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+55.618374,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+55.705467,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+55.669380,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+54.987446,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+55.037052,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+54.992966,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+55.310734,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+55.414551,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+55.377903,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+55.636654,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+55.624851,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+55.735313,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+54.980915,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+54.986378,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+54.957214,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+55.340805,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+55.413498,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+55.289421,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+55.649918,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+55.723186,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+55.716740,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+56.037922,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+56.020084,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+55.957512,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+56.296680,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+56.320217,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+56.306499,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+56.651733,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+56.634670,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+56.740162,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+56.054447,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+55.968010,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+55.970158,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+56.343292,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+56.363796,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+56.401867,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+56.746094,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+56.742115,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+56.692490,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+55.983479,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+55.991444,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+55.995361,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+56.383423,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+56.303028,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+56.377865,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+56.704235,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+56.696442,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+56.620129,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+56.032532,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+56.059372,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+55.986298,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+56.369831,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+56.384441,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+56.293480,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+56.646263,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+56.638676,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+56.614834,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+56.041054,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+56.056549,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+56.065495,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+56.370171,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+56.306896,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+56.400726,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+56.614902,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+56.747295,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+56.725918,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+56.068924,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+56.060345,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+56.052071,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+56.411358,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+56.408333,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+56.378914,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+56.695190,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+56.730515,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+56.659054,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+55.954525,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+55.958214,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+55.998852,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+56.362659,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+56.308914,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+56.331627,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+56.680489,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+56.676567,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+56.619801,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+56.066460,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+56.060059,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+56.051487,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+56.408867,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+56.402992,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+56.327267,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+56.685947,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+56.711430,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+56.723934,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+55.966003,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+56.035019,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+56.017624,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+56.362862,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+56.301201,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+56.372898,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+56.685741,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+56.727535,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+56.715122,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+55.995808,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+55.988811,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+55.956581,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+56.355434,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+56.404232,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+56.413395,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+56.638199,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+56.643253,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+56.666683,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+56.066059,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+56.011280,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+56.036015,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+56.404530,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+56.402985,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+56.377674,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+56.622570,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+56.747395,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+56.633308,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+56.051285,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+56.001354,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+56.029633,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+56.372936,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+56.411743,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+56.339710,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+56.732105,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+56.735638,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+56.739529,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+57.074059,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+57.040905,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+57.035679,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+57.341248,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+57.376530,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+57.367363,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+57.698498,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+57.674461,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+57.715549,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+57.058979,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+57.005245,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+57.012180,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+57.383320,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+57.397522,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+57.304642,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+57.702587,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+57.672310,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+57.643280,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+56.991219,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+56.955223,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+56.962929,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+57.355881,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+57.325539,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+57.303616,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+57.677341,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+57.675419,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+57.723053,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+57.008705,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+56.978378,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+57.020634,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+57.376450,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+57.316505,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+57.304546,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+57.662224,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+57.713337,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+57.646652,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+57.007587,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+56.951908,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+57.006565,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+57.322819,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+57.340092,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+57.363903,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+57.714993,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+57.712311,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+57.716778,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+57.027878,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+57.062496,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+57.077232,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+57.402515,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+57.295509,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+57.302837,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+57.623299,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+57.727093,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+57.668732,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+57.061928,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+56.966797,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+57.010918,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+57.281387,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+57.298534,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+57.360180,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+57.729042,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+57.720295,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+57.731274,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+56.997509,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+57.042252,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+57.017857,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+57.389706,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+57.363487,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+57.318481,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+57.640537,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+57.680080,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+57.667603,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+57.028282,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+56.966991,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+57.008068,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+57.385674,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+57.394882,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+57.303028,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+57.704205,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+57.688061,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+57.677349,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+57.058113,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+57.044521,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+57.003510,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+57.400269,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+57.340599,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+57.354622,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+57.698330,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+57.725250,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+57.683426,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+56.960739,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+57.041851,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+57.053440,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+57.302917,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+57.326939,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+57.360195,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+57.671211,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+57.631702,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+57.688286,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+57.068165,+0.500000] , 1. [+0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+57.037861,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+57.010612,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+57.291882,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+57.373184,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+57.297134,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+57.713882,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+57.703579,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+57.732433,+0.500000] , 1. [-0.000000,-0.576000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-5.txt b/scenes/cell-growth/results/particles-frame-5.txt deleted file mode 100644 index 9667afd2..00000000 --- a/scenes/cell-growth/results/particles-frame-5.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+44.362946,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+44.464191,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+44.436523,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+44.692818,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+44.781094,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+44.692574,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+45.040154,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+45.128468,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+45.093468,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+44.447773,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+44.416588,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+44.466774,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+44.681103,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+44.713627,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+44.717361,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+45.132423,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+45.032230,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+45.097473,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+44.446804,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+44.409519,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+44.446987,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+44.690971,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+44.740601,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+44.682930,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+45.035618,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+45.098965,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+45.074448,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+44.437286,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+44.442265,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+44.390385,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+44.733105,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+44.789871,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+44.726688,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+45.064117,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+45.056717,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+45.027061,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+44.412231,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+44.434017,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+44.363441,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+44.731396,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+44.774769,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+44.723270,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+45.139996,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+45.071278,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+45.058510,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+44.357395,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+44.352108,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+44.420456,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+44.675594,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+44.733063,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+44.686279,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+45.066479,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+45.016903,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+45.032455,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+44.473034,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+44.393208,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+44.379021,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+44.781414,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+44.775341,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+44.747299,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+45.115803,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+45.051891,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+45.091541,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+44.347637,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+44.433914,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+44.464500,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+44.703789,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+44.705147,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+44.683617,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+45.130520,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+45.026203,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+45.093773,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+44.396305,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+44.374683,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+44.378506,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+44.684494,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+44.674053,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+44.697834,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+45.088036,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+45.063114,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+45.077923,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+44.365910,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+44.380657,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+44.362892,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+44.801376,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+44.676270,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+44.782310,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+45.087364,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+45.041313,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+45.057129,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+44.411369,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+44.454227,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+44.465923,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+44.770821,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+44.687946,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+44.717556,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+45.120823,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+45.106277,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+45.018494,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+44.426804,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+44.441875,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+44.411842,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+44.773689,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+44.736179,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+44.714493,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+45.085938,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+45.122910,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+45.013363,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+45.370926,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+45.438881,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+45.384338,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+45.732693,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+45.776112,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+45.703453,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+46.030479,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+46.087280,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+46.007595,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+45.420181,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+45.381027,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+45.343716,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+45.733227,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+45.739552,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+45.785896,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+46.056641,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+46.095516,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+46.065243,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+45.347992,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+45.383991,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+45.433727,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+45.791412,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+45.706867,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+45.679707,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+46.104904,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+46.034431,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+46.023441,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+45.378254,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+45.357510,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+45.357513,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+45.696320,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+45.780884,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+45.688000,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+46.065693,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+46.099487,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+46.080479,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+45.415447,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+45.352047,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+45.471554,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+45.727581,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+45.720814,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+45.704243,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+46.039391,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+46.132893,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+46.100437,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+45.362343,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+45.356995,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+45.378937,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+45.734604,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+45.802841,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+45.781826,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+46.121769,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+46.062149,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+46.013203,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+45.367672,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+45.387615,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+45.363857,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+45.697781,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+45.678082,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+45.792202,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+46.099419,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+46.107281,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+46.091831,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+45.391441,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+45.355255,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+45.350174,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+45.793575,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+45.797207,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+45.698063,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+46.101833,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+46.042294,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+46.102776,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+45.378986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+45.446239,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+45.398655,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+45.694508,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+45.681412,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+45.805649,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+46.016232,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+46.009708,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+46.061867,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+45.367432,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+45.355667,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+45.466072,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+45.780216,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+45.742474,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+45.776962,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+46.040455,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+46.057510,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+46.075382,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+45.453564,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+45.391266,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+45.347275,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+45.738655,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+45.718735,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+45.734474,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+46.058956,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+46.117615,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+46.031483,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+45.372787,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+45.443645,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+45.403214,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+45.766205,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+45.770908,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+45.708210,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+46.072624,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+46.061848,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+46.034729,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+46.402821,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+46.362747,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+46.424877,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+46.764179,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+46.743080,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+46.798645,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+47.125145,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+47.010777,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+47.032242,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+46.392677,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+46.463043,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+46.466702,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+46.740192,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+46.683212,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+46.718243,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+47.086166,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+47.116047,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+47.035282,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+46.376926,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+46.360325,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+46.382946,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+46.792450,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+46.699566,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+46.674583,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+47.012371,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+47.018795,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+47.068932,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+46.364151,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+46.443359,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+46.443485,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+46.696121,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+46.803364,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+46.754711,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+47.118980,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+47.027393,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+47.083462,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+46.370750,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+46.457806,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+46.428921,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+46.785728,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+46.765553,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+46.694157,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+47.070446,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+47.082344,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+47.100346,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+46.370045,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+46.409103,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+46.349262,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+46.733719,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+46.688053,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+46.729206,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+47.061279,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+47.076340,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+47.024986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+46.423820,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+46.364479,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+46.367294,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+46.712090,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+46.727776,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+46.794247,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+47.051422,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+47.138451,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+47.118317,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+46.455517,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+46.340549,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+46.472244,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+46.787331,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+46.685318,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+46.761066,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+47.055157,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+47.050655,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+47.089615,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+46.354969,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+46.383125,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+46.406750,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+46.796467,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+46.788052,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+46.715309,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+47.098186,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+47.094490,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+47.124146,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+46.470211,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+46.385620,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+46.407215,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+46.806496,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+46.686348,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+46.753117,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+47.041191,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+47.108795,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+47.009201,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+46.406425,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+46.340347,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+46.362286,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+46.718395,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+46.790966,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+46.763508,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+47.053108,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+47.080479,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+47.061459,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+46.378536,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+46.345463,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+46.457756,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+46.682671,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+46.804214,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+46.759209,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+47.057735,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+47.053371,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+47.065166,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+47.442104,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+47.403145,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+47.406685,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+47.792030,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+47.764919,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+47.682144,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+48.107899,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+48.019680,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+48.064281,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+47.470737,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+47.452599,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+47.355389,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+47.732376,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+47.715069,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+47.781773,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+48.094856,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+48.007210,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+48.013508,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+47.381657,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+47.406551,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+47.383247,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+47.750721,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+47.746071,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+47.748684,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+48.100327,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+48.117027,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+48.056915,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+47.395786,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+47.445274,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+47.429607,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+47.739487,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+47.770828,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+47.757149,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+48.009178,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+48.058937,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+48.118614,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+47.443924,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+47.397736,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+47.466637,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+47.738216,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+47.754059,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+47.676666,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+48.017391,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+48.121254,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+48.043137,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+47.397797,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+47.376423,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+47.422318,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+47.805595,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+47.690090,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+47.718624,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+48.015697,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+48.097549,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+48.024422,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+47.379822,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+47.419342,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+47.376278,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+47.738323,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+47.678993,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+47.728844,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+48.078091,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+48.050343,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+48.065655,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+47.409927,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+47.357643,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+47.357681,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+47.773029,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+47.733833,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+47.784618,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+48.021713,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+48.129005,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+48.060116,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+47.362583,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+47.428371,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+47.439560,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+47.747681,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+47.686756,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+47.783894,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+48.135475,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+48.107826,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+48.113480,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+47.463024,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+47.431332,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+47.423573,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+47.791302,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+47.790821,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+47.730289,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+48.051743,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+48.053150,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+48.099335,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+47.412479,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+47.461178,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+47.371029,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+47.686531,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+47.718483,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+47.734974,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+48.069546,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+48.014755,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+48.102600,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+47.449226,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+47.439564,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+47.373722,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+47.745277,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+47.792637,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+47.747280,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+48.029018,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+48.099247,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+48.022766,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+48.423065,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+48.385410,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+48.449474,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+48.722198,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+48.731392,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+48.691612,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+49.083065,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+49.076664,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+49.033932,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+48.395065,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+48.370525,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+48.457451,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+48.770889,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+48.729057,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+48.715973,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+49.082474,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+49.066547,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+49.119637,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+48.422356,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+48.405079,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+48.353249,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+48.780636,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+48.757038,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+48.804111,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+49.035980,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+49.032555,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+49.109402,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+48.362652,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+48.377769,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+48.438354,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+48.805008,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+48.695641,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+48.756424,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+49.026855,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+49.075386,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+49.054070,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+48.378067,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+48.451958,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+48.472569,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+48.770844,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+48.718769,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+48.805923,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+49.024204,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+49.012272,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+49.080475,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+48.377762,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+48.430607,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+48.457081,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+48.709557,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+48.761662,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+48.733418,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+49.077618,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+49.080677,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+49.025959,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+48.354866,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+48.402527,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+48.411366,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+48.753117,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+48.696766,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+48.803719,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+49.027775,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+49.102272,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+49.044060,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+48.420368,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+48.471039,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+48.408207,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+48.711010,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+48.795967,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+48.717464,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+49.133331,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+49.123394,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+49.038265,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+48.355839,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+48.397945,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+48.345917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+48.718658,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+48.698666,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+48.751163,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+49.031593,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+49.046127,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+49.116020,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+48.384434,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+48.418293,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+48.397728,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+48.746979,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+48.735489,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+48.709412,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+49.043499,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+49.086727,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+49.097591,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+48.426739,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+48.341228,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+48.367180,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+48.726154,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+48.748112,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+48.728966,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+49.086491,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+49.028965,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+49.115540,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+48.434803,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+48.438671,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+48.362671,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+48.691826,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+48.745613,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+48.673950,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+49.071545,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+49.126801,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+49.067474,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+49.452480,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+49.359821,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+49.469986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+49.696915,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+49.681244,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+49.714504,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+50.082893,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+50.124523,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+50.046513,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+49.367317,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+49.471088,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+49.388313,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+49.724537,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+49.674858,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+49.677731,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+50.052799,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+50.036362,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+50.106628,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+49.409096,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+49.427753,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+49.433811,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+49.748577,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+49.760475,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+49.694012,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+50.023712,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+50.049313,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+50.103317,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+49.350605,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+49.414799,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+49.392208,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+49.681969,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+49.725525,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+49.793896,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+50.018978,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+50.011353,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+50.038803,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+49.347649,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+49.360172,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+49.373882,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+49.729904,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+49.697384,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+49.683807,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+50.009899,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+50.028019,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+50.053478,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+49.392464,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+49.449917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+49.470833,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+49.673801,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+49.765656,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+49.783318,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+50.128506,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+50.131107,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+50.133663,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+49.354313,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+49.351440,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+49.426914,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+49.707497,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+49.724041,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+49.736229,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+50.049232,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+50.122593,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+50.073692,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+49.397903,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+49.341679,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+49.429295,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+49.728352,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+49.677692,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+49.767334,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+50.087669,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+50.010239,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+50.052685,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+49.456699,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+49.418846,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+49.416271,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+49.769054,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+49.682667,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+49.685692,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+50.044415,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+50.080860,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+50.069214,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+49.453094,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+49.461288,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+49.374516,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+49.677586,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+49.689323,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+49.704121,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+50.016514,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+50.028976,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+50.079239,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+49.462635,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+49.389942,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+49.449566,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+49.776932,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+49.794571,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+49.779633,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+50.014084,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+50.124290,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+50.115711,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+49.452511,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+49.465721,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+49.411732,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+49.776447,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+49.769958,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+49.699776,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+50.041309,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+50.125641,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+50.108097,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+50.441498,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+50.431965,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+50.356506,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+50.795479,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+50.683498,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+50.727551,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+51.085781,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+51.118607,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+51.092319,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+50.413635,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+50.421455,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+50.426193,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+50.729050,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+50.734989,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+50.718254,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+51.025734,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+51.011799,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+51.062683,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+50.394321,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+50.410042,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+50.346775,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+50.714592,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+50.755089,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+50.799732,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+51.041981,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+51.025906,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+51.100227,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+50.366993,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+50.443890,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+50.456825,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+50.724266,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+50.727985,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+50.778610,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+51.095509,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+51.022755,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+51.070942,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+50.408413,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+50.343201,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+50.438644,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+50.800842,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+50.742466,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+50.800621,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+51.041660,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+51.113014,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+51.049873,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+50.436172,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+50.369633,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+50.447540,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+50.686687,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+50.768253,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+50.764549,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+51.121937,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+51.105335,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+51.050556,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+50.351562,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+50.414707,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+50.385204,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+50.803486,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+50.782104,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+50.757000,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+51.107986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+51.111145,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+51.092426,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+50.385418,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+50.385906,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+50.411701,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+50.719490,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+50.686081,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+50.703571,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+51.101299,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+51.059849,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+51.103722,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+50.375637,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+50.437443,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+50.369968,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+50.776974,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+50.763096,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+50.796658,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+51.112812,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+51.073978,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+51.118313,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+50.412273,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+50.364674,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+50.407150,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+50.682659,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+50.743149,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+50.758682,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+51.068314,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+51.020233,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+51.076729,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+50.466743,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+50.358456,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+50.373196,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+50.761772,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+50.691975,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+50.774590,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+51.035374,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+51.026150,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+51.063992,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+50.341602,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+50.349487,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+50.360001,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+50.771244,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+50.674637,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+50.771923,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+51.041851,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+51.046799,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+51.016338,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+51.471325,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+51.342846,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+51.472511,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+51.763554,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+51.759880,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+51.718521,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+52.086647,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+52.117878,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+52.014492,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+51.428207,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+51.405872,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+51.440826,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+51.737846,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+51.724876,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+51.717613,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+52.105946,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+52.027382,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+52.067699,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+51.457836,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+51.364307,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+51.393559,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+51.740269,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+51.751385,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+51.730194,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+52.052860,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+52.066624,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+52.055775,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+51.381603,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+51.467033,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+51.371216,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+51.740063,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+51.758362,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+51.708652,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+52.015488,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+52.109104,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+52.013416,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+51.351044,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+51.422710,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+51.407742,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+51.785969,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+51.685337,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+51.746189,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+52.101547,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+52.055122,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+52.136730,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+51.366699,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+51.442585,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+51.435154,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+51.728687,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+51.695564,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+51.743416,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+52.029850,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+52.112007,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+52.019459,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+51.377693,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+51.471149,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+51.433292,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+51.746830,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+51.702198,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+51.676189,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+52.085045,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+52.074211,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+52.054806,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+51.413929,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+51.431293,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+51.457634,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+51.709774,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+51.804050,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+51.731678,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+52.102947,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+52.019356,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+52.125118,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+51.393066,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+51.462471,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+51.408272,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+51.794365,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+51.725578,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+51.772560,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+52.046898,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+52.130413,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+52.088856,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+51.426769,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+51.453522,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+51.380562,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+51.695801,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+51.747540,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+51.792538,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+52.016529,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+52.099224,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+52.058338,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+51.364414,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+51.433929,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+51.423038,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+51.786968,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+51.789917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+51.735794,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+52.092171,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+52.090500,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+52.116459,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+51.442757,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+51.402031,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+51.358727,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+51.694283,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+51.705242,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+51.737385,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+52.038269,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+52.037964,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+52.089413,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+52.366940,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+52.435631,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+52.357857,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+52.718479,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+52.742050,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+52.741356,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+53.053818,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+53.055283,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+53.111954,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+52.346088,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+52.375099,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+52.372589,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+52.756035,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+52.797108,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+52.707542,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+53.081848,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+53.013535,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+53.070526,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+52.392239,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+52.465054,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+52.444714,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+52.743343,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+52.785263,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+52.797005,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+53.044758,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+53.034431,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+53.011700,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+52.464413,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+52.367313,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+52.355251,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+52.713493,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+52.767570,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+52.747910,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+53.053066,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+53.138222,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+53.093014,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+52.361847,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+52.384270,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+52.414558,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+52.766586,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+52.713902,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+52.758007,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+53.087101,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+53.042030,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+53.033516,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+52.383877,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+52.374260,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+52.453335,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+52.790508,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+52.699486,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+52.689827,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+53.041538,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+53.024986,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+53.009224,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+52.436977,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+52.418968,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+52.364822,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+52.756374,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+52.760208,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+52.736214,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+53.058064,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+53.027607,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+53.064602,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+52.407711,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+52.350861,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+52.353588,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+52.796967,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+52.708118,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+52.674828,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+53.122921,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+53.010612,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+53.104568,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+52.359245,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+52.432804,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+52.410152,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+52.765579,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+52.701565,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+52.746998,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+53.034222,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+53.072948,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+53.128838,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+52.360203,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+52.376831,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+52.435501,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+52.767651,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+52.785210,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+52.771568,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+53.031338,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+53.061520,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+53.119965,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+52.466610,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+52.436996,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+52.426926,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+52.742500,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+52.805317,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+52.729073,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+53.041397,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+53.027660,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+53.087475,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+52.350636,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+52.398369,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+52.410007,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+52.763401,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+52.682407,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+52.759781,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+53.014072,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+53.108562,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+53.111191,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+53.430954,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+53.431595,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+53.368366,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+53.726845,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+53.698437,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+53.747494,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+54.125458,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+54.010094,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+54.042568,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+53.344284,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+53.353493,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+53.464409,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+53.678303,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+53.766140,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+53.691906,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+54.113899,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+54.102997,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+54.085979,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+53.449860,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+53.434376,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+53.447193,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+53.710949,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+53.679798,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+53.710014,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+54.103130,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+54.106838,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+54.059532,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+53.413361,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+53.385460,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+53.362347,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+53.751766,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+53.703114,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+53.693211,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+54.048573,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+54.087154,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+54.054241,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+53.351887,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+53.354355,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+53.392731,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+53.752167,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+53.743858,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+53.738716,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+54.112572,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+54.094536,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+54.032841,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+53.351543,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+53.440777,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+53.422710,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+53.774117,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+53.751534,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+53.716106,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+54.122974,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+54.107235,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+54.060871,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+53.429131,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+53.354008,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+53.402336,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+53.747459,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+53.701286,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+53.711243,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+54.042725,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+54.105045,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+54.107475,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+53.455723,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+53.456478,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+53.449551,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+53.703709,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+53.769043,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+53.706211,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+54.052547,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+54.013119,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+54.060535,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+53.456387,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+53.415905,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+53.416451,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+53.685619,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+53.786026,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+53.777424,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+54.019997,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+54.130337,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+54.124470,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+53.448414,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+53.398300,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+53.360870,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+53.766212,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+53.805317,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+53.707638,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+54.111839,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+54.078739,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+54.065563,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+53.473240,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+53.436356,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+53.390903,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+53.798889,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+53.792408,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+53.795891,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+54.053555,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+54.007595,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+54.086712,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+53.385971,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+53.420444,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+53.346802,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+53.696007,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+53.772541,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+53.734898,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+54.103245,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+54.088997,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+54.131786,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+54.406612,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+54.368431,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+54.393814,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+54.679684,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+54.690571,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+54.723125,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+55.126480,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+55.027046,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+55.094166,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+54.350636,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+54.471630,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+54.344078,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+54.736977,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+54.762829,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+54.727798,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+55.113228,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+55.048450,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+55.117931,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+54.387970,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+54.423832,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+54.448574,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+54.774002,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+54.800621,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+54.745644,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+55.012558,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+55.101456,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+55.118923,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+54.369946,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+54.412472,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+54.452320,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+54.720699,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+54.686790,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+54.764702,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+55.093204,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+55.112453,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+55.009373,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+54.380760,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+54.424423,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+54.427917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+54.686211,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+54.712982,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+54.684948,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+55.124542,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+55.099510,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+55.107674,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+54.379742,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+54.361565,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+54.382153,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+54.750378,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+54.792015,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+54.715466,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+55.030003,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+55.032845,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+55.119389,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+54.465374,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+54.386967,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+54.431461,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+54.799984,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+54.781067,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+54.677700,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+55.033188,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+55.069500,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+55.092293,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+54.463497,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+54.448429,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+54.385803,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+54.753571,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+54.738716,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+54.791298,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+55.032902,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+55.096882,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+55.032490,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+54.349083,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+54.421211,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+54.407467,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+54.714893,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+54.747002,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+54.692844,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+55.018894,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+55.123108,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+55.017097,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+54.362904,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+54.421349,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+54.368969,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+54.752342,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+54.712845,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+54.741035,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+55.010376,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+55.097469,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+55.061382,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+54.379448,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+54.429054,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+54.384968,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+54.702736,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+54.806553,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+54.769905,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+55.028656,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+55.016853,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+55.127316,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+54.372917,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+54.378380,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+54.349216,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+54.732807,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+54.805500,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+54.681423,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+55.041920,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+55.115189,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+55.108742,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+55.429924,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+55.412086,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+55.349514,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+55.688683,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+55.712219,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+55.698502,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+56.043736,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+56.026672,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+56.132164,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+55.446449,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+55.360012,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+55.362160,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+55.735294,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+55.755798,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+55.793869,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+56.138096,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+56.134117,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+56.084492,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+55.375481,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+55.383446,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+55.387363,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+55.775425,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+55.695030,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+55.769867,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+56.096237,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+56.088444,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+56.012131,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+55.424534,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+55.451374,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+55.378300,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+55.761833,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+55.776443,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+55.685482,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+56.038265,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+56.030678,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+56.006836,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+55.433056,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+55.448547,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+55.457497,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+55.762173,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+55.698898,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+55.792728,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+56.006905,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+56.139297,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+56.117920,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+55.460922,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+55.452347,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+55.444069,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+55.803360,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+55.800331,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+55.770916,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+56.087193,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+56.122513,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+56.051056,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+55.346527,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+55.350216,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+55.390854,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+55.754662,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+55.700916,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+55.723629,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+56.072491,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+56.068569,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+56.011803,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+55.458462,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+55.452061,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+55.443489,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+55.800869,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+55.794994,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+55.719269,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+56.077950,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+56.103432,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+56.115936,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+55.358006,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+55.427021,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+55.409626,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+55.754864,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+55.693203,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+55.764900,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+56.077744,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+56.119537,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+56.107124,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+55.387810,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+55.380814,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+55.348583,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+55.747437,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+55.796234,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+55.805397,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+56.030201,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+56.035255,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+56.058685,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+55.458061,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+55.403282,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+55.428017,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+55.796532,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+55.794987,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+55.769676,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+56.014572,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+56.139397,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+56.025311,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+55.443287,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+55.393356,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+55.421635,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+55.764938,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+55.803745,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+55.731712,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+56.124107,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+56.127640,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+56.131531,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+56.466061,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+56.432907,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+56.427681,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+56.733250,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+56.768532,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+56.759365,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+57.090500,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+57.066463,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+57.107552,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+56.450981,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+56.397247,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+56.404182,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+56.775322,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+56.789524,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+56.696644,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+57.094589,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+57.064312,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+57.035282,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+56.383221,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+56.347225,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+56.354931,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+56.747883,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+56.717541,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+56.695618,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+57.069344,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+57.067421,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+57.115055,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+56.400707,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+56.370380,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+56.412636,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+56.768452,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+56.708508,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+56.696548,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+57.054226,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+57.105339,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+57.038654,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+56.399590,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+56.343910,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+56.398567,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+56.714821,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+56.732094,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+56.755905,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+57.106995,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+57.104313,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+57.108780,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+56.419880,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+56.454498,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+56.469231,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+56.794518,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+56.687508,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+56.694839,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+57.015301,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+57.119095,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+57.060730,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+56.453930,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+56.358799,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+56.402920,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+56.673389,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+56.690536,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+56.752182,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+57.121044,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+57.112297,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+57.123276,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+56.389511,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+56.434254,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+56.409859,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+56.781708,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+56.755489,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+56.710484,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+57.032539,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+57.072083,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+57.059605,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+56.420284,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+56.358994,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+56.400070,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+56.777676,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+56.786884,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+56.695030,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+57.096207,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+57.080063,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+57.069351,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+56.450115,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+56.436523,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+56.395512,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+56.792271,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+56.732601,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+56.746624,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+57.090332,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+57.117252,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+57.075428,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+56.352741,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+56.433853,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+56.445442,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+56.694920,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+56.718941,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+56.752197,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+57.063213,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+57.023705,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+57.080288,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+56.460167,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+56.429863,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+56.402615,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+56.683884,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+56.765186,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+56.689137,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+57.105885,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+57.095581,+0.500000] , 1. [+0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+57.124435,+0.500000] , 1. [-0.000000,-0.704000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-6.txt b/scenes/cell-growth/results/particles-frame-6.txt deleted file mode 100644 index eb50bff2..00000000 --- a/scenes/cell-growth/results/particles-frame-6.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+43.626945,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+43.728191,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+43.700523,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+43.956818,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+44.045094,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+43.956573,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+44.304153,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+44.392467,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+44.357468,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+43.711773,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+43.680588,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+43.730774,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+43.945103,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+43.977627,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+43.981361,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+44.396423,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+44.296230,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+44.361473,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+43.710804,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+43.673519,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+43.710987,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+43.954971,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+44.004601,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+43.946930,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+44.299618,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+44.362965,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+44.338448,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+43.701286,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+43.706264,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+43.654385,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+43.997105,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+44.053871,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+43.990688,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+44.328117,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+44.320717,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+44.291061,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+43.676231,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+43.698017,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+43.627441,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+43.995396,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+44.038769,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+43.987270,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+44.403996,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+44.335278,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+44.322510,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+43.621395,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+43.616108,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+43.684456,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+43.939594,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+43.997063,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+43.950279,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+44.330479,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+44.280903,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+44.296455,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+43.737034,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+43.657207,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+43.643021,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+44.045414,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+44.039341,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+44.011299,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+44.379803,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+44.315891,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+44.355541,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+43.611637,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+43.697914,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+43.728500,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+43.967789,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+43.969147,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+43.947617,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+44.394520,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+44.290203,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+44.357773,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+43.660305,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+43.638683,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+43.642506,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+43.948494,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+43.938053,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+43.961834,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+44.352036,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+44.327114,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+44.341923,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+43.629910,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+43.644657,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+43.626892,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+44.065376,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+43.940269,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+44.046310,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+44.351364,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+44.305313,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+44.321129,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+43.675369,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+43.718227,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+43.729923,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+44.034821,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+43.951946,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+43.981556,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+44.384823,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+44.370277,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+44.282494,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+43.690804,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+43.705875,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+43.675842,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+44.037689,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+44.000179,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+43.978493,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+44.349937,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+44.386909,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+44.277363,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+44.634926,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+44.702881,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+44.648338,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+44.996693,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+45.040112,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+44.967453,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+45.294479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+45.351280,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+45.271595,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+44.684181,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+44.645027,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+44.607716,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+44.997227,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+45.003551,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+45.049896,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+45.320641,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+45.359516,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+45.329243,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+44.611992,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+44.647991,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+44.697727,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+45.055412,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+44.970867,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+44.943707,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+45.368904,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+45.298431,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+45.287441,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+44.642254,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+44.621510,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+44.621513,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+44.960320,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+45.044884,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+44.952000,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+45.329693,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+45.363487,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+45.344479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+44.679447,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+44.616047,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+44.735554,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+44.991581,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+44.984814,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+44.968243,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+45.303391,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+45.396893,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+45.364437,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+44.626343,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+44.620995,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+44.642937,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+44.998604,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+45.066841,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+45.045826,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+45.385769,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+45.326149,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+45.277203,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+44.631672,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+44.651615,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+44.627857,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+44.961781,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+44.942081,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+45.056202,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+45.363419,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+45.371281,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+45.355831,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+44.655441,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+44.619255,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+44.614174,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+45.057575,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+45.061207,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+44.962063,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+45.365833,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+45.306293,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+45.366776,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+44.642986,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+44.710239,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+44.662655,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+44.958508,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+44.945412,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+45.069649,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+45.280231,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+45.273708,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+45.325867,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+44.631432,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+44.619667,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+44.730072,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+45.044216,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+45.006474,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+45.040962,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+45.304455,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+45.321510,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+45.339382,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+44.717564,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+44.655266,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+44.611275,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+45.002655,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+44.982735,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+44.998474,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+45.322956,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+45.381615,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+45.295483,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+44.636787,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+44.707645,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+44.667213,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+45.030205,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+45.034908,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+44.972210,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+45.336624,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+45.325848,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+45.298729,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+45.666821,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+45.626747,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+45.688877,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+46.028179,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+46.007080,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+46.062645,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+46.389145,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+46.274776,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+46.296242,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+45.656677,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+45.727043,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+45.730701,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+46.004192,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+45.947212,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+45.982243,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+46.350166,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+46.380047,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+46.299282,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+45.640926,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+45.624325,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+45.646946,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+46.056450,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+45.963566,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+45.938583,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+46.276371,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+46.282795,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+46.332932,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+45.628151,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+45.707359,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+45.707485,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+45.960121,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+46.067364,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+46.018711,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+46.382980,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+46.291393,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+46.347462,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+45.634750,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+45.721806,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+45.692921,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+46.049728,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+46.029552,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+45.958157,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+46.334446,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+46.346344,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+46.364346,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+45.634045,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+45.673103,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+45.613262,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+45.997719,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+45.952053,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+45.993206,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+46.325279,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+46.340340,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+46.288986,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+45.687820,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+45.628479,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+45.631294,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+45.976089,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+45.991776,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+46.058247,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+46.315422,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+46.402451,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+46.382317,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+45.719517,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+45.604549,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+45.736244,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+46.051331,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+45.949318,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+46.025066,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+46.319157,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+46.314655,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+46.353615,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+45.618969,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+45.647125,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+45.670750,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+46.060467,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+46.052052,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+45.979309,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+46.362186,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+46.358490,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+46.388145,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+45.734211,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+45.649620,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+45.671215,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+46.070496,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+45.950348,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+46.017117,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+46.305191,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+46.372795,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+46.273201,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+45.670425,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+45.604347,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+45.626286,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+45.982395,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+46.054966,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+46.027508,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+46.317108,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+46.344479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+46.325459,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+45.642536,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+45.609463,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+45.721756,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+45.946671,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+46.068214,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+46.023209,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+46.321735,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+46.317371,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+46.329166,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+46.706104,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+46.667145,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+46.670685,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+47.056030,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+47.028919,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+46.946144,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+47.371899,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+47.283680,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+47.328281,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+46.734737,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+46.716599,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+46.619389,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+46.996376,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+46.979069,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+47.045773,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+47.358856,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+47.271210,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+47.277508,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+46.645657,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+46.670551,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+46.647247,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+47.014721,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+47.010071,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+47.012684,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+47.364326,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+47.381027,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+47.320915,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+46.659786,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+46.709274,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+46.693607,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+47.003487,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+47.034828,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+47.021149,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+47.273178,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+47.322937,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+47.382614,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+46.707924,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+46.661736,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+46.730637,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+47.002216,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+47.018059,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+46.940666,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+47.281391,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+47.385254,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+47.307137,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+46.661797,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+46.640423,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+46.686317,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+47.069595,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+46.954090,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+46.982624,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+47.279697,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+47.361549,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+47.288422,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+46.643822,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+46.683342,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+46.640278,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+47.002323,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+46.942993,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+46.992844,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+47.342091,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+47.314342,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+47.329655,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+46.673927,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+46.621643,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+46.621681,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+47.037029,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+46.997833,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+47.048618,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+47.285713,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+47.393005,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+47.324116,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+46.626583,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+46.692371,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+46.703560,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+47.011681,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+46.950756,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+47.047894,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+47.399475,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+47.371826,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+47.377480,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+46.727024,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+46.695332,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+46.687572,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+47.055302,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+47.054821,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+46.994289,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+47.315742,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+47.317150,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+47.363335,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+46.676479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+46.725178,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+46.635029,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+46.950531,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+46.982483,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+46.998974,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+47.333546,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+47.278755,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+47.366600,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+46.713226,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+46.703564,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+46.637722,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+47.009277,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+47.056637,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+47.011280,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+47.293018,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+47.363247,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+47.286766,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+47.687065,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+47.649410,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+47.713474,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+47.986198,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+47.995392,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+47.955612,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+48.347065,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+48.340664,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+48.297932,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+47.659065,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+47.634525,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+47.721451,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+48.034889,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+47.993057,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+47.979973,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+48.346474,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+48.330547,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+48.383636,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+47.686356,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+47.669079,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+47.617249,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+48.044636,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+48.021038,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+48.068111,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+48.299980,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+48.296555,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+48.373402,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+47.626652,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+47.641769,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+47.702354,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+48.069008,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+47.959641,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+48.020424,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+48.290855,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+48.339386,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+48.318069,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+47.642067,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+47.715958,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+47.736568,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+48.034843,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+47.982769,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+48.069923,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+48.288204,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+48.276272,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+48.344475,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+47.641762,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+47.694607,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+47.721081,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+47.973557,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+48.025661,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+47.997417,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+48.341618,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+48.344677,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+48.289959,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+47.618866,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+47.666527,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+47.675365,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+48.017117,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+47.960766,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+48.067719,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+48.291775,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+48.366272,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+48.308060,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+47.684368,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+47.735039,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+47.672207,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+47.975010,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+48.059967,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+47.981464,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+48.397331,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+48.387394,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+48.302265,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+47.619839,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+47.661945,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+47.609917,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+47.982658,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+47.962666,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+48.015163,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+48.295593,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+48.310127,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+48.380020,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+47.648434,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+47.682293,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+47.661728,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+48.010979,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+47.999489,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+47.973412,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+48.307499,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+48.350727,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+48.361591,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+47.690739,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+47.605228,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+47.631180,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+47.990154,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+48.012112,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+47.992966,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+48.350491,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+48.292965,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+48.379539,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+47.698803,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+47.702671,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+47.626671,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+47.955826,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+48.009613,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+47.937950,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+48.335545,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+48.390800,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+48.331474,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+48.716480,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+48.623821,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+48.733986,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+48.960915,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+48.945244,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+48.978504,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+49.346893,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+49.388523,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+49.310513,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+48.631317,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+48.735088,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+48.652313,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+48.988537,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+48.938858,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+48.941730,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+49.316799,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+49.300362,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+49.370628,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+48.673096,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+48.691753,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+48.697811,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+49.012577,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+49.024475,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+48.958012,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+49.287712,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+49.313313,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+49.367317,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+48.614605,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+48.678799,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+48.656208,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+48.945969,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+48.989525,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+49.057896,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+49.282978,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+49.275352,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+49.302803,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+48.611649,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+48.624172,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+48.637882,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+48.993904,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+48.961384,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+48.947807,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+49.273899,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+49.292019,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+49.317478,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+48.656464,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+48.713917,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+48.734833,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+48.937801,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+49.029655,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+49.047318,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+49.392506,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+49.395107,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+49.397663,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+48.618313,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+48.615440,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+48.690914,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+48.971497,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+48.988041,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+49.000229,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+49.313232,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+49.386593,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+49.337692,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+48.661903,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+48.605679,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+48.693295,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+48.992352,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+48.941692,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+49.031334,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+49.351669,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+49.274239,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+49.316685,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+48.720699,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+48.682846,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+48.680271,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+49.033054,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+48.946667,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+48.949692,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+49.308414,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+49.344860,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+49.333214,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+48.717094,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+48.725288,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+48.638515,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+48.941586,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+48.953323,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+48.968121,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+49.280514,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+49.292976,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+49.343239,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+48.726635,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+48.653942,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+48.713566,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+49.040932,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+49.058571,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+49.043633,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+49.278084,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+49.388290,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+49.379711,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+48.716511,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+48.729721,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+48.675732,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+49.040447,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+49.033958,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+48.963776,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+49.305309,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+49.389641,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+49.372097,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+49.705498,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+49.695965,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+49.620506,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+50.059479,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+49.947498,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+49.991550,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+50.349781,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+50.382607,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+50.356319,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+49.677635,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+49.685455,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+49.690193,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+49.993050,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+49.998989,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+49.982254,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+50.289734,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+50.275799,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+50.326683,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+49.658321,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+49.674042,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+49.610775,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+49.978592,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+50.019089,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+50.063732,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+50.305981,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+50.289906,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+50.364227,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+49.630993,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+49.707890,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+49.720825,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+49.988266,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+49.991985,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+50.042610,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+50.359509,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+50.286755,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+50.334942,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+49.672413,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+49.607201,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+49.702644,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+50.064842,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+50.006466,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+50.064621,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+50.305660,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+50.377014,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+50.313873,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+49.700172,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+49.633633,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+49.711540,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+49.950687,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+50.032253,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+50.028549,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+50.385937,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+50.369335,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+50.314556,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+49.615562,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+49.678707,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+49.649204,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+50.067486,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+50.046104,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+50.021000,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+50.371986,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+50.375145,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+50.356426,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+49.649418,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+49.649906,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+49.675701,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+49.983490,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+49.950081,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+49.967571,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+50.365299,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+50.323849,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+50.367722,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+49.639637,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+49.701443,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+49.633968,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+50.040974,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+50.027096,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+50.060658,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+50.376812,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+50.337978,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+50.382313,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+49.676273,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+49.628674,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+49.671150,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+49.946659,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+50.007149,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+50.022682,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+50.332314,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+50.284233,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+50.340729,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+49.730743,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+49.622456,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+49.637196,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+50.025772,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+49.955975,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+50.038589,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+50.299374,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+50.290150,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+50.327991,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+49.605602,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+49.613487,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+49.624001,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+50.035244,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+49.938637,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+50.035923,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+50.305851,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+50.310799,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+50.280338,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+50.735325,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+50.606846,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+50.736511,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+51.027554,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+51.023880,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+50.982521,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+51.350647,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+51.381878,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+51.278492,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+50.692207,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+50.669872,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+50.704826,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+51.001846,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+50.988876,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+50.981613,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+51.369946,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+51.291382,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+51.331699,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+50.721836,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+50.628307,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+50.657558,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+51.004269,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+51.015385,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+50.994194,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+51.316860,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+51.330624,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+51.319775,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+50.645603,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+50.731033,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+50.635216,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+51.004063,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+51.022362,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+50.972652,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+51.279488,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+51.373104,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+51.277416,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+50.615044,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+50.686710,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+50.671741,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+51.049969,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+50.949337,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+51.010189,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+51.365547,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+51.319122,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+51.400730,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+50.630699,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+50.706585,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+50.699154,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+50.992687,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+50.959564,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+51.007416,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+51.293850,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+51.376007,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+51.283459,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+50.641693,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+50.735149,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+50.697292,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+51.010830,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+50.966198,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+50.940189,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+51.349045,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+51.338211,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+51.318806,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+50.677929,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+50.695293,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+50.721634,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+50.973774,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+51.068050,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+50.995678,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+51.366947,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+51.283356,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+51.389118,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+50.657066,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+50.726471,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+50.672272,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+51.058365,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+50.989578,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+51.036560,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+51.310898,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+51.394413,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+51.352856,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+50.690769,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+50.717522,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+50.644562,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+50.959801,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+51.011539,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+51.056538,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+51.280529,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+51.363224,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+51.322338,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+50.628414,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+50.697929,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+50.687038,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+51.050968,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+51.053917,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+50.999794,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+51.356171,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+51.354500,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+51.380459,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+50.706757,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+50.666031,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+50.622726,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+50.958282,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+50.969242,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+51.001385,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+51.302269,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+51.301964,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+51.353413,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+51.630939,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+51.699631,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+51.621857,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+51.982479,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+52.006050,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+52.005356,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+52.317818,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+52.319283,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+52.375954,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+51.610088,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+51.639099,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+51.636589,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+52.020035,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+52.061108,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+51.971542,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+52.345848,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+52.277534,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+52.334526,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+51.656239,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+51.729053,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+51.708714,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+52.007343,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+52.049263,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+52.061005,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+52.308758,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+52.298431,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+52.275700,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+51.728413,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+51.631313,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+51.619251,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+51.977493,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+52.031570,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+52.011909,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+52.317066,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+52.402222,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+52.357014,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+51.625847,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+51.648270,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+51.678558,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+52.030586,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+51.977901,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+52.022007,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+52.351101,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+52.306030,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+52.297516,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+51.647877,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+51.638260,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+51.717335,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+52.054508,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+51.963486,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+51.953827,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+52.305538,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+52.288986,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+52.273224,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+51.700977,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+51.682968,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+51.628822,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+52.020374,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+52.024208,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+52.000214,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+52.322063,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+52.291607,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+52.328602,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+51.671711,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+51.614861,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+51.617588,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+52.060966,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+51.972118,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+51.938828,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+52.386921,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+52.274612,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+52.368568,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+51.623245,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+51.696804,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+51.674152,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+52.029579,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+51.965565,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+52.010998,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+52.298222,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+52.336948,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+52.392838,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+51.624203,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+51.640831,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+51.699501,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+52.031651,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+52.049210,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+52.035568,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+52.295338,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+52.325520,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+52.383965,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+51.730610,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+51.700996,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+51.690926,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+52.006500,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+52.069317,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+51.993073,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+52.305397,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+52.291660,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+52.351475,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+51.614635,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+51.662369,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+51.674007,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+52.027401,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+51.946407,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+52.023781,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+52.278072,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+52.372562,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+52.375191,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+52.694954,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+52.695595,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+52.632366,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+52.990845,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+52.962437,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+53.011494,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+53.389458,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+53.274094,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+53.306568,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+52.608284,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+52.617493,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+52.728409,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+52.942303,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+53.030140,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+52.955906,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+53.377899,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+53.366997,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+53.349979,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+52.713860,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+52.698376,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+52.711193,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+52.974949,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+52.943798,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+52.974014,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+53.367130,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+53.370838,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+53.323532,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+52.677361,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+52.649460,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+52.626347,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+53.015766,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+52.967113,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+52.957211,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+53.312572,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+53.351154,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+53.318241,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+52.615887,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+52.618355,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+52.656731,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+53.016167,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+53.007858,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+53.002716,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+53.376572,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+53.358536,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+53.296841,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+52.615543,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+52.704777,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+52.686710,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+53.038116,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+53.015533,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+52.980106,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+53.386974,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+53.371235,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+53.324871,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+52.693130,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+52.618008,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+52.666336,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+53.011459,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+52.965286,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+52.975243,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+53.306725,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+53.369045,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+53.371475,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+52.719723,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+52.720478,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+52.713551,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+52.967709,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+53.033043,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+52.970211,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+53.316547,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+53.277119,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+53.324535,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+52.720387,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+52.679905,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+52.680450,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+52.949619,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+53.050026,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+53.041424,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+53.283997,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+53.394337,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+53.388470,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+52.712414,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+52.662300,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+52.624870,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+53.030212,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+53.069317,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+52.971638,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+53.375839,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+53.342739,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+53.329563,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+52.737240,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+52.700356,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+52.654903,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+53.062889,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+53.056408,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+53.059891,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+53.317554,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+53.271595,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+53.350712,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+52.649971,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+52.684444,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+52.610802,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+52.960007,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+53.036541,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+52.998898,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+53.367245,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+53.352997,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+53.395786,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+53.670612,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+53.632431,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+53.657814,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+53.943684,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+53.954571,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+53.987125,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+54.390480,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+54.291046,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+54.358166,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+53.614635,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+53.735630,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+53.608078,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+54.000977,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+54.026829,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+53.991798,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+54.377228,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+54.312450,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+54.381931,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+53.651970,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+53.687832,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+53.712574,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+54.038002,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+54.064621,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+54.009644,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+54.276558,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+54.365456,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+54.382923,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+53.633945,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+53.676472,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+53.716320,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+53.984699,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+53.950790,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+54.028702,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+54.357204,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+54.376453,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+54.273373,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+53.644760,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+53.688423,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+53.691917,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+53.950211,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+53.976982,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+53.948948,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+54.388542,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+54.363510,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+54.371674,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+53.643742,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+53.625565,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+53.646152,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+54.014378,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+54.056015,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+53.979465,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+54.294003,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+54.296844,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+54.383389,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+53.729374,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+53.650967,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+53.695461,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+54.063984,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+54.045067,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+53.941700,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+54.297188,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+54.333500,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+54.356293,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+53.727497,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+53.712429,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+53.649803,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+54.017570,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+54.002716,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+54.055298,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+54.296902,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+54.360882,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+54.296490,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+53.613083,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+53.685211,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+53.671467,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+53.978893,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+54.011002,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+53.956844,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+54.282894,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+54.387108,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+54.281097,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+53.626904,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+53.685349,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+53.632969,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+54.016342,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+53.976845,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+54.005035,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+54.274376,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+54.361469,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+54.325382,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+53.643448,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+53.693054,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+53.648968,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+53.966736,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+54.070553,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+54.033905,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+54.292656,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+54.280853,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+54.391315,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+53.636917,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+53.642380,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+53.613216,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+53.996807,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+54.069500,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+53.945423,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+54.305920,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+54.379189,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+54.372742,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+54.693924,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+54.676086,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+54.613514,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+54.952682,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+54.976219,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+54.962502,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+55.307735,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+55.290672,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+55.396164,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+54.710449,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+54.624012,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+54.626160,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+54.999294,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+55.019798,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+55.057869,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+55.402096,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+55.398117,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+55.348492,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+54.639481,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+54.647446,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+54.651363,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+55.039425,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+54.959030,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+55.033867,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+55.360237,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+55.352444,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+55.276131,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+54.688534,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+54.715374,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+54.642300,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+55.025833,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+55.040443,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+54.949482,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+55.302265,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+55.294678,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+55.270836,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+54.697056,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+54.712547,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+54.721497,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+55.026173,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+54.962898,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+55.056728,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+55.270905,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+55.403297,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+55.381920,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+54.724922,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+54.716347,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+54.708069,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+55.067360,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+55.064331,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+55.034916,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+55.351192,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+55.386513,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+55.315056,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+54.610527,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+54.614216,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+54.654854,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+55.018661,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+54.964916,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+54.987629,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+55.336491,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+55.332569,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+55.275803,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+54.722462,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+54.716061,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+54.707489,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+55.064869,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+55.058994,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+54.983269,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+55.341949,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+55.367432,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+55.379936,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+54.622005,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+54.691021,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+54.673626,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+55.018864,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+54.957203,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+55.028900,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+55.341743,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+55.383537,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+55.371124,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+54.651810,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+54.644814,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+54.612583,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+55.011436,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+55.060234,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+55.069397,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+55.294201,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+55.299255,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+55.322685,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+54.722061,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+54.667282,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+54.692017,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+55.060532,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+55.058987,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+55.033676,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+55.278572,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+55.403397,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+55.289310,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+54.707287,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+54.657356,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+54.685635,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+55.028938,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+55.067745,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+54.995712,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+55.388107,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+55.391640,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+55.395531,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+55.730061,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+55.696907,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+55.691681,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+55.997250,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+56.032532,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+56.023365,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+56.354500,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+56.330463,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+56.371552,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+55.714981,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+55.661247,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+55.668182,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+56.039322,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+56.053524,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+55.960644,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+56.358589,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+56.328312,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+56.299282,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+55.647221,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+55.611225,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+55.618931,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+56.011883,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+55.981541,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+55.959618,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+56.333344,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+56.331421,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+56.379055,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+55.664707,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+55.634380,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+55.676636,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+56.032452,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+55.972507,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+55.960548,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+56.318226,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+56.369339,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+56.302654,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+55.663589,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+55.607910,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+55.662567,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+55.978821,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+55.996094,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+56.019905,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+56.370995,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+56.368313,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+56.372780,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+55.683880,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+55.718498,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+55.733231,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+56.058517,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+55.951508,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+55.958839,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+56.279301,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+56.383095,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+56.324730,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+55.717930,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+55.622799,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+55.666920,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+55.937389,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+55.954536,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+56.016182,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+56.385044,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+56.376297,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+56.387276,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+55.653511,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+55.698254,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+55.673859,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+56.045708,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+56.019489,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+55.974483,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+56.296539,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+56.336082,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+56.323605,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+55.684284,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+55.622993,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+55.664070,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+56.041676,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+56.050884,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+55.959030,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+56.360207,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+56.344063,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+56.333351,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+55.714115,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+55.700523,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+55.659512,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+56.056271,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+55.996601,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+56.010624,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+56.354332,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+56.381252,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+56.339428,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+55.616741,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+55.697853,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+55.709442,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+55.958920,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+55.982941,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+56.016197,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+56.327213,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+56.287704,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+56.344288,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+55.724167,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+55.693863,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+55.666615,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+55.947884,+0.500000] , 1. [+0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+56.029186,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+55.953136,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+56.369884,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+56.359581,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+56.388435,+0.500000] , 1. [-0.000000,-0.832000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-7.txt b/scenes/cell-growth/results/particles-frame-7.txt deleted file mode 100644 index 9d2bf78c..00000000 --- a/scenes/cell-growth/results/particles-frame-7.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+42.762943,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+42.864189,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+42.836521,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+43.092815,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+43.181091,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+43.092571,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+43.440151,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+43.528465,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+43.493465,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+42.847771,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+42.816586,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+42.866772,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+43.081100,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+43.113625,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+43.117359,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+43.532421,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+43.432228,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+43.497471,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+42.846802,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+42.809517,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+42.846985,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+43.090969,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+43.140598,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+43.082928,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+43.435616,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+43.498962,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+43.474445,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+42.837284,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+42.842262,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+42.790382,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+43.133102,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+43.189869,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+43.126686,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+43.464115,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+43.456715,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+43.427059,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+42.812229,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+42.834015,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+42.763439,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+43.131393,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+43.174767,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+43.123268,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+43.539993,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+43.471275,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+43.458508,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+42.757393,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+42.752106,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+42.820454,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+43.075592,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+43.133060,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+43.086277,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+43.466476,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+43.416901,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+43.432453,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+42.873032,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+42.793205,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+42.779018,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+43.181412,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+43.175339,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+43.147297,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+43.515800,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+43.451889,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+43.491539,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+42.747635,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+42.833912,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+42.864498,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+43.103786,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+43.105145,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+43.083614,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+43.530518,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+43.426201,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+43.493771,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+42.796303,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+42.774681,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+42.778503,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+43.084492,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+43.074051,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+43.097832,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+43.488033,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+43.463112,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+43.477921,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+42.765907,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+42.780655,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+42.762890,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+43.201374,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+43.076267,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+43.182308,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+43.487362,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+43.441311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+43.457127,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+42.811367,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+42.854225,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+42.865921,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+43.170818,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+43.087944,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+43.117554,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+43.520821,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+43.506275,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+43.418491,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+42.826801,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+42.841873,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+42.811840,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+43.173687,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+43.136177,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+43.114491,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+43.485935,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+43.522907,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+43.413361,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+43.770924,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+43.838879,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+43.784336,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+44.132690,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+44.176109,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+44.103451,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+44.430477,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+44.487278,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+44.407593,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+43.820179,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+43.781025,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+43.743713,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+44.133224,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+44.139549,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+44.185894,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+44.456642,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+44.495514,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+44.465240,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+43.747990,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+43.783989,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+43.833725,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+44.191410,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+44.106865,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+44.079704,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+44.504902,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+44.434429,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+44.423439,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+43.778252,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+43.757507,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+43.757511,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+44.096317,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+44.180882,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+44.087997,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+44.465691,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+44.499485,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+44.480476,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+43.815445,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+43.752045,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+43.871552,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+44.127579,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+44.120811,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+44.104240,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+44.439388,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+44.532890,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+44.500435,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+43.762341,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+43.756992,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+43.778934,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+44.134602,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+44.202839,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+44.181824,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+44.521767,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+44.462147,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+44.413200,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+43.767670,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+43.787613,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+43.763855,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+44.097778,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+44.078079,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+44.192200,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+44.499416,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+44.507278,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+44.491829,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+43.791439,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+43.755253,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+43.750172,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+44.193573,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+44.197205,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+44.098061,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+44.501831,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+44.442291,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+44.502773,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+43.778984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+43.846237,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+43.798653,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+44.094505,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+44.081409,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+44.205647,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+44.416229,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+44.409706,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+44.461864,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+43.767429,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+43.755665,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+43.866070,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+44.180214,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+44.142471,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+44.176960,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+44.440453,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+44.457508,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+44.475380,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+43.853561,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+43.791264,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+43.747272,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+44.138653,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+44.118732,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+44.134472,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+44.458954,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+44.517612,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+44.431480,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+43.772785,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+43.843643,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+43.803211,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+44.166203,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+44.170906,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+44.108208,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+44.472622,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+44.461845,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+44.434727,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+44.802818,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+44.762749,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+44.824875,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+45.164177,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+45.143082,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+45.198647,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+45.525146,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+45.410774,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+45.432243,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+44.792679,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+44.863045,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+44.866699,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+45.140190,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+45.083210,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+45.118240,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+45.486164,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+45.516045,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+45.435280,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+44.776924,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+44.760323,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+44.782944,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+45.192448,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+45.099564,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+45.074581,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+45.412369,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+45.418793,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+45.468929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+44.764149,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+44.843357,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+44.843483,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+45.096119,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+45.203362,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+45.154709,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+45.518978,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+45.427391,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+45.483459,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+44.770748,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+44.857803,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+44.828918,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+45.185726,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+45.165550,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+45.094154,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+45.470444,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+45.482342,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+45.500343,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+44.770042,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+44.809101,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+44.749260,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+45.133717,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+45.088051,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+45.129204,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+45.461277,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+45.476337,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+45.424984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+44.823818,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+44.764477,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+44.767292,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+45.112087,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+45.127773,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+45.194244,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+45.451420,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+45.538448,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+45.518314,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+44.855515,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+44.740547,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+44.872242,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+45.187328,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+45.085316,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+45.161064,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+45.455154,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+45.450653,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+45.489613,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+44.754967,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+44.783123,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+44.806747,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+45.196465,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+45.188049,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+45.115307,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+45.498184,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+45.494488,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+45.524143,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+44.870209,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+44.785618,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+44.807213,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+45.206493,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+45.086346,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+45.153114,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+45.441189,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+45.508793,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+45.409199,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+44.806423,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+44.740345,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+44.762283,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+45.118393,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+45.190964,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+45.163506,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+45.453106,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+45.480476,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+45.461456,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+44.778534,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+44.745461,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+44.857754,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+45.082668,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+45.204212,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+45.159206,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+45.457733,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+45.453369,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+45.465164,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+45.842102,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+45.803146,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+45.806683,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+46.192032,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+46.164917,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+46.082146,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+46.507896,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+46.419678,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+46.464279,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+45.870735,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+45.852596,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+45.755386,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+46.132374,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+46.115067,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+46.181770,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+46.494854,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+46.407207,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+46.413506,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+45.781654,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+45.806549,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+45.783245,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+46.150719,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+46.146069,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+46.148682,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+46.500324,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+46.517025,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+46.456913,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+45.795784,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+45.845272,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+45.829605,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+46.139484,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+46.170826,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+46.157146,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+46.409176,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+46.458935,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+46.518612,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+45.843922,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+45.797733,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+45.866634,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+46.138214,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+46.154057,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+46.076664,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+46.417389,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+46.521252,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+46.443134,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+45.797794,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+45.776421,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+45.822315,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+46.205593,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+46.090088,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+46.118622,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+46.415695,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+46.497547,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+46.424419,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+45.779819,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+45.819340,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+45.776276,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+46.138321,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+46.078991,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+46.128841,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+46.478088,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+46.450340,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+46.465652,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+45.809925,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+45.757641,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+45.757679,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+46.173027,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+46.133831,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+46.184616,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+46.421711,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+46.529003,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+46.460114,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+45.762581,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+45.828369,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+45.839558,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+46.147678,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+46.086754,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+46.183891,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+46.535473,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+46.507824,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+46.513477,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+45.863022,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+45.831329,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+45.823570,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+46.191299,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+46.190819,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+46.130287,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+46.451740,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+46.453148,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+46.499332,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+45.812477,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+45.861176,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+45.771027,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+46.086529,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+46.118481,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+46.134972,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+46.469543,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+46.414753,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+46.502598,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+45.849224,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+45.839561,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+45.773720,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+46.145275,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+46.192635,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+46.147278,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+46.429016,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+46.499245,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+46.422764,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+46.823063,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+46.785408,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+46.849472,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+47.122196,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+47.131390,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+47.091610,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+47.483063,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+47.476662,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+47.433929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+46.795063,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+46.770523,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+46.857449,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+47.170887,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+47.129055,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+47.115971,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+47.482471,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+47.466545,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+47.519634,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+46.822353,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+46.805077,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+46.753246,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+47.180634,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+47.157036,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+47.204109,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+47.435978,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+47.432552,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+47.509399,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+46.762650,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+46.777767,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+46.838352,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+47.205006,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+47.095638,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+47.156422,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+47.426853,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+47.475384,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+47.454067,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+46.778065,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+46.851955,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+46.872566,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+47.170841,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+47.118767,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+47.205921,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+47.424202,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+47.412270,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+47.480473,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+46.777760,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+46.830605,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+46.857079,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+47.109554,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+47.161659,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+47.133415,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+47.477615,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+47.480675,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+47.425957,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+46.754864,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+46.802525,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+46.811363,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+47.153114,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+47.096764,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+47.203716,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+47.427773,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+47.502274,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+47.444057,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+46.820366,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+46.871037,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+46.808205,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+47.111008,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+47.195965,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+47.117462,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+47.533329,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+47.523392,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+47.438263,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+46.755836,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+46.797943,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+46.745914,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+47.118656,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+47.098663,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+47.151161,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+47.431591,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+47.446125,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+47.516018,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+46.784431,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+46.818291,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+46.797726,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+47.146976,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+47.135487,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+47.109409,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+47.443497,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+47.486725,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+47.497589,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+46.826736,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+46.741226,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+46.767178,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+47.126152,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+47.148109,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+47.128963,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+47.486488,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+47.428963,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+47.515537,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+46.834801,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+46.838669,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+46.762669,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+47.091824,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+47.145611,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+47.073948,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+47.471542,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+47.526798,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+47.467472,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+47.852478,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+47.759819,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+47.869984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+48.096912,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+48.081242,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+48.114502,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+48.482891,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+48.524521,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+48.446510,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+47.767315,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+47.871086,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+47.788311,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+48.124535,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+48.074856,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+48.077728,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+48.452797,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+48.436359,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+48.506626,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+47.809093,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+47.827751,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+47.833809,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+48.148575,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+48.160473,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+48.094009,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+48.423710,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+48.449310,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+48.503315,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+47.750603,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+47.814796,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+47.792206,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+48.081966,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+48.125523,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+48.193893,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+48.418976,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+48.411350,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+48.438801,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+47.747646,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+47.760170,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+47.773880,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+48.129902,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+48.097382,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+48.083805,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+48.409897,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+48.428017,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+48.453476,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+47.792461,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+47.849915,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+47.870831,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+48.073799,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+48.165653,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+48.183315,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+48.528503,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+48.531105,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+48.533661,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+47.754311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+47.751442,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+47.826912,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+48.107494,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+48.124039,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+48.136227,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+48.449230,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+48.522591,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+48.473690,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+47.797901,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+47.741676,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+47.829292,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+48.128349,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+48.077690,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+48.167332,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+48.487667,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+48.410236,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+48.452682,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+47.856697,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+47.818844,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+47.816269,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+48.169052,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+48.082664,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+48.085690,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+48.444412,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+48.480858,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+48.469212,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+47.853092,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+47.861286,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+47.774513,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+48.077583,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+48.089321,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+48.104118,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+48.416512,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+48.428974,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+48.479237,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+47.862633,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+47.789940,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+47.849564,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+48.176929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+48.194569,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+48.179630,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+48.414082,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+48.524288,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+48.515709,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+47.852509,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+47.865719,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+47.811729,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+48.176445,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+48.169956,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+48.099773,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+48.441307,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+48.525639,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+48.508095,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+48.841496,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+48.831963,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+48.756504,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+49.195477,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+49.083496,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+49.127548,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+49.485779,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+49.518604,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+49.492317,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+48.813633,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+48.821453,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+48.826191,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+49.129047,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+49.134987,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+49.118252,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+49.425732,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+49.411797,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+49.462681,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+48.794319,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+48.810040,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+48.746773,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+49.114590,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+49.155087,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+49.199730,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+49.441978,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+49.425903,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+49.500225,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+48.766991,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+48.843887,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+48.856823,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+49.124264,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+49.127983,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+49.178608,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+49.495506,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+49.422752,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+49.470940,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+48.808411,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+48.743198,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+48.838642,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+49.200840,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+49.142464,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+49.200619,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+49.441658,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+49.513012,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+49.449871,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+48.836170,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+48.769630,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+48.847538,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+49.086685,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+49.168251,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+49.164547,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+49.521935,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+49.505333,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+49.450554,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+48.751560,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+48.814705,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+48.785202,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+49.203484,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+49.182102,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+49.156998,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+49.507984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+49.511143,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+49.492424,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+48.785416,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+48.785904,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+48.811699,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+49.119488,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+49.086079,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+49.103569,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+49.501297,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+49.459846,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+49.503719,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+48.775635,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+48.837440,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+48.769966,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+49.176971,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+49.163094,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+49.196655,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+49.512810,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+49.473976,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+49.518311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+48.812271,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+48.764671,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+48.807148,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+49.082657,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+49.143147,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+49.158680,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+49.468311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+49.420231,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+49.476727,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+48.866741,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+48.758453,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+48.773193,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+49.161770,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+49.091972,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+49.174587,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+49.435371,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+49.426147,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+49.463989,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+48.741600,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+48.749485,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+48.759998,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+49.171242,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+49.074635,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+49.171921,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+49.441849,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+49.446796,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+49.416336,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+49.871323,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+49.742844,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+49.872509,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+50.163551,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+50.159878,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+50.118519,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+50.486645,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+50.517876,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+50.414490,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+49.828205,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+49.805870,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+49.840824,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+50.137844,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+50.124874,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+50.117611,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+50.505943,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+50.427380,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+50.467697,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+49.857834,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+49.764305,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+49.793556,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+50.140266,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+50.151382,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+50.130192,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+50.452858,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+50.466621,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+50.455772,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+49.781601,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+49.867031,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+49.771214,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+50.140060,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+50.158360,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+50.108650,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+50.415485,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+50.509102,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+50.413414,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+49.751041,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+49.822708,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+49.807739,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+50.185966,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+50.085335,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+50.146187,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+50.501545,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+50.455120,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+50.536728,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+49.766697,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+49.842583,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+49.835152,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+50.128685,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+50.095562,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+50.143414,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+50.429848,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+50.512005,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+50.419456,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+49.777691,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+49.871147,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+49.833290,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+50.146828,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+50.102196,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+50.076187,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+50.485043,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+50.474209,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+50.454803,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+49.813927,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+49.831291,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+49.857632,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+50.109772,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+50.204048,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+50.131676,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+50.502945,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+50.419353,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+50.525116,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+49.793064,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+49.862469,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+49.808270,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+50.194363,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+50.125576,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+50.172558,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+50.446896,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+50.530411,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+50.488853,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+49.826767,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+49.853519,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+49.780560,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+50.095798,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+50.147537,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+50.192535,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+50.416527,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+50.499222,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+50.458336,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+49.764412,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+49.833927,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+49.823036,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+50.186966,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+50.189915,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+50.135792,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+50.492168,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+50.490498,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+50.516457,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+49.842754,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+49.802029,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+49.758724,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+50.094280,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+50.105240,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+50.137383,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+50.438267,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+50.437962,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+50.489410,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+50.766937,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+50.835629,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+50.757854,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+51.118477,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+51.142048,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+51.141354,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+51.453815,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+51.455280,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+51.511951,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+50.746086,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+50.775097,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+50.772587,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+51.156033,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+51.197105,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+51.107540,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+51.481846,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+51.413532,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+51.470524,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+50.792236,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+50.865051,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+50.844711,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+51.143341,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+51.185261,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+51.197002,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+51.444756,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+51.434429,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+51.411697,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+50.864410,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+50.767311,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+50.755249,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+51.113491,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+51.167568,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+51.147907,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+51.453064,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+51.538219,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+51.493011,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+50.761845,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+50.784267,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+50.814556,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+51.166584,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+51.113899,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+51.158005,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+51.487099,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+51.442028,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+51.433514,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+50.783875,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+50.774258,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+50.853333,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+51.190506,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+51.099483,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+51.089825,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+51.441536,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+51.424984,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+51.409222,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+50.836975,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+50.818966,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+50.764820,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+51.156372,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+51.160206,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+51.136211,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+51.458061,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+51.427605,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+51.464600,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+50.807709,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+50.750858,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+50.753586,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+51.196964,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+51.108116,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+51.074825,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+51.522919,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+51.410610,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+51.504566,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+50.759243,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+50.832802,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+50.810150,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+51.165577,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+51.101562,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+51.146996,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+51.434219,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+51.472946,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+51.528835,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+50.760201,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+50.776829,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+50.835499,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+51.167648,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+51.185207,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+51.171566,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+51.431335,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+51.461517,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+51.519962,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+50.866608,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+50.836994,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+50.826923,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+51.142498,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+51.205315,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+51.129070,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+51.441395,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+51.427658,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+51.487473,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+50.750633,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+50.798367,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+50.810005,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+51.163399,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+51.082405,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+51.159779,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+51.414070,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+51.508560,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+51.511189,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+51.830952,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+51.831593,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+51.768364,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+52.126842,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+52.098434,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+52.147491,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+52.525459,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+52.410091,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+52.442566,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+51.744282,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+51.753490,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+51.864407,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+52.078300,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+52.166138,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+52.091904,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+52.513897,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+52.502995,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+52.485977,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+51.849857,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+51.834373,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+51.847191,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+52.110947,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+52.079796,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+52.110012,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+52.503128,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+52.506836,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+52.459530,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+51.813358,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+51.785458,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+51.762344,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+52.151764,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+52.103111,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+52.093208,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+52.448570,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+52.487152,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+52.454239,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+51.751884,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+51.754353,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+51.792728,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+52.152164,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+52.143856,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+52.138714,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+52.512569,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+52.494534,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+52.432838,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+51.751541,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+51.840775,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+51.822708,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+52.174114,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+52.151531,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+52.116104,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+52.522972,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+52.507233,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+52.460869,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+51.829128,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+51.754005,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+51.802334,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+52.147457,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+52.101284,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+52.111240,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+52.442722,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+52.505043,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+52.507473,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+51.855721,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+51.856476,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+51.849548,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+52.103706,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+52.169041,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+52.106209,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+52.452545,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+52.413116,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+52.460533,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+51.856384,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+51.815903,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+51.816448,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+52.085617,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+52.186024,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+52.177422,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+52.419994,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+52.530334,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+52.524467,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+51.848412,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+51.798298,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+51.760868,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+52.166210,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+52.205315,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+52.107635,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+52.511837,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+52.478737,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+52.465561,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+51.873238,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+51.836353,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+51.790901,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+52.198887,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+52.192406,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+52.195889,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+52.453552,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+52.407593,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+52.486710,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+51.785969,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+51.820442,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+51.746799,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+52.096004,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+52.172539,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+52.134895,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+52.503242,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+52.488995,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+52.531784,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+52.806610,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+52.768429,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+52.793812,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+53.079681,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+53.090569,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+53.123123,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+53.526478,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+53.427048,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+53.494164,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+52.750633,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+52.871628,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+52.744076,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+53.136974,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+53.162827,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+53.127796,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+53.513226,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+53.448448,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+53.517929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+52.787968,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+52.823830,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+52.848572,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+53.174000,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+53.200619,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+53.145641,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+53.412556,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+53.501453,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+53.518921,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+52.769943,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+52.812469,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+52.852318,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+53.120697,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+53.086788,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+53.164700,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+53.493202,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+53.512451,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+53.409370,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+52.780758,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+52.824421,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+52.827915,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+53.086208,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+53.112980,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+53.084946,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+53.524540,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+53.499508,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+53.507671,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+52.779739,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+52.761562,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+52.782150,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+53.150375,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+53.192013,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+53.115463,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+53.430000,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+53.432842,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+53.519386,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+52.865372,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+52.786964,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+52.831459,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+53.199982,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+53.181065,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+53.077698,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+53.433186,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+53.469498,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+53.492290,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+52.863495,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+52.848427,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+52.785801,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+53.153568,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+53.138714,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+53.191296,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+53.432899,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+53.496880,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+53.432487,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+52.749081,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+52.821209,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+52.807465,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+53.114891,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+53.146999,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+53.092842,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+53.418892,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+53.523106,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+53.417095,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+52.762901,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+52.821346,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+52.768967,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+53.152340,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+53.112843,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+53.141033,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+53.410374,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+53.497467,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+53.461380,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+52.779446,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+52.829052,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+52.784966,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+53.102734,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+53.206551,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+53.169903,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+53.428654,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+53.416851,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+53.527313,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+52.772915,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+52.778378,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+52.749214,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+53.132805,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+53.205498,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+53.081421,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+53.441917,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+53.515186,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+53.508739,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+53.829922,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+53.812084,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+53.749512,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+54.088680,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+54.112217,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+54.098499,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+54.443733,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+54.426670,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+54.532162,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+53.846447,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+53.760010,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+53.762157,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+54.135292,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+54.155796,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+54.193867,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+54.538094,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+54.534115,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+54.484489,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+53.775478,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+53.783443,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+53.787361,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+54.175423,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+54.095028,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+54.169865,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+54.496235,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+54.488441,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+54.412128,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+53.824532,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+53.851372,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+53.778297,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+54.161831,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+54.176441,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+54.085480,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+54.438263,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+54.430676,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+54.406834,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+53.833054,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+53.848545,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+53.857494,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+54.162170,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+54.098896,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+54.192726,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+54.406902,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+54.539295,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+54.517918,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+53.860920,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+53.852345,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+53.844067,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+54.203358,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+54.200329,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+54.170914,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+54.487190,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+54.522511,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+54.451054,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+53.746525,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+53.750214,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+53.790852,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+54.154659,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+54.100914,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+54.123627,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+54.472488,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+54.468567,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+54.411800,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+53.858459,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+53.852058,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+53.843487,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+54.200867,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+54.194992,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+54.119267,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+54.477947,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+54.503429,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+54.515934,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+53.758003,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+53.827019,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+53.809624,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+54.154861,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+54.093201,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+54.164898,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+54.477741,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+54.519535,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+54.507122,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+53.787807,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+53.780811,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+53.748581,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+54.147434,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+54.196232,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+54.205395,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+54.430199,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+54.435253,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+54.458683,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+53.858059,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+53.803280,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+53.828014,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+54.196529,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+54.194984,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+54.169674,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+54.414570,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+54.539394,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+54.425308,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+53.843285,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+53.793354,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+53.821632,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+54.164936,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+54.203743,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+54.131710,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+54.524105,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+54.527637,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+54.531528,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+54.866058,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+54.832905,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+54.827679,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+55.133247,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+55.168530,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+55.159363,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+55.490498,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+55.466461,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+55.507549,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+54.850979,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+54.797245,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+54.804180,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+55.175320,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+55.189522,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+55.096642,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+55.494587,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+55.464310,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+55.435280,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+54.783218,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+54.747223,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+54.754929,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+55.147881,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+55.117538,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+55.095615,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+55.469341,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+55.467419,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+55.515053,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+54.800705,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+54.770378,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+54.812634,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+55.168449,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+55.108505,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+55.096546,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+55.454224,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+55.505337,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+55.438652,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+54.799587,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+54.743908,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+54.798565,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+55.114819,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+55.132092,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+55.155903,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+55.506992,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+55.504311,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+55.508778,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+54.819878,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+54.854496,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+54.869228,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+55.194515,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+55.087505,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+55.094837,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+55.415298,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+55.519093,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+55.460728,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+54.853928,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+54.758797,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+54.802917,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+55.073387,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+55.090534,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+55.152180,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+55.521042,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+55.512295,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+55.523273,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+54.789509,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+54.834251,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+54.809856,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+55.181705,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+55.155487,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+55.110481,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+55.432537,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+55.472080,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+55.459602,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+54.820282,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+54.758991,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+54.800072,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+55.177673,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+55.186882,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+55.095032,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+55.496204,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+55.480061,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+55.469353,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+54.850113,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+54.836525,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+54.795509,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+55.192268,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+55.132599,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+55.146622,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+55.490330,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+55.517250,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+55.475426,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+54.752739,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+54.833851,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+54.845440,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+55.094917,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+55.118938,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+55.152195,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+55.463211,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+55.423702,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+55.480286,+0.500000] , 1. [+0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+54.860165,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+54.829861,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+54.802612,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+55.083881,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+55.165184,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+55.089134,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+55.505882,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+55.495579,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+55.524433,+0.500000] , 1. [-0.000000,-0.960000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-8.txt b/scenes/cell-growth/results/particles-frame-8.txt deleted file mode 100644 index af466a23..00000000 --- a/scenes/cell-growth/results/particles-frame-8.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+41.770943,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+41.872189,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+41.844521,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+42.100815,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+42.189091,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+42.100571,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+42.448151,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+42.536465,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+42.501465,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+41.855770,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+41.824585,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+41.874771,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+42.089100,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+42.121624,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+42.125359,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+42.540421,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+42.440228,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+42.505470,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+41.854801,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+41.817516,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+41.854984,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+42.098969,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+42.148598,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+42.090927,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+42.443615,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+42.506962,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+42.482445,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+41.845284,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+41.850262,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+41.798382,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+42.141102,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+42.197868,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+42.134686,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+42.472115,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+42.464714,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+42.435059,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+41.820229,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+41.842014,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+41.771439,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+42.139393,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+42.182766,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+42.131268,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+42.547993,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+42.479275,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+42.466507,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+41.765392,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+41.760105,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+41.828453,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+42.083591,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+42.141060,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+42.094276,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+42.474476,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+42.424900,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+42.440453,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+41.881031,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+41.801205,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+41.787018,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+42.189411,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+42.183338,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+42.155296,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+42.523800,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+42.459888,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+42.499538,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+41.755634,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+41.841911,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+41.872498,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+42.111786,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+42.113144,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+42.091614,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+42.538517,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+42.434200,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+42.501770,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+41.804302,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+41.782681,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+41.786503,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+42.092491,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+42.082050,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+42.105831,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+42.496033,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+42.471111,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+42.485920,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+41.773907,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+41.788654,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+41.770889,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+42.209373,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+42.084267,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+42.190308,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+42.495361,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+42.449310,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+42.465126,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+41.819366,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+41.862225,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+41.873920,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+42.178818,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+42.095943,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+42.125553,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+42.528820,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+42.514275,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+42.426491,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+41.834801,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+41.849873,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+41.819839,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+42.181686,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+42.144176,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+42.122490,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+42.493935,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+42.530907,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+42.421360,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+42.778923,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+42.846878,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+42.792336,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+43.140690,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+43.184109,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+43.111450,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+43.438477,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+43.495277,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+43.415592,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+42.828178,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+42.789024,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+42.751713,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+43.141224,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+43.147549,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+43.193893,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+43.464642,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+43.503513,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+43.473240,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+42.755989,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+42.791988,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+42.841724,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+43.199409,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+43.114864,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+43.087704,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+43.512901,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+43.442429,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+43.431438,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+42.786251,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+42.765507,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+42.765511,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+43.104317,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+43.188881,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+43.095997,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+43.473690,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+43.507484,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+43.488476,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+42.823444,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+42.760044,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+42.879551,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+43.135578,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+43.128811,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+43.112240,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+43.447388,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+43.540890,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+43.508434,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+42.770340,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+42.764992,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+42.786934,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+43.142601,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+43.210838,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+43.189823,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+43.529766,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+43.470146,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+43.421200,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+42.775669,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+42.795612,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+42.771854,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+43.105778,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+43.086079,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+43.200199,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+43.507416,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+43.515278,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+43.499828,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+42.799438,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+42.763252,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+42.758171,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+43.201572,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+43.205204,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+43.106060,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+43.509830,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+43.450291,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+43.510773,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+42.786983,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+42.854237,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+42.806652,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+43.102505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+43.089409,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+43.213646,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+43.424229,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+43.417706,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+43.469864,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+42.775429,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+42.763664,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+42.874069,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+43.188213,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+43.150471,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+43.184959,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+43.448452,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+43.465508,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+43.483379,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+42.861561,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+42.799263,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+42.755272,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+43.146652,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+43.126732,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+43.142471,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+43.466953,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+43.525612,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+43.439480,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+42.780785,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+42.851643,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+42.811211,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+43.174202,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+43.178905,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+43.116207,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+43.480621,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+43.469845,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+43.442726,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+43.810818,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+43.770748,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+43.832874,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+44.172176,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+44.151081,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+44.206646,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+44.533146,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+44.418774,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+44.440243,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+43.800678,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+43.871044,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+43.874699,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+44.148190,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+44.091209,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+44.126240,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+44.494164,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+44.524044,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+44.443279,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+43.784924,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+43.768322,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+43.790943,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+44.200447,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+44.107563,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+44.082581,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+44.420368,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+44.426792,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+44.476929,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+43.772148,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+43.851357,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+43.851482,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+44.104118,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+44.211361,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+44.162708,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+44.526978,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+44.435390,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+44.491459,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+43.778748,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+43.865803,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+43.836918,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+44.193726,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+44.173550,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+44.102154,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+44.478443,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+44.490341,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+44.508343,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+43.778042,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+43.817101,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+43.757259,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+44.141716,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+44.096050,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+44.137203,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+44.469276,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+44.484337,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+44.432983,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+43.831818,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+43.772476,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+43.775291,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+44.120087,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+44.135773,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+44.202244,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+44.459419,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+44.546448,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+44.526314,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+43.863514,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+43.748547,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+43.880241,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+44.195328,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+44.093315,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+44.169064,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+44.463154,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+44.458652,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+44.497612,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+43.762966,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+43.791122,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+43.814747,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+44.204464,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+44.196049,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+44.123306,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+44.506184,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+44.502487,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+44.532143,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+43.878208,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+43.793617,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+43.815212,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+44.214493,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+44.094345,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+44.161114,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+44.449188,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+44.516792,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+44.417198,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+43.814423,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+43.748344,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+43.770283,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+44.126392,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+44.198963,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+44.171505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+44.461105,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+44.488476,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+44.469456,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+43.786533,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+43.753460,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+43.865753,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+44.090668,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+44.212212,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+44.167206,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+44.465733,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+44.461369,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+44.473164,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+44.850101,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+44.811146,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+44.814682,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+45.200031,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+45.172916,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+45.090145,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+45.515896,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+45.427677,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+45.472279,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+44.878735,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+44.860596,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+44.763386,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+45.140373,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+45.123066,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+45.189770,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+45.502853,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+45.415207,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+45.421505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+44.789654,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+44.814548,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+44.791245,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+45.158718,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+45.154068,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+45.156681,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+45.508324,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+45.525024,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+45.464912,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+44.803783,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+44.853271,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+44.837605,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+45.147484,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+45.178825,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+45.165146,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+45.417175,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+45.466934,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+45.526611,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+44.851921,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+44.805733,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+44.874634,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+45.146214,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+45.162056,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+45.084663,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+45.425388,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+45.529251,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+45.451134,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+44.805794,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+44.784420,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+44.830315,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+45.213593,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+45.098087,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+45.126621,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+45.423695,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+45.505547,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+45.432419,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+44.787819,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+44.827339,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+44.784275,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+45.146320,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+45.086990,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+45.136841,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+45.486088,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+45.458340,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+45.473652,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+44.817924,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+44.765640,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+44.765678,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+45.181026,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+45.141830,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+45.192616,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+45.429710,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+45.537003,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+45.468113,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+44.770580,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+44.836369,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+44.847557,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+45.155678,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+45.094753,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+45.191891,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+45.543472,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+45.515823,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+45.521477,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+44.871021,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+44.839329,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+44.831570,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+45.199299,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+45.198818,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+45.138287,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+45.459740,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+45.461147,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+45.507332,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+44.820477,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+44.869175,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+44.779026,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+45.094528,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+45.126480,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+45.142971,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+45.477543,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+45.422752,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+45.510597,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+44.857224,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+44.847561,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+44.781719,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+45.153275,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+45.200634,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+45.155277,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+45.437016,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+45.507244,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+45.430763,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+45.831062,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+45.793407,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+45.857471,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+46.130196,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+46.139389,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+46.099609,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+46.491062,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+46.484661,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+46.441929,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+45.803062,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+45.778522,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+45.865448,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+46.178886,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+46.137054,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+46.123970,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+46.490471,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+46.474545,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+46.527634,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+45.830353,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+45.813076,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+45.761246,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+46.188633,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+46.165035,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+46.212109,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+46.443977,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+46.440552,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+46.517399,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+45.770649,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+45.785767,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+45.846352,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+46.213005,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+46.103638,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+46.164421,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+46.434853,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+46.483383,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+46.462067,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+45.786064,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+45.859955,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+45.880566,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+46.178841,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+46.126766,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+46.213921,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+46.432201,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+46.420269,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+46.488472,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+45.785759,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+45.838604,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+45.865078,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+46.117554,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+46.169659,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+46.141415,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+46.485615,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+46.488674,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+46.433956,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+45.762863,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+45.810524,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+45.819363,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+46.161114,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+46.104763,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+46.211716,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+46.435772,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+46.510273,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+46.452057,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+45.828365,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+45.879036,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+45.816204,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+46.119007,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+46.203964,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+46.125462,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+46.541328,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+46.531391,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+46.446262,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+45.763836,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+45.805943,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+45.753914,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+46.126656,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+46.106663,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+46.159161,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+46.439590,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+46.454124,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+46.524017,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+45.792431,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+45.826290,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+45.805725,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+46.154976,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+46.143486,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+46.117409,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+46.451496,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+46.494724,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+46.505589,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+45.834736,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+45.749226,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+45.775177,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+46.134151,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+46.156109,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+46.136963,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+46.494488,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+46.436962,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+46.523537,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+45.842800,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+45.846668,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+45.770668,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+46.099823,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+46.153610,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+46.081947,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+46.479542,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+46.534798,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+46.475471,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+46.860477,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+46.767818,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+46.877983,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+47.104912,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+47.089241,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+47.122501,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+47.490891,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+47.532520,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+47.454510,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+46.775314,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+46.879086,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+46.796310,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+47.132534,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+47.082855,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+47.085728,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+47.460796,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+47.444359,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+47.514626,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+46.817093,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+46.835751,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+46.841808,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+47.156574,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+47.168472,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+47.102009,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+47.431709,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+47.457310,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+47.511314,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+46.758602,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+46.822796,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+46.800205,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+47.089966,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+47.133522,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+47.201893,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+47.426975,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+47.419350,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+47.446800,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+46.755646,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+46.768169,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+46.781879,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+47.137901,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+47.105381,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+47.091805,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+47.417896,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+47.436016,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+47.461475,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+46.800461,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+46.857914,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+46.878830,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+47.081799,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+47.173653,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+47.191315,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+47.536503,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+47.539104,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+47.541660,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+46.762310,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+46.759441,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+46.834911,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+47.115494,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+47.132038,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+47.144226,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+47.457230,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+47.530590,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+47.481689,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+46.805901,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+46.749676,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+46.837292,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+47.136349,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+47.085690,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+47.175331,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+47.495667,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+47.418236,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+47.460682,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+46.864697,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+46.826843,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+46.824268,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+47.177052,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+47.090664,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+47.093689,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+47.452412,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+47.488857,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+47.477211,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+46.861092,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+46.869286,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+46.782513,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+47.085583,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+47.097321,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+47.112118,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+47.424511,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+47.436974,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+47.487236,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+46.870632,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+46.797939,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+46.857563,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+47.184929,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+47.202568,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+47.187630,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+47.422081,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+47.532288,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+47.523708,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+46.860508,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+46.873718,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+46.819729,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+47.184444,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+47.177956,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+47.107773,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+47.449306,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+47.533638,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+47.516094,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+47.849495,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+47.839962,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+47.764503,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+48.203476,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+48.091496,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+48.135548,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+48.493778,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+48.526604,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+48.500317,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+47.821632,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+47.829453,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+47.834190,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+48.137047,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+48.142986,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+48.126251,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+48.433731,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+48.419796,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+48.470680,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+47.802319,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+47.818039,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+47.754772,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+48.122589,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+48.163086,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+48.207729,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+48.449978,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+48.433903,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+48.508224,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+47.774990,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+47.851887,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+47.864822,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+48.132263,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+48.135983,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+48.186607,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+48.503506,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+48.430752,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+48.478939,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+47.816410,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+47.751198,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+47.846642,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+48.208839,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+48.150463,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+48.208618,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+48.449657,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+48.521011,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+48.457870,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+47.844170,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+47.777630,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+47.855537,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+48.094685,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+48.176250,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+48.172546,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+48.529934,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+48.513332,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+48.458553,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+47.759560,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+47.822704,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+47.793201,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+48.211483,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+48.190102,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+48.164997,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+48.515984,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+48.519142,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+48.500423,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+47.793415,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+47.793903,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+47.819698,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+48.127487,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+48.094078,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+48.111568,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+48.509296,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+48.467846,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+48.511719,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+47.783634,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+47.845440,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+47.777966,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+48.184971,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+48.171093,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+48.204655,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+48.520809,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+48.481976,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+48.526310,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+47.820271,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+47.772671,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+47.815147,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+48.090656,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+48.151146,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+48.166679,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+48.476311,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+48.428230,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+48.484726,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+47.874741,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+47.766453,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+47.781193,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+48.169769,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+48.099972,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+48.182587,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+48.443371,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+48.434147,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+48.471989,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+47.749599,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+47.757484,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+47.767998,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+48.179241,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+48.082634,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+48.179920,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+48.449848,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+48.454796,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+48.424335,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+48.879322,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+48.750843,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+48.880508,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+49.171551,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+49.167877,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+49.126518,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+49.494644,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+49.525875,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+49.422489,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+48.836205,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+48.813869,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+48.848824,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+49.145844,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+49.132874,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+49.125610,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+49.513943,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+49.435379,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+49.475697,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+48.865833,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+48.772305,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+48.801556,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+49.148266,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+49.159382,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+49.138191,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+49.460857,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+49.474621,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+49.463772,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+48.789600,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+48.875031,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+48.779213,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+49.148060,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+49.166359,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+49.116650,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+49.423485,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+49.517101,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+49.421413,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+48.759041,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+48.830708,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+48.815739,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+49.193966,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+49.093334,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+49.154186,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+49.509544,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+49.463120,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+49.544727,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+48.774696,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+48.850582,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+48.843151,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+49.136684,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+49.103561,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+49.151413,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+49.437847,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+49.520004,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+49.427456,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+48.785690,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+48.879147,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+48.841290,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+49.154827,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+49.110195,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+49.084187,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+49.493042,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+49.482208,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+49.462803,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+48.821926,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+48.839291,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+48.865631,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+49.117771,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+49.212048,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+49.139675,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+49.510944,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+49.427353,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+49.533115,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+48.801064,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+48.870468,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+48.816269,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+49.202362,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+49.133575,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+49.180557,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+49.454895,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+49.538410,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+49.496853,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+48.834766,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+48.861519,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+48.788559,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+49.103798,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+49.155537,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+49.200535,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+49.424526,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+49.507221,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+49.466335,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+48.772411,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+48.841927,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+48.831036,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+49.194965,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+49.197914,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+49.143791,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+49.500168,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+49.498497,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+49.524456,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+48.850754,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+48.810028,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+48.766724,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+49.102280,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+49.113239,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+49.145382,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+49.446266,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+49.445961,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+49.497410,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+49.774937,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+49.843628,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+49.765854,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+50.126476,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+50.150047,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+50.149353,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+50.461815,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+50.463280,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+50.519951,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+49.754086,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+49.783096,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+49.780586,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+50.164032,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+50.205105,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+50.115540,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+50.489845,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+50.421532,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+50.478523,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+49.800236,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+49.873051,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+49.852711,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+50.151340,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+50.193260,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+50.205002,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+50.452755,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+50.442429,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+50.419697,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+49.872410,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+49.775311,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+49.763248,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+50.121490,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+50.175568,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+50.155907,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+50.461063,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+50.546219,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+50.501011,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+49.769844,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+49.792267,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+49.822556,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+50.174583,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+50.121899,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+50.166004,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+50.495098,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+50.450027,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+50.441513,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+49.791874,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+49.782257,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+49.861332,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+50.198505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+50.107483,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+50.097824,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+50.449535,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+50.432983,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+50.417221,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+49.844975,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+49.826965,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+49.772820,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+50.164371,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+50.168205,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+50.144211,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+50.466061,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+50.435604,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+50.472599,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+49.815708,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+49.758858,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+49.761585,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+50.204964,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+50.116116,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+50.082825,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+50.530918,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+50.418610,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+50.512566,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+49.767242,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+49.840801,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+49.818150,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+50.173576,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+50.109562,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+50.154995,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+50.442219,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+50.480946,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+50.536835,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+49.768200,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+49.784828,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+49.843498,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+50.175648,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+50.193207,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+50.179565,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+50.439335,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+50.469517,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+50.527962,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+49.874607,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+49.844994,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+49.834923,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+50.150497,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+50.213314,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+50.137070,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+50.449394,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+50.435658,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+50.495472,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+49.758633,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+49.806366,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+49.818005,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+50.171398,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+50.090405,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+50.167778,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+50.422070,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+50.516560,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+50.519188,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+50.838951,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+50.839592,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+50.776363,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+51.134842,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+51.106434,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+51.155491,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+51.533459,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+51.418091,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+51.450565,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+50.752281,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+50.761490,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+50.872406,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+51.086300,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+51.174137,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+51.099903,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+51.521896,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+51.510994,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+51.493977,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+50.857857,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+50.842373,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+50.855190,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+51.118946,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+51.087795,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+51.118011,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+51.511127,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+51.514835,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+51.467529,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+50.821358,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+50.793457,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+50.770344,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+51.159763,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+51.111111,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+51.101208,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+51.456570,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+51.495152,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+51.462238,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+50.759884,+0.500000] , 1. [-0.000000,-1.088001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+50.762352,+0.500000] , 1. [-0.000000,-1.088001,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+50.800728,+0.500000] , 1. [+0.000000,-1.088001,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+51.160164,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+51.151855,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+51.146713,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+51.520569,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+51.502533,+0.500000] , 1. [+0.000000,-1.088001,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+51.440838,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+50.759541,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+50.848774,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+50.830708,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+51.182114,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+51.159531,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+51.124104,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+51.530972,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+51.515232,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+51.468868,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+50.837128,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+50.762005,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+50.810333,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+51.155457,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+51.109283,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+51.119240,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+51.450722,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+51.513042,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+51.515472,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+50.863720,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+50.864475,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+50.857548,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+51.111706,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+51.177040,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+51.114208,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+51.460545,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+51.421116,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+51.468533,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+50.864384,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+50.823902,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+50.824448,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+51.093616,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+51.194023,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+51.185421,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+51.427994,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+51.538334,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+51.532467,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+50.856411,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+50.806297,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+50.768867,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+51.174210,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+51.213314,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+51.115635,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+51.519836,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+51.486736,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+51.473560,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+50.881237,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+50.844353,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+50.798901,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+51.206886,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+51.200405,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+51.203888,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+51.461552,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+51.415592,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+51.494709,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+50.793968,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+50.828442,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+50.754799,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+51.104004,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+51.180538,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+51.142895,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+51.511242,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+51.496994,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+51.539783,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+51.814610,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+51.776428,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+51.801811,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+52.087681,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+52.098568,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+52.131123,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+52.534477,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+52.435047,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+52.502163,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+51.758633,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+51.879627,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+51.752075,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+52.144974,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+52.170826,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+52.135796,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+52.521225,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+52.456448,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+52.525928,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+51.795967,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+51.831829,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+51.856571,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+52.181999,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+52.208618,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+52.153641,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+52.420555,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+52.509453,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+52.526920,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+51.777943,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+51.820469,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+51.860317,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+52.128696,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+52.094788,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+52.172699,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+52.501202,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+52.520451,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+52.417370,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+51.788757,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+51.832420,+0.500000] , 1. [+0.000000,-1.088001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+51.835915,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+52.094208,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+52.120979,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+52.092945,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+52.532539,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+52.507507,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+52.515671,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+51.787739,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+51.769562,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+51.790150,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+52.158375,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+52.200012,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+52.123463,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+52.438000,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+52.440842,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+52.527386,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+51.873371,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+51.794964,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+51.839458,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+52.207981,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+52.189064,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+52.085697,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+52.441185,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+52.477497,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+52.500290,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+51.871494,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+51.856426,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+51.793800,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+52.161568,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+52.146713,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+52.199295,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+52.440899,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+52.504879,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+52.440487,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+51.757080,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+51.829208,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+51.815464,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+52.122890,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+52.154999,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+52.100842,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+52.426891,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+52.531105,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+52.425095,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+51.770901,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+51.829346,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+51.776966,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+52.160339,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+52.120842,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+52.149033,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+52.418373,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+52.505466,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+52.469379,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+51.787445,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+51.837051,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+51.792965,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+52.110733,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+52.214550,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+52.177902,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+52.436653,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+52.424850,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+52.535313,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+51.780914,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+51.786377,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+51.757214,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+52.140804,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+52.213497,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+52.089420,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+52.449917,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+52.523186,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+52.516739,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+52.837921,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+52.820084,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+52.757511,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+53.096680,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+53.120216,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+53.106499,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+53.451733,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+53.434669,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+53.540161,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+52.854446,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+52.768009,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+52.770157,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+53.143291,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+53.163795,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+53.201866,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+53.546093,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+53.542114,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+53.492489,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+52.783478,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+52.791443,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+52.795361,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+53.183422,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+53.103027,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+53.177864,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+53.504234,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+53.496441,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+53.420128,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+52.832531,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+52.859371,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+52.786297,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+53.169830,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+53.184441,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+53.093479,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+53.446262,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+53.438675,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+53.414833,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+52.841053,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+52.856544,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+52.865494,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+53.170170,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+53.106895,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+53.200726,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+53.414902,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+53.547295,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+53.525917,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+52.868919,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+52.860344,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+52.852066,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+53.211357,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+53.208328,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+53.178913,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+53.495190,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+53.530510,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+53.459053,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+52.754524,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+52.758213,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+52.798851,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+53.162659,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+53.108913,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+53.131626,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+53.480488,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+53.476566,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+53.419800,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+52.866459,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+52.860058,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+52.851486,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+53.208866,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+53.202991,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+53.127266,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+53.485947,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+53.511429,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+53.523933,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+52.766003,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+52.835018,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+52.817623,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+53.162861,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+53.101200,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+53.172897,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+53.485741,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+53.527534,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+53.515121,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+52.795807,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+52.788811,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+52.756580,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+53.155434,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+53.204231,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+53.213394,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+53.438198,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+53.443253,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+53.466682,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+52.866058,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+52.811279,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+52.836014,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+53.204529,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+53.202984,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+53.177673,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+53.422569,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+53.547394,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+53.433308,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+52.851284,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+52.801353,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+52.829632,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+53.172935,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+53.211742,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+53.139709,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+53.532104,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+53.535637,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+53.539528,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+53.874058,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+53.840904,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+53.835678,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+54.141247,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+54.176529,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+54.167362,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+54.498497,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+54.474461,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+54.515549,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+53.858978,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+53.805244,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+53.812180,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+54.183319,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+54.197521,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+54.104641,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+54.502586,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+54.472309,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+54.443279,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+53.791218,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+53.755222,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+53.762928,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+54.155880,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+54.125538,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+54.103615,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+54.477341,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+54.475418,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+54.523052,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+53.808704,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+53.778378,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+53.820633,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+54.176449,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+54.116505,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+54.104546,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+54.462223,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+54.513336,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+54.446651,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+53.807587,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+53.751907,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+53.806564,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+54.122818,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+54.140091,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+54.163902,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+54.514992,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+54.512310,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+54.516777,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+53.827877,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+53.862495,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+53.877228,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+54.202515,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+54.095505,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+54.102837,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+54.423298,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+54.527092,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+54.468727,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+53.861927,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+53.766796,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+53.810917,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+54.081387,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+54.098534,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+54.160179,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+54.529041,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+54.520294,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+54.531273,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+53.797508,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+53.842251,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+53.817856,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+54.189705,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+54.163486,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+54.118481,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+54.440536,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+54.480080,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+54.467602,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+53.828281,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+53.766991,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+53.808071,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+54.185673,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+54.194881,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+54.103031,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+54.504204,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+54.488060,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+54.477352,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+53.858112,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+53.844524,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+53.803509,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+54.200268,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+54.140598,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+54.154621,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+54.498329,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+54.525249,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+54.483425,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+53.760738,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+53.841850,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+53.853439,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+54.102917,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+54.126938,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+54.160194,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+54.471210,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+54.431702,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+54.488285,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+53.868164,+0.500000] , 1. [+0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+53.837860,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+53.810612,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+54.091881,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+54.173183,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+54.097134,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+54.513882,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+54.503578,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+54.532433,+0.500000] , 1. [-0.000000,-1.088000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/results/particles-frame-9.txt b/scenes/cell-growth/results/particles-frame-9.txt deleted file mode 100644 index 388037da..00000000 --- a/scenes/cell-growth/results/particles-frame-9.txt +++ /dev/null @@ -1,1405 +0,0 @@ -1404, pdata: 4 (0,0,4) -0: [+26.122849,+40.650944,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1: [+26.453651,+40.752190,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -2: [+26.898863,+40.724522,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -3: [+26.112722,+40.980816,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -4: [+26.557117,+41.069092,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -5: [+26.849995,+40.980572,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -6: [+26.138176,+41.328152,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -7: [+26.462387,+41.416466,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -8: [+26.819349,+41.381466,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -9: [+27.153780,+40.735771,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -10: [+27.494139,+40.704586,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -11: [+27.813742,+40.754772,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -12: [+27.153406,+40.969101,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -13: [+27.490547,+41.001625,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -14: [+27.873196,+41.005360,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -15: [+27.105312,+41.420422,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -16: [+27.469255,+41.320229,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -17: [+27.878544,+41.385471,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -18: [+28.123091,+40.734802,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -19: [+28.439552,+40.697517,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -20: [+28.845465,+40.734985,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -21: [+28.226366,+40.978970,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -22: [+28.520102,+41.028599,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -23: [+28.832155,+40.970928,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -24: [+28.162525,+41.323616,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -25: [+28.546179,+41.386963,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -26: [+28.770546,+41.362446,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -27: [+29.172895,+40.725285,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -28: [+29.528704,+40.730263,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -29: [+29.772995,+40.678383,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -30: [+29.205616,+41.021103,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -31: [+29.467356,+41.077869,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -32: [+29.827061,+41.014687,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -33: [+29.137615,+41.352116,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -34: [+29.550869,+41.344715,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -35: [+29.832321,+41.315060,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -36: [+30.152824,+40.700230,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -37: [+30.549763,+40.722015,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -38: [+30.891993,+40.651440,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -39: [+30.106831,+41.019394,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -40: [+30.458609,+41.062767,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -41: [+30.861563,+41.011269,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -42: [+30.217495,+41.427994,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -43: [+30.445452,+41.359276,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -44: [+30.895409,+41.346508,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -45: [+31.183985,+40.645393,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -46: [+31.439808,+40.640106,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -47: [+31.788757,+40.708454,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -48: [+31.118870,+40.963593,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -49: [+31.541147,+41.021061,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -50: [+31.883717,+40.974277,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -51: [+31.168335,+41.354477,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -52: [+31.551752,+41.304901,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -53: [+31.825630,+41.320454,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -54: [+32.145584,+40.761032,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -55: [+32.473881,+40.681206,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -56: [+32.822876,+40.667019,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -57: [+32.152546,+41.069412,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -58: [+32.463303,+41.063339,+0.500000] , 1. [-0.000000,-1.216001,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -59: [+32.872761,+41.035297,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -60: [+32.207577,+41.403801,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -61: [+32.544460,+41.339890,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -62: [+32.773670,+41.379539,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -63: [+33.142303,+40.635635,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -64: [+33.502468,+40.721912,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -65: [+33.791725,+40.752499,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -66: [+33.225315,+40.991787,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -67: [+33.535233,+40.993145,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -68: [+33.817600,+40.971615,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -69: [+33.219772,+41.418518,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -70: [+33.561031,+41.314201,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -71: [+33.869625,+41.381771,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -72: [+34.177883,+40.684303,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -73: [+34.547924,+40.662682,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -74: [+34.869041,+40.666504,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -75: [+34.150482,+40.972492,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -76: [+34.486000,+40.962051,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -77: [+34.816849,+40.985832,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -78: [+34.230778,+41.376034,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -79: [+34.453911,+41.351112,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -80: [+34.899162,+41.365921,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -81: [+35.123138,+40.653908,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -82: [+35.436810,+40.668655,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -83: [+35.838326,+40.650890,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -84: [+35.167435,+41.089375,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -85: [+35.562302,+40.964268,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -86: [+35.860424,+41.070309,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -87: [+35.117565,+41.375362,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -88: [+35.461723,+41.329311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -89: [+35.823490,+41.345127,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -90: [+36.148548,+40.699368,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -91: [+36.494766,+40.742226,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -92: [+36.855301,+40.753922,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -93: [+36.193554,+41.058819,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -94: [+36.562984,+40.975945,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -95: [+36.771172,+41.005554,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -96: [+36.223289,+41.408821,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -97: [+36.454117,+41.394276,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -98: [+36.863091,+41.306492,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -99: [+37.204296,+40.714802,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -100: [+37.534031,+40.729874,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -101: [+37.882481,+40.699841,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -102: [+37.227539,+41.061687,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -103: [+37.459435,+41.024178,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -104: [+37.791687,+41.002491,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -105: [+37.188446,+41.373936,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -106: [+37.436653,+41.410908,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -107: [+37.839451,+41.301361,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -108: [+26.134302,+41.658924,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -109: [+26.436390,+41.726879,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -110: [+26.830048,+41.672337,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -111: [+26.187742,+42.020691,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -112: [+26.506567,+42.064110,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -113: [+26.780262,+41.991451,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -114: [+26.197567,+42.318478,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -115: [+26.462009,+42.375278,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -116: [+26.818893,+42.295593,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -117: [+27.233263,+41.708179,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -118: [+27.561996,+41.669025,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -119: [+27.868622,+41.631714,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -120: [+27.157400,+42.021225,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -121: [+27.555435,+42.027550,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -122: [+27.807611,+42.073895,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -123: [+27.169657,+42.344643,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -124: [+27.541897,+42.383514,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -125: [+27.836441,+42.353241,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -126: [+28.222355,+41.635990,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -127: [+28.493765,+41.671989,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -128: [+28.787922,+41.721725,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -129: [+28.211403,+42.079411,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -130: [+28.510069,+41.994865,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -131: [+28.872284,+41.967705,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -132: [+28.202276,+42.392902,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -133: [+28.461607,+42.322430,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -134: [+28.895288,+42.311440,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -135: [+29.148680,+41.666252,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -136: [+29.534229,+41.645508,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -137: [+29.879578,+41.645512,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -138: [+29.205069,+41.984318,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -139: [+29.506006,+42.068882,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -140: [+29.810587,+41.975998,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -141: [+29.144794,+42.353691,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -142: [+29.512489,+42.387486,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -143: [+29.769785,+42.368477,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -144: [+30.224350,+41.703445,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -145: [+30.558168,+41.640045,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -146: [+30.812428,+41.759552,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -147: [+30.218658,+42.015579,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -148: [+30.500448,+42.008812,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -149: [+30.822800,+41.992241,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -150: [+30.188595,+42.327389,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -151: [+30.463940,+42.420891,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -152: [+30.830355,+42.388435,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -153: [+31.191008,+41.650341,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -154: [+31.458574,+41.644993,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -155: [+31.826435,+41.666935,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -156: [+31.213301,+42.022602,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -157: [+31.528881,+42.090839,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -158: [+31.877710,+42.069824,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -159: [+31.125656,+42.409767,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -160: [+31.512571,+42.350147,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -161: [+31.835161,+42.301201,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -162: [+32.208939,+41.655670,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -163: [+32.559525,+41.675613,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -164: [+32.773132,+41.651855,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -165: [+32.185898,+41.985779,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -166: [+32.553947,+41.966080,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -167: [+32.817741,+42.080200,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -168: [+32.136250,+42.387417,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -169: [+32.491989,+42.395279,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -170: [+32.856728,+42.379829,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -171: [+33.105419,+41.679440,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -172: [+33.525867,+41.643253,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -173: [+33.770809,+41.638172,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -174: [+33.209290,+42.081573,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -175: [+33.443485,+42.085205,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -176: [+33.804619,+41.986061,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -177: [+33.201485,+42.389832,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -178: [+33.535725,+42.330292,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -179: [+33.852070,+42.390774,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -180: [+34.192776,+41.666985,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -181: [+34.523987,+41.734238,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -182: [+34.884480,+41.686653,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -183: [+34.171944,+41.982506,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -184: [+34.529778,+41.969410,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -185: [+34.775215,+42.093647,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -186: [+34.148880,+42.304230,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -187: [+34.561382,+42.297707,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -188: [+34.889530,+42.349865,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -189: [+35.151173,+41.655430,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -190: [+35.482433,+41.643665,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -191: [+35.866528,+41.754070,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -192: [+35.233025,+42.068214,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -193: [+35.447628,+42.030472,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -194: [+35.795452,+42.064960,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -195: [+35.213272,+42.328453,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -196: [+35.476944,+42.345509,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -197: [+35.881947,+42.363380,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -198: [+36.206589,+41.741562,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -199: [+36.497036,+41.679264,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -200: [+36.811108,+41.635273,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -201: [+36.230728,+42.026653,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -202: [+36.495499,+42.006733,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -203: [+36.835556,+42.022472,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -204: [+36.186050,+42.346954,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -205: [+36.558033,+42.405613,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -206: [+36.881329,+42.319481,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -207: [+37.150661,+41.660786,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -208: [+37.455067,+41.731644,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -209: [+37.852238,+41.691212,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -210: [+37.110943,+42.054203,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -211: [+37.457649,+42.058907,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -212: [+37.892643,+41.996208,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -213: [+37.195641,+42.360622,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -214: [+37.494659,+42.349846,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -215: [+37.835560,+42.322727,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -216: [+26.137892,+42.690819,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -217: [+26.564360,+42.650749,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -218: [+26.776350,+42.712875,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -219: [+26.198835,+43.052177,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -220: [+26.530161,+43.031082,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -221: [+26.838749,+43.086647,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -222: [+26.154537,+43.413147,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -223: [+26.507488,+43.298775,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -224: [+26.883324,+43.320244,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -225: [+27.131704,+42.680679,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -226: [+27.465704,+42.751045,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -227: [+27.882172,+42.754700,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -228: [+27.139505,+43.028191,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -229: [+27.494884,+42.971210,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -230: [+27.810974,+43.006241,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -231: [+27.145584,+43.374165,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -232: [+27.504309,+43.404045,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -233: [+27.837379,+43.323280,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -234: [+28.135340,+42.664925,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -235: [+28.466215,+42.648323,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -236: [+28.882393,+42.670944,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -237: [+28.180235,+43.080448,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -238: [+28.486662,+42.987564,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -239: [+28.808311,+42.962582,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -240: [+28.216877,+43.300369,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -241: [+28.556948,+43.306793,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -242: [+28.896992,+43.356930,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -243: [+29.107769,+42.652149,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -244: [+29.481386,+42.731358,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -245: [+29.894106,+42.731483,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -246: [+29.216625,+42.984119,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -247: [+29.482180,+43.091362,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -248: [+29.792667,+43.042709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -249: [+29.155807,+43.406979,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -250: [+29.507292,+43.315392,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -251: [+29.817974,+43.371460,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -252: [+30.176001,+42.658749,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -253: [+30.519308,+42.745804,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -254: [+30.849657,+42.716919,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -255: [+30.171932,+43.073727,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -256: [+30.478603,+43.053551,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -257: [+30.814066,+42.982155,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -258: [+30.163485,+43.358444,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -259: [+30.485065,+43.370342,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -260: [+30.776529,+43.388344,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -261: [+31.158104,+42.658043,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -262: [+31.547148,+42.697102,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -263: [+31.778715,+42.637260,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -264: [+31.108032,+43.021717,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -265: [+31.550665,+42.976051,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -266: [+31.833670,+43.017204,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -267: [+31.188221,+43.349277,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -268: [+31.524952,+43.364338,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -269: [+31.865829,+43.312984,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -270: [+32.187241,+42.711819,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -271: [+32.525917,+42.652477,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -272: [+32.896835,+42.655293,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -273: [+32.219246,+43.000088,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -274: [+32.475636,+43.015774,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -275: [+32.770649,+43.082245,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -276: [+32.155064,+43.339420,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -277: [+32.547039,+43.426449,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -278: [+32.886135,+43.406315,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -279: [+33.150379,+42.743515,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -280: [+33.438343,+42.628548,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -281: [+33.809101,+42.760242,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -282: [+33.146351,+43.075329,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -283: [+33.541290,+42.973316,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -284: [+33.788952,+43.049065,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -285: [+33.155712,+43.343155,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000001,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -286: [+33.523201,+43.338654,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -287: [+33.850475,+43.377613,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -288: [+34.220455,+42.642967,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -289: [+34.452557,+42.671124,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -290: [+34.865963,+42.694748,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -291: [+34.121689,+43.084465,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -292: [+34.444706,+43.076050,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -293: [+34.777813,+43.003307,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -294: [+34.203300,+43.386185,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -295: [+34.518120,+43.382488,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -296: [+34.888905,+43.412144,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -297: [+35.223499,+42.758209,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -298: [+35.561901,+42.673618,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -299: [+35.767326,+42.695213,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -300: [+35.139511,+43.094494,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -301: [+35.513969,+42.974346,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -302: [+35.795242,+43.041115,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -303: [+35.162331,+43.329189,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -304: [+35.462322,+43.396793,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -305: [+35.858078,+43.297199,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -306: [+36.189159,+42.694424,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -307: [+36.543751,+42.628345,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -308: [+36.877609,+42.650284,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -309: [+36.198441,+43.006393,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -310: [+36.486958,+43.078964,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -311: [+36.824402,+43.051506,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -312: [+36.127506,+43.341106,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -313: [+36.528912,+43.368477,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -314: [+36.815155,+43.349457,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -315: [+37.140553,+42.666534,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -316: [+37.543423,+42.633461,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -317: [+37.821983,+42.745754,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -318: [+37.177097,+42.970669,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -319: [+37.502895,+43.092213,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -320: [+37.848942,+43.047207,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -321: [+37.222137,+43.345734,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -322: [+37.438778,+43.341370,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -323: [+37.834827,+43.353165,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -324: [+26.227354,+43.730103,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -325: [+26.563053,+43.691147,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -326: [+26.886887,+43.694683,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -327: [+26.206791,+44.080032,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -328: [+26.448837,+44.052917,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -329: [+26.874334,+43.970146,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -330: [+26.107798,+44.395897,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -331: [+26.438719,+44.307678,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -332: [+26.898939,+44.352280,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -333: [+27.137068,+43.758736,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -334: [+27.446260,+43.740597,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -335: [+27.874607,+43.643387,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -336: [+27.126635,+44.020374,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -337: [+27.441753,+44.003067,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -338: [+27.817467,+44.069771,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -339: [+27.117468,+44.382854,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -340: [+27.445736,+44.295208,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -341: [+27.816925,+44.301506,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -342: [+28.203413,+43.669655,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -343: [+28.541676,+43.694550,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -344: [+28.798317,+43.671246,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -345: [+28.175928,+44.038719,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -346: [+28.526117,+44.034069,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -347: [+28.822929,+44.036682,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -348: [+28.202461,+44.388325,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -349: [+28.480091,+44.405025,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -350: [+28.841024,+44.344913,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -351: [+29.132620,+43.683784,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -352: [+29.514725,+43.733273,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -353: [+29.823849,+43.717606,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -354: [+29.186638,+44.027485,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -355: [+29.439491,+44.058826,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -356: [+29.890490,+44.045147,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -357: [+29.193056,+44.297176,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -358: [+29.471260,+44.346935,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -359: [+29.795563,+44.406612,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -360: [+30.112366,+43.731922,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -361: [+30.503618,+43.685734,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -362: [+30.814365,+43.754635,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -363: [+30.181637,+44.026215,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -364: [+30.560530,+44.042057,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -365: [+30.772852,+43.964664,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -366: [+30.117184,+44.305389,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -367: [+30.477829,+44.409252,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -368: [+30.809412,+44.331135,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -369: [+31.204601,+43.685795,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -370: [+31.533020,+43.664421,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -371: [+31.797686,+43.710316,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -372: [+31.116585,+44.093594,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -373: [+31.514935,+43.978088,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -374: [+31.831554,+44.006622,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -375: [+31.221670,+44.303696,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -376: [+31.469028,+44.385548,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -377: [+31.777996,+44.312420,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -378: [+32.116398,+43.667820,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -379: [+32.454342,+43.707340,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -380: [+32.878414,+43.664276,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -381: [+32.203915,+44.026321,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -382: [+32.434147,+43.966991,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -383: [+32.840946,+44.016842,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -384: [+32.200455,+44.366089,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -385: [+32.466507,+44.338341,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -386: [+32.805607,+44.353653,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -387: [+33.215591,+43.697926,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -388: [+33.448513,+43.645641,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -389: [+33.832005,+43.645679,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -390: [+33.176243,+44.061028,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -391: [+33.521526,+44.021832,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -392: [+33.819328,+44.072617,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -393: [+33.105701,+44.309711,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -394: [+33.475674,+44.417004,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -395: [+33.860840,+44.348114,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -396: [+34.212353,+43.650581,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -397: [+34.451550,+43.716370,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -398: [+34.768917,+43.727558,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -399: [+34.198761,+44.035679,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -400: [+34.499531,+43.974754,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -401: [+34.879768,+44.071892,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -402: [+34.100639,+44.423473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -403: [+34.526295,+44.395824,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -404: [+34.808968,+44.401478,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -405: [+35.128220,+43.751022,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -406: [+35.532078,+43.719330,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -407: [+35.811043,+43.711571,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -408: [+35.212860,+44.079300,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -409: [+35.522419,+44.078819,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -410: [+35.877869,+44.018288,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -411: [+35.194744,+44.339741,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -412: [+35.442913,+44.341148,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -413: [+35.866562,+44.387333,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -414: [+36.217724,+43.700478,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -415: [+36.504215,+43.749176,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -416: [+36.855545,+43.659027,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -417: [+36.229424,+43.974529,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -418: [+36.462029,+44.006481,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -419: [+36.795109,+44.022972,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -420: [+36.121078,+44.357544,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -421: [+36.552444,+44.302753,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -422: [+36.792374,+44.390598,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -423: [+37.178127,+43.737225,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -424: [+37.463825,+43.727562,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -425: [+37.829937,+43.661720,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -426: [+37.215340,+44.033276,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -427: [+37.525673,+44.080635,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -428: [+37.843407,+44.035278,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -429: [+37.203705,+44.317017,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -430: [+37.444981,+44.387245,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -431: [+37.878426,+44.310764,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -432: [+26.169304,+44.711063,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -433: [+26.494114,+44.673409,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -434: [+26.891106,+44.737473,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -435: [+26.223078,+45.010197,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -436: [+26.560616,+45.019390,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -437: [+26.813375,+44.979610,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -438: [+26.153881,+45.371063,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -439: [+26.441355,+45.364662,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -440: [+26.769859,+45.321930,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -441: [+27.131750,+44.683064,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -442: [+27.454788,+44.658524,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -443: [+27.891685,+44.745449,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -444: [+27.147400,+45.058887,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -445: [+27.542244,+45.017056,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -446: [+27.780220,+45.003971,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -447: [+27.228924,+45.370472,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -448: [+27.554148,+45.354546,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -449: [+27.801252,+45.407635,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -450: [+28.222219,+44.710354,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -451: [+28.542095,+44.693077,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -452: [+28.866283,+44.641247,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -453: [+28.138630,+45.068634,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -454: [+28.498919,+45.045036,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -455: [+28.804499,+45.092110,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -456: [+28.155701,+45.323978,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -457: [+28.501501,+45.320553,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -458: [+28.839417,+45.397400,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -459: [+29.150669,+44.650650,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -460: [+29.433784,+44.665768,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -461: [+29.805565,+44.726353,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -462: [+29.162487,+45.093006,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -463: [+29.545538,+44.983639,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -464: [+29.821646,+45.044422,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -465: [+29.125980,+45.314854,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -466: [+29.467813,+45.363384,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -467: [+29.837288,+45.342068,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -468: [+30.130318,+44.666065,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -469: [+30.554920,+44.739956,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -470: [+30.889189,+44.760567,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -471: [+30.150354,+45.058842,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -472: [+30.440491,+45.006767,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -473: [+30.790045,+45.093922,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -474: [+30.191614,+45.312202,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -475: [+30.487307,+45.300270,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -476: [+30.846930,+45.368473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -477: [+31.134449,+44.665760,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -478: [+31.536016,+44.718605,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -479: [+31.875565,+44.745079,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -480: [+31.110296,+44.997555,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -481: [+31.498850,+45.049660,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -482: [+31.796240,+45.021416,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -483: [+31.223383,+45.365616,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -484: [+31.437931,+45.368675,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -485: [+31.772993,+45.313957,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -486: [+32.186558,+44.642864,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -487: [+32.502735,+44.690525,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -488: [+32.841076,+44.699364,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -489: [+32.108414,+45.041115,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -490: [+32.551327,+44.984764,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -491: [+32.872826,+45.091717,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -492: [+32.194412,+45.315773,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -493: [+32.445648,+45.390274,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -494: [+32.873524,+45.332058,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -495: [+33.180828,+44.708366,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -496: [+33.513321,+44.759037,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -497: [+33.869335,+44.696205,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -498: [+33.219219,+44.999008,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -499: [+33.444836,+45.083965,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -500: [+33.771328,+45.005463,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -501: [+33.137901,+45.421329,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -502: [+33.504860,+45.411392,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -503: [+33.868862,+45.326263,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -504: [+34.173611,+44.643837,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -505: [+34.465107,+44.685944,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -506: [+34.859787,+44.633915,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -507: [+34.162338,+45.006657,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -508: [+34.450863,+44.986664,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -509: [+34.891315,+45.039162,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -510: [+34.199619,+45.319592,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -511: [+34.459911,+45.334126,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -512: [+34.875820,+45.404018,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -513: [+35.179668,+44.672432,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -514: [+35.473339,+44.706291,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -515: [+35.885281,+44.685726,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -516: [+35.191395,+45.034977,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -517: [+35.529182,+45.023487,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -518: [+35.796116,+44.997410,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -519: [+35.227928,+45.331497,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -520: [+35.475693,+45.374725,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -521: [+35.802204,+45.385590,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -522: [+36.153381,+44.714737,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -523: [+36.470707,+44.629227,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -524: [+36.808128,+44.655178,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -525: [+36.156731,+45.014153,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -526: [+36.562897,+45.036110,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -527: [+36.844894,+45.016964,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -528: [+36.204403,+45.374489,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -529: [+36.555809,+45.316963,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -530: [+36.770992,+45.403538,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -531: [+37.130798,+44.722801,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -532: [+37.555607,+44.726669,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -533: [+37.852448,+44.650669,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -534: [+37.160126,+44.979824,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -535: [+37.492367,+45.033611,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -536: [+37.857296,+44.961948,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -537: [+37.176144,+45.359543,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -538: [+37.479027,+45.414799,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -539: [+37.811455,+45.355473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -540: [+26.120478,+45.740479,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -541: [+26.538008,+45.647820,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -542: [+26.768793,+45.757984,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -543: [+26.120617,+45.984913,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -544: [+26.437273,+45.969242,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -545: [+26.773155,+46.002502,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -546: [+26.206295,+46.370892,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -547: [+26.556435,+46.412521,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -548: [+26.790157,+46.334511,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -549: [+27.113314,+45.655315,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -550: [+27.476250,+45.759087,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -551: [+27.832233,+45.676311,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -552: [+27.146631,+46.012535,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -553: [+27.467800,+45.962856,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -554: [+27.862038,+45.965729,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -555: [+27.120094,+46.340797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -556: [+27.565704,+46.324360,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -557: [+27.789284,+46.394627,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -558: [+28.175238,+45.697094,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -559: [+28.545595,+45.715752,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -560: [+28.790249,+45.721809,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -561: [+28.125372,+46.036575,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -562: [+28.546757,+46.048473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -563: [+28.884050,+45.982010,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -564: [+28.158592,+46.311710,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -565: [+28.525600,+46.337311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -566: [+28.868280,+46.391315,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -567: [+29.149418,+45.638603,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -568: [+29.512615,+45.702797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -569: [+29.792858,+45.680206,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -570: [+29.220648,+45.969967,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -571: [+29.447641,+46.013523,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -572: [+29.821341,+46.081894,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -573: [+29.134836,+46.306976,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -574: [+29.561060,+46.299351,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -575: [+29.792946,+46.326801,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -576: [+30.165318,+45.635647,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -577: [+30.543350,+45.648170,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -578: [+30.821724,+45.661880,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -579: [+30.116848,+46.017902,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -580: [+30.462219,+45.985382,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -581: [+30.846199,+45.971806,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -582: [+30.114794,+46.297897,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -583: [+30.442413,+46.316017,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -584: [+30.856615,+46.341476,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -585: [+31.135983,+45.680462,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -586: [+31.524622,+45.737915,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -587: [+31.832605,+45.758831,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -588: [+31.152872,+45.961800,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -589: [+31.466873,+46.053654,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -590: [+31.867289,+46.071316,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -591: [+31.100241,+46.416504,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -592: [+31.471888,+46.419106,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -593: [+31.852221,+46.421661,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -594: [+32.183708,+45.642311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -595: [+32.494385,+45.639442,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -596: [+32.784027,+45.714912,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -597: [+32.191868,+45.995495,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -598: [+32.554089,+46.012039,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -599: [+32.890656,+46.024227,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -600: [+32.200447,+46.337231,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -601: [+32.560368,+46.410591,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -602: [+32.880180,+46.361691,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -603: [+33.217876,+45.685902,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -604: [+33.547699,+45.629677,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -605: [+33.861176,+45.717293,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -606: [+33.166458,+46.016350,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -607: [+33.517853,+45.965691,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -608: [+33.806286,+46.055332,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -609: [+33.187016,+46.375668,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -610: [+33.471409,+46.298237,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -611: [+33.794468,+46.340683,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -612: [+34.195618,+45.744698,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -613: [+34.517776,+45.706844,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -614: [+34.868855,+45.704269,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -615: [+34.232933,+46.057053,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -616: [+34.483116,+45.970665,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -617: [+34.815872,+45.973690,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -618: [+34.219654,+46.332413,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -619: [+34.445362,+46.368858,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -620: [+34.806679,+46.357212,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -621: [+35.140030,+45.741093,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -622: [+35.471497,+45.749287,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -623: [+35.846958,+45.662514,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -624: [+35.131748,+45.965584,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -625: [+35.513260,+45.977322,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -626: [+35.875885,+45.992119,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -627: [+35.210693,+46.304512,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -628: [+35.548458,+46.316975,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -629: [+35.856850,+46.367237,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -630: [+36.123787,+45.750633,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -631: [+36.463593,+45.677940,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -632: [+36.819050,+45.737564,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -633: [+36.108574,+46.064930,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -634: [+36.487011,+46.082569,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -635: [+36.883583,+46.067631,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -636: [+36.212326,+46.302082,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -637: [+36.456989,+46.412289,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -638: [+36.889515,+46.403709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -639: [+37.220661,+45.740509,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -640: [+37.444389,+45.753719,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -641: [+37.846413,+45.699730,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -642: [+37.174664,+46.064445,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -643: [+37.539112,+46.057957,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -644: [+37.804855,+45.987774,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -645: [+37.188816,+46.329308,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -646: [+37.504467,+46.413639,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -647: [+37.896751,+46.396095,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -648: [+26.114902,+46.729496,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -649: [+26.520790,+46.719963,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -650: [+26.852573,+46.644505,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -651: [+26.120377,+47.083477,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -652: [+26.442600,+46.971497,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -653: [+26.815523,+47.015549,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -654: [+26.121269,+47.373779,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -655: [+26.481812,+47.406605,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -656: [+26.814674,+47.380318,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -657: [+27.109781,+46.701633,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -658: [+27.508560,+46.709454,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -659: [+27.883595,+46.714191,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -660: [+27.130463,+47.017048,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -661: [+27.465319,+47.022987,+0.500000] , 1. [-0.000000,-1.216001,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -662: [+27.804302,+47.006252,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -663: [+27.231428,+47.313732,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -664: [+27.496567,+47.299797,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -665: [+27.825541,+47.350681,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -666: [+28.117174,+46.682320,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -667: [+28.511965,+46.698040,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -668: [+28.796368,+46.634773,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -669: [+28.170046,+47.002590,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -670: [+28.541039,+47.043087,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -671: [+28.894999,+47.087730,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -672: [+28.212408,+47.329979,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -673: [+28.532591,+47.313904,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -674: [+28.802336,+47.388226,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -675: [+29.154409,+46.654991,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -676: [+29.459482,+46.731888,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -677: [+29.790939,+46.744823,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -678: [+29.133907,+47.012264,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -679: [+29.522968,+47.015984,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -680: [+29.860323,+47.066608,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -681: [+29.113859,+47.383507,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -682: [+29.467556,+47.310753,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -683: [+29.792334,+47.358940,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -684: [+30.200785,+46.696411,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -685: [+30.517881,+46.631199,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -686: [+30.797268,+46.726643,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -687: [+30.151020,+47.088840,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -688: [+30.565712,+47.030464,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -689: [+30.829908,+47.088619,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -690: [+30.164457,+47.329659,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -691: [+30.521252,+47.401012,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -692: [+30.803623,+47.337872,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -693: [+31.154066,+46.724171,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -694: [+31.462618,+46.657631,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -695: [+31.785936,+46.735538,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -696: [+31.172501,+46.974686,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -697: [+31.500378,+47.056252,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -698: [+31.836432,+47.052547,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -699: [+31.194084,+47.409935,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -700: [+31.544813,+47.393333,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -701: [+31.867426,+47.338554,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -702: [+32.120182,+46.639561,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -703: [+32.519775,+46.702705,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -704: [+32.823372,+46.673203,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -705: [+32.139420,+47.091484,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -706: [+32.514462,+47.070103,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -707: [+32.771336,+47.044998,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -708: [+32.101830,+47.395985,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -709: [+32.504032,+47.399143,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -710: [+32.834190,+47.380424,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -711: [+33.130466,+46.673416,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -712: [+33.500599,+46.673904,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -713: [+33.879128,+46.699699,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -714: [+33.187775,+47.007488,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -715: [+33.536949,+46.974079,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -716: [+33.866936,+46.991570,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -717: [+33.183647,+47.389297,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -718: [+33.453434,+47.347847,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -719: [+33.847366,+47.391720,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -720: [+34.122066,+46.663635,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -721: [+34.509968,+46.725441,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -722: [+34.898540,+46.657967,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -723: [+34.131439,+47.064972,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -724: [+34.457664,+47.051094,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -725: [+34.884525,+47.084656,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -726: [+34.152725,+47.400810,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -727: [+34.436584,+47.361977,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -728: [+34.819588,+47.406311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -729: [+35.201206,+46.700272,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -730: [+35.476215,+46.652672,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -731: [+35.855953,+46.695148,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -732: [+35.192253,+46.970657,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -733: [+35.538158,+47.031147,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -734: [+35.864307,+47.046680,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -735: [+35.112125,+47.356312,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -736: [+35.562016,+47.308231,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -737: [+35.849182,+47.364727,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -738: [+36.156960,+46.754742,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -739: [+36.492546,+46.646454,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -740: [+36.849674,+46.661194,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -741: [+36.101673,+47.049770,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -742: [+36.484783,+46.979973,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -743: [+36.882294,+47.062588,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -744: [+36.184345,+47.323372,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -745: [+36.555805,+47.314148,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -746: [+36.843319,+47.351990,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -747: [+37.219425,+46.629601,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -748: [+37.495239,+46.637486,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -749: [+37.877598,+46.647999,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -750: [+37.153622,+47.059242,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -751: [+37.494354,+46.962635,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -752: [+37.899815,+47.059921,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -753: [+37.149654,+47.329849,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -754: [+37.468407,+47.334797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -755: [+37.788860,+47.304337,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -756: [+26.149149,+47.759323,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -757: [+26.460608,+47.630844,+0.500000] , 1. [+0.000000,-1.216001,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -758: [+26.858616,+47.760509,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -759: [+26.125908,+48.051552,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -760: [+26.467369,+48.047878,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -761: [+26.803995,+48.006519,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -762: [+26.134998,+48.374645,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -763: [+26.556503,+48.405876,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -764: [+26.865408,+48.302490,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -765: [+27.145180,+47.716206,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -766: [+27.485327,+47.693871,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -767: [+27.892828,+47.728825,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -768: [+27.137873,+48.025845,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -769: [+27.525503,+48.012875,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -770: [+27.804333,+48.005611,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -771: [+27.218630,+48.393944,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -772: [+27.473864,+48.315380,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -773: [+27.835186,+48.355698,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -774: [+28.105444,+47.745834,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -775: [+28.473755,+47.652306,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -776: [+28.890522,+47.681557,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -777: [+28.141596,+48.028267,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -778: [+28.460203,+48.039383,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -779: [+28.896736,+48.018192,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -780: [+28.131691,+48.340858,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -781: [+28.534880,+48.354622,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -782: [+28.886747,+48.343773,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -783: [+29.132887,+47.669601,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -784: [+29.472742,+47.755032,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -785: [+29.859537,+47.659214,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -786: [+29.194283,+48.028061,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -787: [+29.552193,+48.046360,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -788: [+29.796343,+47.996651,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -789: [+29.205294,+48.303486,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -790: [+29.492943,+48.397102,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -791: [+29.779907,+48.301414,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -792: [+30.160954,+47.639042,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -793: [+30.481503,+47.710709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -794: [+30.850628,+47.695740,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -795: [+30.106712,+48.073967,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -796: [+30.544125,+47.973335,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -797: [+30.853552,+48.034187,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -798: [+30.207396,+48.389545,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -799: [+30.546389,+48.343121,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -800: [+30.827963,+48.424728,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -801: [+31.101477,+47.654697,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -802: [+31.503988,+47.730583,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -803: [+31.771484,+47.723152,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -804: [+31.135811,+48.016685,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -805: [+31.470810,+47.983562,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -806: [+31.848373,+48.031414,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -807: [+31.141485,+48.317848,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -808: [+31.527235,+48.400005,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -809: [+31.894653,+48.307457,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -810: [+32.126587,+47.665691,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -811: [+32.521114,+47.759148,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -812: [+32.859058,+47.721291,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -813: [+32.133949,+48.034828,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -814: [+32.442291,+47.990196,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -815: [+32.892700,+47.964188,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -816: [+32.132492,+48.373043,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -817: [+32.467842,+48.362209,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -818: [+32.840862,+48.342804,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -819: [+33.201447,+47.701927,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -820: [+33.558567,+47.719292,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -821: [+33.804775,+47.745632,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -822: [+33.170921,+47.997772,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -823: [+33.543110,+48.092049,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -824: [+33.833275,+48.019676,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -825: [+33.148209,+48.390945,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -826: [+33.506756,+48.307354,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -827: [+33.801613,+48.413116,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -828: [+34.110409,+47.681065,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -829: [+34.439758,+47.750469,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -830: [+34.875191,+47.696270,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -831: [+34.206432,+48.082363,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -832: [+34.512966,+48.013577,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -833: [+34.885456,+48.060558,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -834: [+34.219738,+48.334896,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -835: [+34.435211,+48.418411,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -836: [+34.812500,+48.376854,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -837: [+35.224815,+47.714767,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -838: [+35.495049,+47.741520,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -839: [+35.896206,+47.668560,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -840: [+35.108387,+47.983799,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -841: [+35.457355,+48.035538,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -842: [+35.881550,+48.080536,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -843: [+35.206078,+48.304527,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -844: [+35.487328,+48.387222,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -845: [+35.890820,+48.346336,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -846: [+36.195946,+47.652412,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -847: [+36.475105,+47.721928,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -848: [+36.828655,+47.711037,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -849: [+36.164520,+48.074966,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -850: [+36.518147,+48.077915,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -851: [+36.776234,+48.023792,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -852: [+36.156277,+48.380169,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -853: [+36.479759,+48.378498,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -854: [+36.840729,+48.404457,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -855: [+37.196606,+47.730755,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -856: [+37.562302,+47.690029,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -857: [+37.870094,+47.646725,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -858: [+37.229519,+47.982281,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -859: [+37.526077,+47.993240,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -860: [+37.855621,+48.025383,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -861: [+37.137138,+48.326267,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -862: [+37.502850,+48.325962,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -863: [+37.832649,+48.377411,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -864: [+26.231997,+48.654938,+0.500000] , 1. [+0.000000,-1.216001,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -865: [+26.542580,+48.723629,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -866: [+26.770626,+48.645855,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -867: [+26.155806,+49.006477,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -868: [+26.529467,+49.030048,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -869: [+26.869736,+49.029354,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -870: [+26.189566,+49.341816,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -871: [+26.510792,+49.343281,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -872: [+26.830320,+49.399952,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -873: [+27.134605,+48.634087,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -874: [+27.443459,+48.663097,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -875: [+27.857122,+48.660587,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -876: [+27.124495,+49.044033,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -877: [+27.471586,+49.085106,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -878: [+27.856516,+48.995541,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -879: [+27.183117,+49.369846,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -880: [+27.474194,+49.301533,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -881: [+27.854292,+49.358524,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -882: [+28.220310,+48.680237,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -883: [+28.461962,+48.753052,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -884: [+28.775028,+48.732712,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -885: [+28.214243,+49.031342,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -886: [+28.509596,+49.073261,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -887: [+28.882565,+49.085003,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -888: [+28.156755,+49.332756,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -889: [+28.457436,+49.322430,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -890: [+28.819435,+49.299698,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -891: [+29.214371,+48.752411,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -892: [+29.446362,+48.655312,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -893: [+29.823751,+48.643250,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -894: [+29.152260,+49.001492,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -895: [+29.516272,+49.055569,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -896: [+29.823322,+49.035908,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -897: [+29.143723,+49.341064,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -898: [+29.539940,+49.426220,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -899: [+29.869474,+49.381012,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -900: [+30.197229,+48.649845,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -901: [+30.497908,+48.672268,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -902: [+30.798164,+48.702557,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -903: [+30.172680,+49.054585,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -904: [+30.519325,+49.001900,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -905: [+30.825335,+49.046005,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -906: [+30.149263,+49.375099,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -907: [+30.548489,+49.330029,+0.500000] , 1. [-0.000000,-1.216001,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -908: [+30.806307,+49.321514,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -909: [+31.164091,+48.671875,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -910: [+31.566116,+48.662258,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -911: [+31.829193,+48.741333,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -912: [+31.196243,+49.078506,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -913: [+31.566414,+48.987484,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -914: [+31.886446,+48.977825,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -915: [+31.119308,+49.329536,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -916: [+31.490938,+49.312984,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -917: [+31.767349,+49.297222,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -918: [+32.184532,+48.724976,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -919: [+32.491871,+48.706966,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -920: [+32.771656,+48.652821,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -921: [+32.218079,+49.044373,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -922: [+32.520073,+49.048206,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -923: [+32.826725,+49.024212,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -924: [+32.141068,+49.346062,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -925: [+32.459621,+49.315605,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -926: [+32.846973,+49.352600,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -927: [+33.139301,+48.695709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -928: [+33.552181,+48.638859,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -929: [+33.828751,+48.641586,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -930: [+33.162834,+49.084965,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -931: [+33.468586,+48.996117,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -932: [+33.883072,+48.962826,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -933: [+33.171394,+49.410919,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -934: [+33.446423,+49.298611,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -935: [+33.829922,+49.392567,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -936: [+34.119251,+48.647243,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -937: [+34.446831,+48.720802,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -938: [+34.860279,+48.698151,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -939: [+34.186932,+49.053577,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -940: [+34.546638,+48.989563,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -941: [+34.862690,+49.034996,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -942: [+34.169643,+49.322220,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -943: [+34.464252,+49.360947,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -944: [+34.899994,+49.416836,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -945: [+35.171719,+48.648201,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -946: [+35.458164,+48.664829,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -947: [+35.834389,+48.723499,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -948: [+35.155464,+49.055649,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -949: [+35.554844,+49.073208,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -950: [+35.814445,+49.059566,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -951: [+35.221390,+49.319336,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -952: [+35.520138,+49.349518,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -953: [+35.816730,+49.407963,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -954: [+36.226353,+48.754608,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -955: [+36.535820,+48.724995,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -956: [+36.861179,+48.714924,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -957: [+36.172020,+49.030499,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -958: [+36.507149,+49.093315,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -959: [+36.879562,+49.017071,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -960: [+36.137104,+49.329395,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -961: [+36.462936,+49.315659,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -962: [+36.834877,+49.375473,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -963: [+37.191387,+48.638634,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -964: [+37.462688,+48.686367,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -965: [+37.818886,+48.698006,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -966: [+37.142696,+49.051399,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -967: [+37.561386,+48.970406,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -968: [+37.776711,+49.047779,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -969: [+37.179211,+49.302071,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -970: [+37.524548,+49.396561,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -971: [+37.833260,+49.399189,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -972: [+26.181145,+49.718952,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -973: [+26.485340,+49.719593,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -974: [+26.885241,+49.656364,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -975: [+26.138872,+50.014843,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -976: [+26.506968,+49.986435,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -977: [+26.884155,+50.035492,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -978: [+26.132893,+50.413460,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -979: [+26.441977,+50.298092,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -980: [+26.780386,+50.330566,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -981: [+27.140551,+49.632282,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -982: [+27.502588,+49.641491,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -983: [+27.892059,+49.752407,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -984: [+27.210596,+49.966301,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -985: [+27.534935,+50.054138,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -986: [+27.859142,+49.979904,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -987: [+27.128996,+50.401897,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -988: [+27.560595,+50.390995,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -989: [+27.867004,+50.373978,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -990: [+28.162024,+49.737858,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -991: [+28.503239,+49.722374,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -992: [+28.851326,+49.735191,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -993: [+28.105694,+49.998947,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -994: [+28.499071,+49.967796,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -995: [+28.873707,+49.998013,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -996: [+28.121435,+50.391129,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -997: [+28.508137,+50.394836,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -998: [+28.854774,+50.347530,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -999: [+29.145632,+49.701359,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1000: [+29.546824,+49.673458,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1001: [+29.800119,+49.650345,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1002: [+29.194515,+50.039764,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1003: [+29.495457,+49.991112,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1004: [+29.807726,+49.981209,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1005: [+29.218763,+50.336571,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1006: [+29.455954,+50.375153,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1007: [+29.840296,+50.342239,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1008: [+30.191797,+49.639885,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1009: [+30.496613,+49.642353,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1010: [+30.767942,+49.680729,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1011: [+30.161652,+50.040165,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1012: [+30.471777,+50.031857,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1013: [+30.857004,+50.026714,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1014: [+30.215729,+50.400570,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1015: [+30.452332,+50.382534,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1016: [+30.858644,+50.320839,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1017: [+31.229849,+49.639542,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1018: [+31.451706,+49.728775,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1019: [+31.899271,+49.710709,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1020: [+31.155169,+50.062115,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1021: [+31.525824,+50.039532,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1022: [+31.773676,+50.004105,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1023: [+31.203428,+50.410973,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1024: [+31.444162,+50.395233,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1025: [+31.892878,+50.348869,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1026: [+32.153301,+49.717129,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1027: [+32.482731,+49.642006,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1028: [+32.829216,+49.690334,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1029: [+32.160320,+50.035458,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1030: [+32.453968,+49.989285,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1031: [+32.877758,+49.999241,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1032: [+32.210239,+50.330723,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1033: [+32.520920,+50.393044,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1034: [+32.823597,+50.395473,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1035: [+33.123859,+49.743721,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1036: [+33.492268,+49.744476,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1037: [+33.794796,+49.737549,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1038: [+33.100540,+49.991707,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1039: [+33.520931,+50.057041,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1040: [+33.855515,+49.994209,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1041: [+33.137817,+50.340546,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1042: [+33.475304,+50.301117,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1043: [+33.886486,+50.348534,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1044: [+34.164127,+49.744385,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1045: [+34.562622,+49.703903,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1046: [+34.829189,+49.704449,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1047: [+34.230247,+49.973618,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1048: [+34.450653,+50.074024,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1049: [+34.890366,+50.065422,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1050: [+34.195587,+50.307995,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1051: [+34.514618,+50.418335,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1052: [+34.898682,+50.412468,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1053: [+35.187389,+49.736412,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1054: [+35.548561,+49.686298,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1055: [+35.799995,+49.648869,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1056: [+35.135944,+50.054211,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1057: [+35.557716,+50.093315,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1058: [+35.883801,+49.995636,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1059: [+35.196846,+50.399837,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1060: [+35.446491,+50.366737,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1061: [+35.878273,+50.353561,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1062: [+36.208195,+49.761238,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1063: [+36.447163,+49.724354,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1064: [+36.804268,+49.678902,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1065: [+36.182274,+50.086887,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1066: [+36.511421,+50.080406,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1067: [+36.859463,+50.083889,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1068: [+36.166683,+50.341553,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1069: [+36.508068,+50.295593,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1070: [+36.813854,+50.374710,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1071: [+37.106918,+49.673969,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1072: [+37.452469,+49.708443,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1073: [+37.800678,+49.634800,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1074: [+37.142063,+49.984005,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1075: [+37.554890,+50.060539,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1076: [+37.847836,+50.022896,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1077: [+37.146149,+50.391243,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1078: [+37.486485,+50.376995,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1079: [+37.801922,+50.419785,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1080: [+26.145683,+50.694611,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1081: [+26.549934,+50.656429,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1082: [+26.815035,+50.681812,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1083: [+26.116388,+50.967682,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1084: [+26.514036,+50.978569,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1085: [+26.800982,+51.011124,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1086: [+26.111874,+51.414478,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1087: [+26.452007,+51.315048,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1088: [+26.896536,+51.382164,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1089: [+27.150267,+50.638634,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1090: [+27.475077,+50.759628,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1091: [+27.797777,+50.632076,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1092: [+27.132200,+51.024975,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1093: [+27.434484,+51.050827,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1094: [+27.812796,+51.015797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1095: [+27.152418,+51.401226,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1096: [+27.478634,+51.336449,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1097: [+27.815691,+51.405930,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1098: [+28.228899,+50.675968,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1099: [+28.459553,+50.711830,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1100: [+28.874266,+50.736572,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1101: [+28.226063,+51.062000,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1102: [+28.500563,+51.088619,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1103: [+28.814074,+51.033642,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1104: [+28.209883,+51.300556,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1105: [+28.522642,+51.389454,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1106: [+28.791962,+51.406921,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1107: [+29.108419,+50.657944,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1108: [+29.565859,+50.700470,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1109: [+29.771557,+50.740318,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1110: [+29.161978,+51.008698,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1111: [+29.439610,+50.974789,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1112: [+29.853176,+51.052700,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1113: [+29.222317,+51.381203,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1114: [+29.551077,+51.400452,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1115: [+29.788046,+51.297371,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1116: [+30.107138,+50.668758,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1117: [+30.512489,+50.712421,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1118: [+30.859211,+50.715916,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1119: [+30.113325,+50.974209,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1120: [+30.566620,+51.000980,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1121: [+30.864244,+50.972946,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1122: [+30.110962,+51.412540,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1123: [+30.464476,+51.387508,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1124: [+30.832165,+51.395672,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1125: [+31.134148,+50.667740,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1126: [+31.500801,+50.649563,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1127: [+31.868782,+50.670151,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1128: [+31.186663,+51.038376,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1129: [+31.563356,+51.080013,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1130: [+31.833807,+51.003464,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1131: [+31.103012,+51.318001,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1132: [+31.489643,+51.320843,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1133: [+31.847889,+51.407387,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1134: [+32.221802,+50.753372,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1135: [+32.453896,+50.674965,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1136: [+32.853199,+50.719460,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1137: [+32.128700,+51.087982,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1138: [+32.524502,+51.069065,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1139: [+32.843990,+50.965698,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1140: [+32.169987,+51.321186,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1141: [+32.550262,+51.357498,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1142: [+32.802917,+51.380291,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1143: [+33.223553,+50.751495,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1144: [+33.559479,+50.736427,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1145: [+33.827744,+50.673801,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1146: [+33.146255,+51.041569,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1147: [+33.443310,+51.026714,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1148: [+33.777138,+51.079296,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1149: [+33.204704,+51.320900,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1150: [+33.442631,+51.384880,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1151: [+33.824780,+51.320488,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1152: [+34.185558,+50.637081,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1153: [+34.522926,+50.709209,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1154: [+34.777260,+50.695465,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1155: [+34.111656,+51.002892,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1156: [+34.482647,+51.035000,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1157: [+34.768551,+50.980843,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1158: [+34.150738,+51.306892,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1159: [+34.539558,+51.411106,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1160: [+34.895054,+51.305096,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1161: [+35.197632,+50.650902,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1162: [+35.494675,+50.709347,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1163: [+35.893951,+50.656967,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1164: [+35.168327,+51.040340,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1165: [+35.559547,+51.000843,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1166: [+35.789150,+51.029034,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1167: [+35.184204,+51.298374,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1168: [+35.553635,+51.385468,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1169: [+35.819077,+51.349380,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1170: [+36.115459,+50.667446,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1171: [+36.453609,+50.717052,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1172: [+36.816837,+50.672966,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1173: [+36.152802,+50.990734,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1174: [+36.507339,+51.094551,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1175: [+36.834019,+51.057903,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1176: [+36.105309,+51.316654,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1177: [+36.553280,+51.304852,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1178: [+36.785191,+51.415314,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1179: [+37.189362,+50.660915,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1180: [+37.544514,+50.666378,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1181: [+37.815769,+50.637215,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1182: [+37.200932,+51.020805,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1183: [+37.495907,+51.093498,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1184: [+37.887375,+50.969421,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1185: [+37.167770,+51.329918,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1186: [+37.483566,+51.403187,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1187: [+37.850609,+51.396740,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1188: [+26.174967,+51.717922,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1189: [+26.532068,+51.700085,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1190: [+26.836506,+51.637512,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1191: [+26.128227,+51.976681,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1192: [+26.546846,+52.000217,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1193: [+26.815516,+51.986500,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1194: [+26.159708,+52.331734,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1195: [+26.534210,+52.314671,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1196: [+26.799082,+52.420162,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1197: [+27.221546,+51.734447,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1198: [+27.454885,+51.648010,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1199: [+27.783737,+51.650158,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1200: [+27.169382,+52.023293,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1201: [+27.461916,+52.043797,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1202: [+27.782387,+52.081867,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1203: [+27.122646,+52.426094,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1204: [+27.553684,+52.422115,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1205: [+27.825413,+52.372490,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1206: [+28.195730,+51.663479,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1207: [+28.455826,+51.671444,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1208: [+28.770428,+51.675362,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1209: [+28.222630,+52.063423,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1210: [+28.483170,+51.983028,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1211: [+28.800524,+52.057865,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1212: [+28.140997,+52.384235,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1213: [+28.500694,+52.376442,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1214: [+28.808376,+52.300129,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1215: [+29.146938,+51.712532,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1216: [+29.555840,+51.739372,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1217: [+29.785217,+51.666298,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1218: [+29.156393,+52.049831,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1219: [+29.489552,+52.064442,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1220: [+29.831295,+51.973480,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1221: [+29.192970,+52.326263,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1222: [+29.456808,+52.318676,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1223: [+29.899529,+52.294834,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1224: [+30.215548,+51.721054,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1225: [+30.526377,+51.736546,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1226: [+30.895494,+51.745495,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1227: [+30.109131,+52.050171,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1228: [+30.466684,+51.986897,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1229: [+30.863811,+52.080727,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1230: [+30.148386,+52.294903,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1231: [+30.467072,+52.427296,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1232: [+30.813093,+52.405918,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1233: [+31.205460,+51.748920,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1234: [+31.528139,+51.740345,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1235: [+31.778778,+51.732067,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1236: [+31.131807,+52.091358,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1237: [+31.494091,+52.088329,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1238: [+31.896719,+52.058914,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1239: [+31.116276,+52.375191,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1240: [+31.483829,+52.410511,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1241: [+31.848871,+52.339054,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1242: [+32.220638,+51.634525,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1243: [+32.565769,+51.638214,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1244: [+32.793968,+51.678852,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1245: [+32.160706,+52.042660,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1246: [+32.454163,+51.988914,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1247: [+32.848091,+52.011627,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1248: [+32.166317,+52.360489,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1249: [+32.514416,+52.356567,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1250: [+32.829662,+52.299801,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1251: [+33.230198,+51.746460,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1252: [+33.553432,+51.740059,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1253: [+33.867157,+51.731487,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1254: [+33.135555,+52.088867,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1255: [+33.543674,+52.082993,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1256: [+33.852711,+52.007267,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1257: [+33.203182,+52.365948,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1258: [+33.476608,+52.391430,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1259: [+33.856209,+52.403934,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1260: [+34.177761,+51.646004,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1261: [+34.454643,+51.715019,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1262: [+34.869801,+51.697624,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1263: [+34.129253,+52.042862,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1264: [+34.491459,+51.981201,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1265: [+34.869259,+52.052898,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1266: [+34.170040,+52.365742,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1267: [+34.458027,+52.407536,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1268: [+34.869686,+52.395123,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1269: [+35.130779,+51.675808,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1270: [+35.487305,+51.668812,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1271: [+35.873878,+51.636581,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1272: [+35.113514,+52.035435,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1273: [+35.562611,+52.084232,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1274: [+35.863914,+52.093395,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1275: [+35.113007,+52.318199,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1276: [+35.522667,+52.323254,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1277: [+35.891228,+52.346684,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1278: [+36.101475,+51.746059,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1279: [+36.526443,+51.691280,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1280: [+36.780518,+51.716015,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1281: [+36.146862,+52.084530,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1282: [+36.447590,+52.082985,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1283: [+36.863811,+52.057674,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1284: [+36.137531,+52.302570,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1285: [+36.528572,+52.427395,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1286: [+36.792183,+52.313309,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1287: [+37.143024,+51.731285,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1288: [+37.493160,+51.681355,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1289: [+37.821247,+51.709633,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1290: [+37.109638,+52.052937,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1291: [+37.548199,+52.091743,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1292: [+37.896259,+52.019711,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1293: [+37.221291,+52.412106,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1294: [+37.534088,+52.415638,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1295: [+37.836216,+52.419529,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1296: [+26.228083,+52.754059,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1297: [+26.467760,+52.720905,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1298: [+26.895361,+52.715679,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1299: [+26.133934,+53.021248,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1300: [+26.447676,+53.056530,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1301: [+26.874157,+53.047363,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1302: [+26.111940,+53.378498,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1303: [+26.531075,+53.354462,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1304: [+26.825447,+53.395550,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1305: [+27.186844,+52.738979,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1306: [+27.509798,+52.685246,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1307: [+27.771620,+52.692181,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1308: [+27.209784,+53.063320,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1309: [+27.555914,+53.077522,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1310: [+27.842365,+52.984642,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1311: [+27.144770,+53.382587,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1312: [+27.521910,+53.352310,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1313: [+27.870264,+53.323280,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1314: [+28.120066,+52.671219,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1315: [+28.461159,+52.635223,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1316: [+28.783125,+52.642929,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1317: [+28.184456,+53.035881,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1318: [+28.520199,+53.005539,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1319: [+28.799465,+52.983616,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1320: [+28.222631,+53.357342,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1321: [+28.525751,+53.355419,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1322: [+28.816057,+53.403053,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1323: [+29.207741,+52.688705,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1324: [+29.553543,+52.658379,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1325: [+29.860205,+52.700634,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1326: [+29.171360,+53.056450,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1327: [+29.507917,+52.996506,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1328: [+29.813246,+52.984547,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1329: [+29.118004,+53.342224,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1330: [+29.503080,+53.393337,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1331: [+29.897375,+53.326653,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1332: [+30.228010,+52.687588,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1333: [+30.560743,+52.631908,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1334: [+30.855007,+52.686565,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1335: [+30.106350,+53.002819,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1336: [+30.445833,+53.020092,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1337: [+30.823641,+53.043903,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1338: [+30.152920,+53.394993,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1339: [+30.493826,+53.392311,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1340: [+30.886061,+53.396778,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1341: [+31.106194,+52.707878,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1342: [+31.546730,+52.742496,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1343: [+31.781065,+52.757229,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1344: [+31.151148,+53.082516,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1345: [+31.546522,+52.975506,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1346: [+31.796497,+52.982838,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1347: [+31.164881,+53.303299,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1348: [+31.458332,+53.407093,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1349: [+31.768354,+53.348728,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1350: [+32.229572,+52.741928,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1351: [+32.509586,+52.646797,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1352: [+32.771206,+52.690918,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1353: [+32.102791,+52.961388,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1354: [+32.525040,+52.978535,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1355: [+32.855042,+53.040180,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1356: [+32.132862,+53.409042,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1357: [+32.547379,+53.400295,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1358: [+32.814655,+53.411274,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1359: [+33.107964,+52.677509,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1360: [+33.549393,+52.722252,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1361: [+33.876259,+52.697857,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1362: [+33.140415,+53.069706,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1363: [+33.487480,+53.043488,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1364: [+33.823132,+52.998482,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1365: [+33.153606,+53.320538,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1366: [+33.555695,+53.360081,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1367: [+33.886368,+53.347603,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1368: [+34.167728,+52.708282,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1369: [+34.507839,+52.646992,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1370: [+34.799141,+52.688072,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1371: [+34.207287,+53.065674,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1372: [+34.440655,+53.074883,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1373: [+34.880047,+52.983032,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1374: [+34.200047,+53.384205,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1375: [+34.479080,+53.368061,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1376: [+34.784893,+53.357353,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1377: [+35.163120,+52.738113,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1378: [+35.491985,+52.724525,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1379: [+35.817135,+52.683510,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1380: [+35.180683,+53.080269,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1381: [+35.544750,+53.020599,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1382: [+35.782356,+53.034622,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1383: [+35.175343,+53.378330,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1384: [+35.450748,+53.405251,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1385: [+35.802040,+53.363426,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1386: [+36.128307,+52.640739,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1387: [+36.551025,+52.721851,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1388: [+36.895729,+52.733440,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1389: [+36.115437,+52.982918,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1390: [+36.439560,+53.006939,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1391: [+36.811745,+53.040195,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1392: [+36.152557,+53.351212,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1393: [+36.459557,+53.311703,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1394: [+36.861771,+53.368286,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [+0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1395: [+37.141144,+52.748165,+0.500000] , 1. [+0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1396: [+37.544632,+52.717861,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1397: [+37.810879,+52.690613,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1398: [+37.144939,+52.971882,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1399: [+37.534695,+53.053185,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1400: [+37.884216,+52.977135,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1401: [+37.140438,+53.393883,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1402: [+37.530254,+53.383579,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] -1403: [+37.851040,+53.412434,+0.500000] , 1. [-0.000000,-1.216000,+0.000000] [-0.000000,-0.000000,+0.000000] [-0.000000,+0.000000,+0.000000] [+0.000000,+0.000000,+0.000000] diff --git a/scenes/cell-growth/view-particles.ipynb b/scenes/cell-growth/view-particles.ipynb deleted file mode 100644 index 05ea3695..00000000 --- a/scenes/cell-growth/view-particles.ipynb +++ /dev/null @@ -1,111 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import k3d\n", - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": " position\n \n0 [+26.122849,+46.122944,+0.500000] , 1. [+0.00...\n1 [+26.453651,+46.224190,+0.500000] , 1. [+0.00...\n2 [+26.898863,+46.196522,+0.500000] , 1. [+0.00...\n3 [+26.112722,+46.452816,+0.500000] , 1. [+0.00...\n4 [+26.557117,+46.541092,+0.500000] , 1. [+0.00...\n... ...\n1399 [+37.534695,+58.525185,+0.500000] , 1. [+0.00...\n1400 [+37.884216,+58.449135,+0.500000] , 1. [+0.00...\n1401 [+37.140438,+58.865883,+0.500000] , 1. [+0.00...\n1402 [+37.530254,+58.855579,+0.500000] , 1. [+0.00...\n1403 [+37.851040,+58.884434,+0.500000] , 1. [+0.00...\n\n[1404 rows x 1 columns]", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
position
0[+26.122849,+46.122944,+0.500000] , 1. [+0.00...
1[+26.453651,+46.224190,+0.500000] , 1. [+0.00...
2[+26.898863,+46.196522,+0.500000] , 1. [+0.00...
3[+26.112722,+46.452816,+0.500000] , 1. [+0.00...
4[+26.557117,+46.541092,+0.500000] , 1. [+0.00...
......
1399[+37.534695,+58.525185,+0.500000] , 1. [+0.00...
1400[+37.884216,+58.449135,+0.500000] , 1. [+0.00...
1401[+37.140438,+58.865883,+0.500000] , 1. [+0.00...
1402[+37.530254,+58.855579,+0.500000] , 1. [+0.00...
1403[+37.851040,+58.884434,+0.500000] , 1. [+0.00...
\n

1404 rows × 1 columns

\n
" - }, - "metadata": {}, - "execution_count": 27 - } - ], - "source": [ - "df = pd.read_table('./results/particles-frame-0.txt', skiprows=1, usecols=[0,1], names=['','position'], sep=':', index_col=0)\n", - "df" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": "Plot(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camera=[2, -3, 0.2, 0.0, 0…", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "bbed7b1ed2124cf29489bae49f656fde" - } - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { - "text/plain": "Output()", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "984bfc3ac728407e8362a7ae19e20f18" - } - }, - "metadata": {} - } - ], - "source": [ - "Nx, Ny = 34, 33\n", - "xmin, xmax = -3, 4\n", - "ymin, ymax = -0, 3\n", - "\n", - "x = np.linspace(xmin, xmax, Nx)\n", - "y = np.linspace(ymin, ymax, Ny)\n", - "x, y = np.meshgrid(x, y)\n", - "f = np.sin(x**2 + y**2)\n", - "\n", - "plot = k3d.plot()\n", - "plt_surface = k3d.surface(f.astype(np.float32), bounds=[xmin,xmax,ymin,ymax])\n", - "plot += plt_surface\n", - "plot.display()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6-final" - }, - "orig_nbformat": 2, - "kernelspec": { - "name": "python37664bitanaconda3condab615bb15fcd4464bb2000e7d9e446dfd", - "display_name": "Python 3.7.6 64-bit ('anaconda3': conda)" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file diff --git a/scenes/view_particles_in_notebook.ipynb b/scenes/view_particles_in_notebook.ipynb new file mode 100644 index 00000000..b4d62ecc --- /dev/null +++ b/scenes/view_particles_in_notebook.ipynb @@ -0,0 +1,132 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import k3d\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "Plot(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camera=[2, -3, 0.2, 0.0, 0…", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "dc015d82dedc4ed1a40a64c9ac473461" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": "Output()", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "4f43755aff934a1981a52faf60c0ef39" + } + }, + "metadata": {} + } + ], + "source": [ + "# View 50 frames at a time\n", + "\n", + "start = 50\n", + "stop = 100\n", + "\n", + "if start > 0:\n", + " offset = start\n", + " start = 0\n", + " stop = stop - offset\n", + "else:\n", + " offset = 0\n", + "\n", + "positions = {\n", + " str(t):np.loadtxt('frames/particles-frame-%d.nptxt' % (t+offset)).astype(np.float32) for t in range(start, stop, 1)\n", + "}\n", + "plot = k3d.plot(name='points', time=float(start))\n", + "plt_points = k3d.points(positions=positions, point_size=0.4, shader='3dSpecular', color=0xD0D3D4, opacity=0.5)\n", + "plot += plt_points\n", + "plot.display()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "Plot(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camera=[2, -3, 0.2, 0.0, 0…", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "46416d7dc80c488ba1c2fad5e4c0d307" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": "Output()", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "6eeaba734d8f4348a42cb3ce0b7da2c9" + } + }, + "metadata": {} + } + ], + "source": [ + "positions = np.loadtxt('frames/particles-frame-0.nptxt').astype(np.float32)\n", + "plot = k3d.plot()\n", + "points = k3d.points(positions, point_size=0.4, shader='3dSpecular')\n", + "plot += points\n", + "plot.display()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6-final" + }, + "orig_nbformat": 2, + "kernelspec": { + "name": "python37664bitanaconda3condab615bb15fcd4464bb2000e7d9e446dfd", + "display_name": "Python 3.7.6 64-bit ('anaconda3': conda)" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/scenes/cell-growth/export-particles.py b/scenes/view_particles_in_notebook.py similarity index 73% rename from scenes/cell-growth/export-particles.py rename to scenes/view_particles_in_notebook.py index 014cdd51..5d88a00d 100644 --- a/scenes/cell-growth/export-particles.py +++ b/scenes/view_particles_in_notebook.py @@ -4,10 +4,12 @@ from manta import * # solver params -dim = 2 +dim = 3 +# dim = 2 particleNumber = 2 res = 64 gs = vec3(res,res,res) + if (dim==2): gs.z=1 particleNumber = 3 # use more particles in 2d @@ -46,10 +48,10 @@ sampleFlagsWithParticles(flags=flags, parts=pp, discretization=particleNumber, randomness=0.2) #main loop -for t in range(20): +for t in range(200): mantaMsg('\nFrame %i, simulation time %f' % (s.frame, s.timeTotal)) - # APIC + # Affine Particle in Cell (APIC) method pp.advectInGrid(flags=flags, vel=vel, integrationMode=IntRK4, deleteInObstacle=False) apicMapPartsToMAC(flags=flags, vel=vel, parts=pp, partVel=pVel, cpx=pCx, cpy=pCy, cpz=pCz, mass=mass) extrapolateMACFromWeight(vel=vel , distance=2, weight=tmpVec3) @@ -70,8 +72,30 @@ s.step() - file_name = 'results/particles-frame-%d.txt' % s.frame + path = './frames/' + file_name = path + 'particles-frame-%d.nptxt' % s.frame pp.save(file_name) + + # The following lines read in the text file exported with BasicParticleSystem and converts it to a NumPy array format. + # NumPy is then able to read the file. + # file = open(file_name, 'r') + # lines = file.read().split('\n') + # file.close() + # particles = [] + # lines.pop(0) + # lines.pop() + + # for line in lines: + # token = line.split(']').pop(0) + # coordinates = token.split('[').pop(1) + # coordinates = coordinates.replace('+', '') + # coordinates = coordinates.replace(',', ' ') + # particles.append(coordinates + '\n') + + # file = open(file_name, 'w') + # file.writelines(particles) + # file.close() + diff --git a/source/particle.cpp b/source/particle.cpp index 11d1641d..57fa2b7c 100644 --- a/source/particle.cpp +++ b/source/particle.cpp @@ -154,6 +154,22 @@ void BasicParticleSystem::writeParticlesText(const string name) const ofs.close(); } +void BasicParticleSystem::writeParticlesNpText(const string name) const +{ + Vec3 positions; + ofstream ofs(name.c_str()); + + if(!ofs.good()) errMsg("can't open file!"); + + for(IndexInt i=0; i < this->size(); ++i) { + positions = this->getPos(i); + ofs << positions[0] << " " << positions[1] << " " << positions[2]; + ofs << "\n"; + } + + ofs.close(); +} + void BasicParticleSystem::writeParticlesRawPositionsGz(const string name) const { # if NO_ZLIB!=1 @@ -207,6 +223,8 @@ void BasicParticleSystem::save(const string name) const string ext = name.substr(name.find_last_of('.')); if(ext == ".txt") this->writeParticlesText(name); + else if(ext == ".nptxt") + this->writeParticlesNpText(name); else if(ext == ".uni") writeParticlesUni(name, this); else if(ext == ".raw") // raw = uni for now diff --git a/source/particle.h b/source/particle.h index 079f376a..a9b5f6ab 100644 --- a/source/particle.h +++ b/source/particle.h @@ -200,6 +200,10 @@ PYTHON() class BasicParticleSystem : public ParticleSystem { //! save to text file void writeParticlesText(const std::string name) const; + + //! save to numpy text file + void writeParticlesNpText(const std::string name) const; + //! other output formats void writeParticlesRawPositionsGz(const std::string name) const; void writeParticlesRawVelocityGz(const std::string name) const; From 8469b07ddf9dfb7233a51bb3cd7686cdbc72898e Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Thu, 2 Jul 2020 00:54:51 -0400 Subject: [PATCH 04/15] Added the method BasicParticleSystem.writeParticlesNumpyText which serializes the particle coordinates in NumPy text format. The particle coordinates can be read from a Jupyter notebook and visualized with K3D-Jupyter. --- scenes/view_particles_in_notebook.ipynb | 26 +++++++------------------ scenes/view_particles_in_notebook.py | 24 ++--------------------- source/particle.cpp | 12 +++++++----- source/particle.h | 4 ++-- 4 files changed, 18 insertions(+), 48 deletions(-) diff --git a/scenes/view_particles_in_notebook.ipynb b/scenes/view_particles_in_notebook.ipynb index b4d62ecc..7824eba6 100644 --- a/scenes/view_particles_in_notebook.ipynb +++ b/scenes/view_particles_in_notebook.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -22,7 +22,7 @@ "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, - "model_id": "dc015d82dedc4ed1a40a64c9ac473461" + "model_id": "0e0a5ef4bff240f5969982c65a22206a" } }, "metadata": {} @@ -34,7 +34,7 @@ "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, - "model_id": "4f43755aff934a1981a52faf60c0ef39" + "model_id": "7d368ed0ef33419097359475b5b8390e" } }, "metadata": {} @@ -43,8 +43,8 @@ "source": [ "# View 50 frames at a time\n", "\n", - "start = 50\n", - "stop = 100\n", + "start = 0\n", + "stop = 49\n", "\n", "if start > 0:\n", " offset = start\n", @@ -64,21 +64,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 2, "metadata": {}, "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": "Plot(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camera=[2, -3, 0.2, 0.0, 0…", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "46416d7dc80c488ba1c2fad5e4c0d307" - } - }, - "metadata": {} - }, { "output_type": "display_data", "data": { @@ -86,7 +74,7 @@ "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, - "model_id": "6eeaba734d8f4348a42cb3ce0b7da2c9" + "model_id": "cb2dd2dd2645492bb35b24809bcdc72c" } }, "metadata": {} diff --git a/scenes/view_particles_in_notebook.py b/scenes/view_particles_in_notebook.py index 5d88a00d..4584b00d 100644 --- a/scenes/view_particles_in_notebook.py +++ b/scenes/view_particles_in_notebook.py @@ -72,29 +72,9 @@ s.step() - path = './frames/' - file_name = path + 'particles-frame-%d.nptxt' % s.frame + file_name = './frames/' + 'particles-frame-%d.nptxt' % s.frame pp.save(file_name) - - # The following lines read in the text file exported with BasicParticleSystem and converts it to a NumPy array format. - # NumPy is then able to read the file. - # file = open(file_name, 'r') - # lines = file.read().split('\n') - # file.close() - # particles = [] - # lines.pop(0) - # lines.pop() - - # for line in lines: - # token = line.split(']').pop(0) - # coordinates = token.split('[').pop(1) - # coordinates = coordinates.replace('+', '') - # coordinates = coordinates.replace(',', ' ') - # particles.append(coordinates + '\n') - - # file = open(file_name, 'w') - # file.writelines(particles) - # file.close() + diff --git a/source/particle.cpp b/source/particle.cpp index 57fa2b7c..e1f47538 100644 --- a/source/particle.cpp +++ b/source/particle.cpp @@ -154,7 +154,10 @@ void BasicParticleSystem::writeParticlesText(const string name) const ofs.close(); } -void BasicParticleSystem::writeParticlesNpText(const string name) const +// The writeParticlesNumPyText method serializes the coordinates of the particles in NumPy text format. +// The NumPy text files can be read from a Jupyter notebook to instantiate NumPy arrays. +// The NumPy arrays can be used to visualize the particles in a Jupyter notebook using the K3D library. +void BasicParticleSystem::writeParticlesNumPyText(const string name) const { Vec3 positions; ofstream ofs(name.c_str()); @@ -163,10 +166,9 @@ void BasicParticleSystem::writeParticlesNpText(const string name) const for(IndexInt i=0; i < this->size(); ++i) { positions = this->getPos(i); - ofs << positions[0] << " " << positions[1] << " " << positions[2]; - ofs << "\n"; + ofs << positions[0] << " " << positions[1] << " " << positions[2] << "\n"; } - + ofs.close(); } @@ -224,7 +226,7 @@ void BasicParticleSystem::save(const string name) const if(ext == ".txt") this->writeParticlesText(name); else if(ext == ".nptxt") - this->writeParticlesNpText(name); + this->writeParticlesNumPyText(name); else if(ext == ".uni") writeParticlesUni(name, this); else if(ext == ".raw") // raw = uni for now diff --git a/source/particle.h b/source/particle.h index a9b5f6ab..2f91e54d 100644 --- a/source/particle.h +++ b/source/particle.h @@ -201,8 +201,8 @@ PYTHON() class BasicParticleSystem : public ParticleSystem { //! save to text file void writeParticlesText(const std::string name) const; - //! save to numpy text file - void writeParticlesNpText(const std::string name) const; + //! save to NumPy text file + void writeParticlesNumPyText(const std::string name) const; //! other output formats void writeParticlesRawPositionsGz(const std::string name) const; From cb061051876eba48b67695dddbce0e42a1bd4130 Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Thu, 2 Jul 2020 08:28:36 -0400 Subject: [PATCH 05/15] Small changes to the notebook. --- scenes/view_particles_in_notebook.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scenes/view_particles_in_notebook.ipynb b/scenes/view_particles_in_notebook.ipynb index 7824eba6..bc6a518e 100644 --- a/scenes/view_particles_in_notebook.ipynb +++ b/scenes/view_particles_in_notebook.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -22,7 +22,7 @@ "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, - "model_id": "0e0a5ef4bff240f5969982c65a22206a" + "model_id": "17f27373a6c54bf5966f364a96882600" } }, "metadata": {} @@ -34,7 +34,7 @@ "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, - "model_id": "7d368ed0ef33419097359475b5b8390e" + "model_id": "070ba65666b8407794596c0157bdd6bb" } }, "metadata": {} From b915cd605cbf6a252737c5ee9094c5403298e313 Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Thu, 16 Jul 2020 20:58:35 -0400 Subject: [PATCH 06/15] Minor changes to the view_particles_in_notebook.ipynb file. --- scenes/view_particles_in_notebook.ipynb | 60 ++++++++++--------------- scenes/view_particles_in_notebook.py | 2 +- 2 files changed, 25 insertions(+), 37 deletions(-) diff --git a/scenes/view_particles_in_notebook.ipynb b/scenes/view_particles_in_notebook.ipynb index bc6a518e..45413390 100644 --- a/scenes/view_particles_in_notebook.ipynb +++ b/scenes/view_particles_in_notebook.ipynb @@ -18,15 +18,29 @@ { "output_type": "display_data", "data": { - "text/plain": "Plot(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camera=[2, -3, 0.2, 0.0, 0…", + "text/plain": "Output()", "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, - "model_id": "17f27373a6c54bf5966f364a96882600" + "model_id": "b32e24084a764b3f960f19125f2df785" } }, "metadata": {} - }, + } + ], + "source": [ + "positions = np.loadtxt('frames/particles-frame-0.nptxt').astype(np.float32)\n", + "plot = k3d.plot()\n", + "points = k3d.points(positions, point_size=0.4, shader='3dSpecular')\n", + "plot += points\n", + "plot.display()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ { "output_type": "display_data", "data": { @@ -34,7 +48,7 @@ "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, - "model_id": "070ba65666b8407794596c0157bdd6bb" + "model_id": "7abf44e35b4c430aba7bf0c6d17fba96" } }, "metadata": {} @@ -62,32 +76,6 @@ "plot.display()" ] }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": "Output()", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "cb2dd2dd2645492bb35b24809bcdc72c" - } - }, - "metadata": {} - } - ], - "source": [ - "positions = np.loadtxt('frames/particles-frame-0.nptxt').astype(np.float32)\n", - "plot = k3d.plot()\n", - "points = k3d.points(positions, point_size=0.4, shader='3dSpecular')\n", - "plot += points\n", - "plot.display()" - ] - }, { "cell_type": "code", "execution_count": null, @@ -97,6 +85,11 @@ } ], "metadata": { + "kernelspec": { + "display_name": "Python 3.7.6 64-bit ('anaconda3': conda)", + "language": "python", + "name": "python37664bitanaconda3condab615bb15fcd4464bb2000e7d9e446dfd" + }, "language_info": { "codemirror_mode": { "name": "ipython", @@ -108,13 +101,8 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6-final" - }, - "orig_nbformat": 2, - "kernelspec": { - "name": "python37664bitanaconda3condab615bb15fcd4464bb2000e7d9e446dfd", - "display_name": "Python 3.7.6 64-bit ('anaconda3': conda)" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } \ No newline at end of file diff --git a/scenes/view_particles_in_notebook.py b/scenes/view_particles_in_notebook.py index 4584b00d..54e95dc9 100644 --- a/scenes/view_particles_in_notebook.py +++ b/scenes/view_particles_in_notebook.py @@ -72,7 +72,7 @@ s.step() - file_name = './frames/' + 'particles-frame-%d.nptxt' % s.frame + file_name = './frames/particles-frame-%d.nptxt' % s.frame pp.save(file_name) From 635639ff3d9a549bce713ea286d57e57b83bb8d9 Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Fri, 24 Jul 2020 04:57:45 -0400 Subject: [PATCH 07/15] Added the openVDB source code but I haven't been able to compile the version of mantaflow that supports openVDB. --- CMakeLists.txt | 69 +- openvdb/CMakeCache.txt | 380 ++ .../3.18.0-rc2/CMakeCCompiler.cmake | 77 + .../3.18.0-rc2/CMakeCXXCompiler.cmake | 89 + .../CMakeFiles/3.18.0-rc2/CMakeSystem.cmake | 15 + .../3.18.0-rc2/CompilerIdC/CMakeCCompilerId.c | 671 +++ .../CMakeFiles/3.18.0-rc2/CompilerIdC/a.out | Bin 0 -> 8616 bytes .../CompilerIdCXX/CMakeCXXCompilerId.cpp | 660 +++ .../CMakeFiles/3.18.0-rc2/CompilerIdCXX/a.out | Bin 0 -> 8600 bytes openvdb/CMakeFiles/CMakeOutput.log | 266 + openvdb/CMakeFiles/cmake.check_cache | 1 + openvdb/CMakeLists.txt | 531 ++ openvdb/COPYRIGHT | 2 + openvdb/Exceptions.h | 93 + openvdb/Grid.cc | 433 ++ openvdb/Grid.h | 1827 ++++++ openvdb/LICENSE | 373 ++ openvdb/Makefile | 1057 ++++ openvdb/MetaMap.cc | 209 + openvdb/MetaMap.h | 217 + openvdb/Metadata.cc | 189 + openvdb/Metadata.h | 437 ++ openvdb/Platform.cc | 7 + openvdb/Platform.h | 241 + openvdb/PlatformConfig.h | 26 + openvdb/README | 16 + openvdb/Types.h | 739 +++ openvdb/cmd/CMakeLists.txt | 220 + openvdb/cmd/openvdb_lod.cc | 358 ++ openvdb/cmd/openvdb_print.cc | 315 + openvdb/cmd/openvdb_render.cc | 691 +++ openvdb/cmd/openvdb_view.cc | 169 + openvdb/io/Archive.cc | 1459 +++++ openvdb/io/Archive.h | 197 + openvdb/io/Compression.cc | 281 + openvdb/io/Compression.h | 755 +++ openvdb/io/DelayedLoadMetadata.cc | 299 + openvdb/io/DelayedLoadMetadata.h | 102 + openvdb/io/File.cc | 833 +++ openvdb/io/File.h | 236 + openvdb/io/GridDescriptor.cc | 196 + openvdb/io/GridDescriptor.h | 105 + openvdb/io/Queue.cc | 313 + openvdb/io/Queue.h | 247 + openvdb/io/Stream.cc | 238 + openvdb/io/Stream.h | 89 + openvdb/io/TempFile.cc | 136 + openvdb/io/TempFile.h | 50 + openvdb/io/io.h | 274 + openvdb/lib/libopenvdb.7.0.0.dylib | Bin 0 -> 3765576 bytes openvdb/lib/libopenvdb.7.0.dylib | 1 + openvdb/lib/libopenvdb.dylib | 1 + openvdb/math/BBox.h | 423 ++ openvdb/math/ConjGradient.h | 1771 ++++++ openvdb/math/Coord.h | 600 ++ openvdb/math/DDA.h | 344 ++ openvdb/math/FiniteDifference.h | 2324 ++++++++ openvdb/math/LegacyFrustum.h | 165 + openvdb/math/Maps.cc | 262 + openvdb/math/Maps.h | 2692 +++++++++ openvdb/math/Mat.h | 1019 ++++ openvdb/math/Mat3.h | 828 +++ openvdb/math/Mat4.h | 1346 +++++ openvdb/math/Math.h | 913 +++ openvdb/math/Operators.h | 2126 +++++++ openvdb/math/Proximity.cc | 131 + openvdb/math/Proximity.h | 48 + openvdb/math/QuantizedUnitVec.cc | 2082 +++++++ openvdb/math/QuantizedUnitVec.h | 121 + openvdb/math/Quat.h | 631 ++ openvdb/math/Ray.h | 311 + openvdb/math/Stats.h | 382 ++ openvdb/math/Stencils.h | 1815 ++++++ openvdb/math/Transform.cc | 547 ++ openvdb/math/Transform.h | 279 + openvdb/math/Tuple.h | 245 + openvdb/math/Vec2.h | 538 ++ openvdb/math/Vec3.h | 668 +++ openvdb/math/Vec4.h | 566 ++ openvdb/openvdb.cc | 161 + openvdb/openvdb.h | 68 + openvdb/points/AttributeArray.cc | 210 + openvdb/points/AttributeArray.h | 2339 ++++++++ openvdb/points/AttributeArrayString.cc | 366 ++ openvdb/points/AttributeArrayString.h | 203 + openvdb/points/AttributeGroup.cc | 116 + openvdb/points/AttributeGroup.h | 173 + openvdb/points/AttributeSet.cc | 1179 ++++ openvdb/points/AttributeSet.h | 465 ++ openvdb/points/IndexFilter.h | 581 ++ openvdb/points/IndexIterator.h | 329 ++ openvdb/points/PointAdvect.h | 247 + openvdb/points/PointAttribute.h | 549 ++ openvdb/points/PointConversion.h | 1103 ++++ openvdb/points/PointCount.h | 325 + openvdb/points/PointDataGrid.h | 1736 ++++++ openvdb/points/PointDelete.h | 278 + openvdb/points/PointGroup.h | 845 +++ openvdb/points/PointMask.h | 435 ++ openvdb/points/PointMove.h | 1237 ++++ openvdb/points/PointSample.h | 544 ++ openvdb/points/PointScatter.h | 554 ++ openvdb/points/StreamCompression.cc | 633 ++ openvdb/points/StreamCompression.h | 289 + openvdb/points/points.cc | 70 + openvdb/python/CMakeLists.txt | 312 + openvdb/python/pyAccessor.h | 312 + openvdb/python/pyFloatGrid.cc | 39 + openvdb/python/pyGrid.h | 2566 ++++++++ openvdb/python/pyIntGrid.cc | 21 + openvdb/python/pyMetadata.cc | 64 + openvdb/python/pyOpenVDBModule.cc | 958 +++ openvdb/python/pyPointGrid.cc | 23 + openvdb/python/pyTransform.cc | 326 + openvdb/python/pyVec3Grid.cc | 21 + openvdb/python/pyopenvdb.h | 82 + openvdb/python/pyutil.h | 255 + openvdb/python/test/TestOpenVDB.py | 783 +++ openvdb/tools/ChangeBackground.h | 247 + openvdb/tools/Clip.h | 567 ++ openvdb/tools/Composite.h | 1238 ++++ openvdb/tools/Dense.h | 582 ++ openvdb/tools/DenseSparseTools.h | 1192 ++++ openvdb/tools/Diagnostics.h | 1331 +++++ openvdb/tools/Filter.h | 445 ++ openvdb/tools/FindActiveValues.h | 430 ++ openvdb/tools/GridOperators.h | 1083 ++++ openvdb/tools/GridTransformer.h | 1016 ++++ openvdb/tools/Interpolation.h | 1017 ++++ openvdb/tools/LevelSetAdvect.h | 575 ++ openvdb/tools/LevelSetFilter.h | 510 ++ openvdb/tools/LevelSetFracture.h | 317 + openvdb/tools/LevelSetMeasure.h | 563 ++ openvdb/tools/LevelSetMorph.h | 644 ++ openvdb/tools/LevelSetPlatonic.h | 478 ++ openvdb/tools/LevelSetRebuild.h | 327 + openvdb/tools/LevelSetSphere.h | 233 + openvdb/tools/LevelSetTracker.h | 679 +++ openvdb/tools/LevelSetUtil.h | 2599 ++++++++ openvdb/tools/Mask.h | 123 + openvdb/tools/MeshToVolume.h | 4216 +++++++++++++ openvdb/tools/Morphology.h | 1069 ++++ openvdb/tools/MultiResGrid.h | 951 +++ openvdb/tools/ParticleAtlas.h | 1033 ++++ openvdb/tools/ParticlesToLevelSet.h | 1032 ++++ openvdb/tools/PointAdvect.h | 393 ++ openvdb/tools/PointIndexGrid.h | 1807 ++++++ openvdb/tools/PointPartitioner.h | 1049 ++++ openvdb/tools/PointScatter.h | 421 ++ openvdb/tools/PointsToMask.h | 252 + openvdb/tools/PoissonSolver.h | 842 +++ openvdb/tools/PotentialFlow.h | 395 ++ openvdb/tools/Prune.h | 400 ++ openvdb/tools/RayIntersector.h | 671 +++ openvdb/tools/RayTracer.h | 1094 ++++ openvdb/tools/SignedFloodFill.h | 279 + openvdb/tools/Statistics.h | 407 ++ openvdb/tools/TopologyToLevelSet.h | 258 + openvdb/tools/ValueTransformer.h | 685 +++ openvdb/tools/VectorTransformer.h | 134 + openvdb/tools/VelocityFields.h | 275 + openvdb/tools/VolumeAdvect.h | 541 ++ openvdb/tools/VolumeToMesh.h | 5262 +++++++++++++++++ openvdb/tools/VolumeToSpheres.h | 1030 ++++ openvdb/tree/InternalNode.h | 3295 +++++++++++ openvdb/tree/Iterator.h | 253 + openvdb/tree/LeafBuffer.h | 492 ++ openvdb/tree/LeafManager.h | 822 +++ openvdb/tree/LeafNode.h | 1990 +++++++ openvdb/tree/LeafNodeBool.h | 1733 ++++++ openvdb/tree/LeafNodeMask.h | 1643 +++++ openvdb/tree/NodeManager.h | 1017 ++++ openvdb/tree/NodeUnion.h | 200 + openvdb/tree/RootNode.h | 3484 +++++++++++ openvdb/tree/Tree.h | 2353 ++++++++ openvdb/tree/TreeIterator.h | 1369 +++++ openvdb/tree/ValueAccessor.h | 2634 +++++++++ openvdb/unittest/CMakeLists.txt | 198 + openvdb/unittest/TestAttributeArray.cc | 2453 ++++++++ openvdb/unittest/TestAttributeArrayString.cc | 528 ++ openvdb/unittest/TestAttributeGroup.cc | 560 ++ openvdb/unittest/TestAttributeSet.cc | 1108 ++++ openvdb/unittest/TestBBox.cc | 95 + openvdb/unittest/TestClip.cc | 248 + openvdb/unittest/TestConjGradient.cc | 211 + openvdb/unittest/TestCoord.cc | 396 ++ openvdb/unittest/TestCpt.cc | 535 ++ openvdb/unittest/TestCurl.cc | 561 ++ openvdb/unittest/TestDelayedLoadMetadata.cc | 215 + openvdb/unittest/TestDense.cc | 837 +++ openvdb/unittest/TestDenseSparseTools.cc | 380 ++ openvdb/unittest/TestDiagnostics.cc | 384 ++ openvdb/unittest/TestDivergence.cc | 679 +++ openvdb/unittest/TestDoubleMetadata.cc | 46 + openvdb/unittest/TestExceptions.cc | 80 + openvdb/unittest/TestFile.cc | 2729 +++++++++ openvdb/unittest/TestFindActiveValues.cc | 305 + openvdb/unittest/TestFloatMetadata.cc | 46 + openvdb/unittest/TestGradient.cc | 786 +++ openvdb/unittest/TestGrid.cc | 523 ++ openvdb/unittest/TestGridBbox.cc | 83 + openvdb/unittest/TestGridDescriptor.cc | 159 + openvdb/unittest/TestGridIO.cc | 205 + openvdb/unittest/TestGridTransformer.cc | 239 + openvdb/unittest/TestIndexFilter.cc | 1084 ++++ openvdb/unittest/TestIndexIterator.cc | 396 ++ openvdb/unittest/TestInit.cc | 91 + openvdb/unittest/TestInt32Metadata.cc | 43 + openvdb/unittest/TestInt64Metadata.cc | 43 + openvdb/unittest/TestInternalOrigin.cc | 75 + openvdb/unittest/TestLaplacian.cc | 462 ++ openvdb/unittest/TestLeaf.cc | 535 ++ openvdb/unittest/TestLeafBool.cc | 606 ++ openvdb/unittest/TestLeafIO.cc | 141 + openvdb/unittest/TestLeafManager.cc | 324 + openvdb/unittest/TestLeafMask.cc | 613 ++ openvdb/unittest/TestLeafOrigin.cc | 107 + .../unittest/TestLevelSetRayIntersector.cc | 383 ++ openvdb/unittest/TestLevelSetUtil.cc | 242 + openvdb/unittest/TestLinearInterp.cc | 1070 ++++ openvdb/unittest/TestMaps.cc | 799 +++ openvdb/unittest/TestMat4Metadata.cc | 100 + openvdb/unittest/TestMath.cc | 223 + openvdb/unittest/TestMeanCurvature.cc | 810 +++ openvdb/unittest/TestMeshToVolume.cc | 149 + openvdb/unittest/TestMetaMap.cc | 373 ++ openvdb/unittest/TestMetadata.cc | 163 + openvdb/unittest/TestMetadataIO.cc | 127 + openvdb/unittest/TestMultiResGrid.cc | 341 ++ openvdb/unittest/TestName.cc | 81 + openvdb/unittest/TestNodeIterator.cc | 375 ++ openvdb/unittest/TestNodeManager.cc | 153 + openvdb/unittest/TestNodeMask.cc | 371 ++ openvdb/unittest/TestParticleAtlas.cc | 190 + openvdb/unittest/TestParticlesToLevelSet.cc | 580 ++ openvdb/unittest/TestPointAdvect.cc | 462 ++ openvdb/unittest/TestPointAttribute.cc | 433 ++ openvdb/unittest/TestPointConversion.cc | 1280 ++++ openvdb/unittest/TestPointCount.cc | 849 +++ openvdb/unittest/TestPointDataLeaf.cc | 1550 +++++ openvdb/unittest/TestPointDelete.cc | 249 + openvdb/unittest/TestPointGroup.cc | 699 +++ openvdb/unittest/TestPointIndexGrid.cc | 316 + openvdb/unittest/TestPointMask.cc | 352 ++ openvdb/unittest/TestPointMove.cc | 1176 ++++ openvdb/unittest/TestPointPartitioner.cc | 98 + openvdb/unittest/TestPointSample.cc | 551 ++ openvdb/unittest/TestPointScatter.cc | 703 +++ openvdb/unittest/TestPointsToMask.cc | 172 + openvdb/unittest/TestPoissonSolver.cc | 511 ++ openvdb/unittest/TestPotentialFlow.cc | 429 ++ openvdb/unittest/TestPrePostAPI.cc | 686 +++ openvdb/unittest/TestQuadraticInterp.cc | 330 ++ openvdb/unittest/TestQuantizedUnitVec.cc | 146 + openvdb/unittest/TestQuat.cc | 282 + openvdb/unittest/TestRay.cc | 463 ++ openvdb/unittest/TestStats.cc | 756 +++ openvdb/unittest/TestStream.cc | 248 + openvdb/unittest/TestStreamCompression.cc | 660 +++ openvdb/unittest/TestStringMetadata.cc | 43 + openvdb/unittest/TestTools.cc | 2936 +++++++++ openvdb/unittest/TestTopologyToLevelSet.cc | 54 + openvdb/unittest/TestTransform.cc | 535 ++ openvdb/unittest/TestTree.cc | 3166 ++++++++++ openvdb/unittest/TestTreeCombine.cc | 1044 ++++ openvdb/unittest/TestTreeGetSetValues.cc | 436 ++ openvdb/unittest/TestTreeIterators.cc | 582 ++ openvdb/unittest/TestTreeVisitor.cc | 344 ++ openvdb/unittest/TestTypes.cc | 468 ++ openvdb/unittest/TestUtil.cc | 674 +++ openvdb/unittest/TestValueAccessor.cc | 568 ++ openvdb/unittest/TestVec2Metadata.cc | 97 + openvdb/unittest/TestVec3Metadata.cc | 97 + openvdb/unittest/TestVolumeRayIntersector.cc | 301 + openvdb/unittest/TestVolumeToMesh.cc | 126 + openvdb/unittest/TestVolumeToSpheres.cc | 243 + openvdb/unittest/main.cc | 246 + openvdb/unittest/util.h | 140 + openvdb/util/CpuTimer.h | 174 + openvdb/util/Formats.cc | 148 + openvdb/util/Formats.h | 124 + openvdb/util/MapsUtil.h | 294 + openvdb/util/Name.h | 41 + openvdb/util/NodeMasks.h | 1415 +++++ openvdb/util/NullInterrupter.h | 59 + openvdb/util/PagedArray.h | 749 +++ openvdb/util/Util.cc | 46 + openvdb/util/Util.h | 135 + openvdb/util/logging.h | 319 + openvdb/version.h | 219 + openvdb/viewer/Camera.cc | 249 + openvdb/viewer/Camera.h | 66 + openvdb/viewer/ClipBox.cc | 266 + openvdb/viewer/ClipBox.h | 60 + openvdb/viewer/Font.cc | 169 + openvdb/viewer/Font.h | 39 + openvdb/viewer/RenderModules.cc | 1665 ++++++ openvdb/viewer/RenderModules.h | 182 + openvdb/viewer/Viewer.cc | 1219 ++++ openvdb/viewer/Viewer.h | 62 + scenes/bacilli_on_agar.py | 143 + scenes/flip01_simple.py | 1 + scenes/flip02_surface.py | 1 + scenes/freesurface.py | 2 +- scenes/view_particles_in_notebook.ipynb | 6 +- 305 files changed, 181096 insertions(+), 38 deletions(-) create mode 100644 openvdb/CMakeCache.txt create mode 100644 openvdb/CMakeFiles/3.18.0-rc2/CMakeCCompiler.cmake create mode 100644 openvdb/CMakeFiles/3.18.0-rc2/CMakeCXXCompiler.cmake create mode 100644 openvdb/CMakeFiles/3.18.0-rc2/CMakeSystem.cmake create mode 100644 openvdb/CMakeFiles/3.18.0-rc2/CompilerIdC/CMakeCCompilerId.c create mode 100755 openvdb/CMakeFiles/3.18.0-rc2/CompilerIdC/a.out create mode 100644 openvdb/CMakeFiles/3.18.0-rc2/CompilerIdCXX/CMakeCXXCompilerId.cpp create mode 100755 openvdb/CMakeFiles/3.18.0-rc2/CompilerIdCXX/a.out create mode 100644 openvdb/CMakeFiles/CMakeOutput.log create mode 100644 openvdb/CMakeFiles/cmake.check_cache create mode 100644 openvdb/CMakeLists.txt create mode 100644 openvdb/COPYRIGHT create mode 100644 openvdb/Exceptions.h create mode 100644 openvdb/Grid.cc create mode 100644 openvdb/Grid.h create mode 100644 openvdb/LICENSE create mode 100644 openvdb/Makefile create mode 100644 openvdb/MetaMap.cc create mode 100644 openvdb/MetaMap.h create mode 100644 openvdb/Metadata.cc create mode 100644 openvdb/Metadata.h create mode 100644 openvdb/Platform.cc create mode 100644 openvdb/Platform.h create mode 100644 openvdb/PlatformConfig.h create mode 100644 openvdb/README create mode 100644 openvdb/Types.h create mode 100644 openvdb/cmd/CMakeLists.txt create mode 100644 openvdb/cmd/openvdb_lod.cc create mode 100644 openvdb/cmd/openvdb_print.cc create mode 100644 openvdb/cmd/openvdb_render.cc create mode 100644 openvdb/cmd/openvdb_view.cc create mode 100644 openvdb/io/Archive.cc create mode 100644 openvdb/io/Archive.h create mode 100644 openvdb/io/Compression.cc create mode 100644 openvdb/io/Compression.h create mode 100644 openvdb/io/DelayedLoadMetadata.cc create mode 100644 openvdb/io/DelayedLoadMetadata.h create mode 100644 openvdb/io/File.cc create mode 100644 openvdb/io/File.h create mode 100644 openvdb/io/GridDescriptor.cc create mode 100644 openvdb/io/GridDescriptor.h create mode 100644 openvdb/io/Queue.cc create mode 100644 openvdb/io/Queue.h create mode 100644 openvdb/io/Stream.cc create mode 100644 openvdb/io/Stream.h create mode 100644 openvdb/io/TempFile.cc create mode 100644 openvdb/io/TempFile.h create mode 100644 openvdb/io/io.h create mode 100644 openvdb/lib/libopenvdb.7.0.0.dylib create mode 120000 openvdb/lib/libopenvdb.7.0.dylib create mode 120000 openvdb/lib/libopenvdb.dylib create mode 100644 openvdb/math/BBox.h create mode 100644 openvdb/math/ConjGradient.h create mode 100644 openvdb/math/Coord.h create mode 100644 openvdb/math/DDA.h create mode 100644 openvdb/math/FiniteDifference.h create mode 100644 openvdb/math/LegacyFrustum.h create mode 100644 openvdb/math/Maps.cc create mode 100644 openvdb/math/Maps.h create mode 100644 openvdb/math/Mat.h create mode 100644 openvdb/math/Mat3.h create mode 100644 openvdb/math/Mat4.h create mode 100644 openvdb/math/Math.h create mode 100644 openvdb/math/Operators.h create mode 100644 openvdb/math/Proximity.cc create mode 100644 openvdb/math/Proximity.h create mode 100644 openvdb/math/QuantizedUnitVec.cc create mode 100644 openvdb/math/QuantizedUnitVec.h create mode 100644 openvdb/math/Quat.h create mode 100644 openvdb/math/Ray.h create mode 100644 openvdb/math/Stats.h create mode 100644 openvdb/math/Stencils.h create mode 100644 openvdb/math/Transform.cc create mode 100644 openvdb/math/Transform.h create mode 100644 openvdb/math/Tuple.h create mode 100644 openvdb/math/Vec2.h create mode 100644 openvdb/math/Vec3.h create mode 100644 openvdb/math/Vec4.h create mode 100644 openvdb/openvdb.cc create mode 100644 openvdb/openvdb.h create mode 100644 openvdb/points/AttributeArray.cc create mode 100644 openvdb/points/AttributeArray.h create mode 100644 openvdb/points/AttributeArrayString.cc create mode 100644 openvdb/points/AttributeArrayString.h create mode 100644 openvdb/points/AttributeGroup.cc create mode 100644 openvdb/points/AttributeGroup.h create mode 100644 openvdb/points/AttributeSet.cc create mode 100644 openvdb/points/AttributeSet.h create mode 100644 openvdb/points/IndexFilter.h create mode 100644 openvdb/points/IndexIterator.h create mode 100644 openvdb/points/PointAdvect.h create mode 100644 openvdb/points/PointAttribute.h create mode 100644 openvdb/points/PointConversion.h create mode 100644 openvdb/points/PointCount.h create mode 100644 openvdb/points/PointDataGrid.h create mode 100644 openvdb/points/PointDelete.h create mode 100644 openvdb/points/PointGroup.h create mode 100644 openvdb/points/PointMask.h create mode 100644 openvdb/points/PointMove.h create mode 100644 openvdb/points/PointSample.h create mode 100644 openvdb/points/PointScatter.h create mode 100644 openvdb/points/StreamCompression.cc create mode 100644 openvdb/points/StreamCompression.h create mode 100644 openvdb/points/points.cc create mode 100644 openvdb/python/CMakeLists.txt create mode 100644 openvdb/python/pyAccessor.h create mode 100644 openvdb/python/pyFloatGrid.cc create mode 100644 openvdb/python/pyGrid.h create mode 100644 openvdb/python/pyIntGrid.cc create mode 100644 openvdb/python/pyMetadata.cc create mode 100644 openvdb/python/pyOpenVDBModule.cc create mode 100644 openvdb/python/pyPointGrid.cc create mode 100644 openvdb/python/pyTransform.cc create mode 100644 openvdb/python/pyVec3Grid.cc create mode 100644 openvdb/python/pyopenvdb.h create mode 100644 openvdb/python/pyutil.h create mode 100644 openvdb/python/test/TestOpenVDB.py create mode 100644 openvdb/tools/ChangeBackground.h create mode 100644 openvdb/tools/Clip.h create mode 100644 openvdb/tools/Composite.h create mode 100644 openvdb/tools/Dense.h create mode 100644 openvdb/tools/DenseSparseTools.h create mode 100644 openvdb/tools/Diagnostics.h create mode 100644 openvdb/tools/Filter.h create mode 100644 openvdb/tools/FindActiveValues.h create mode 100644 openvdb/tools/GridOperators.h create mode 100644 openvdb/tools/GridTransformer.h create mode 100644 openvdb/tools/Interpolation.h create mode 100644 openvdb/tools/LevelSetAdvect.h create mode 100644 openvdb/tools/LevelSetFilter.h create mode 100644 openvdb/tools/LevelSetFracture.h create mode 100644 openvdb/tools/LevelSetMeasure.h create mode 100644 openvdb/tools/LevelSetMorph.h create mode 100644 openvdb/tools/LevelSetPlatonic.h create mode 100644 openvdb/tools/LevelSetRebuild.h create mode 100644 openvdb/tools/LevelSetSphere.h create mode 100644 openvdb/tools/LevelSetTracker.h create mode 100644 openvdb/tools/LevelSetUtil.h create mode 100644 openvdb/tools/Mask.h create mode 100644 openvdb/tools/MeshToVolume.h create mode 100644 openvdb/tools/Morphology.h create mode 100644 openvdb/tools/MultiResGrid.h create mode 100644 openvdb/tools/ParticleAtlas.h create mode 100644 openvdb/tools/ParticlesToLevelSet.h create mode 100644 openvdb/tools/PointAdvect.h create mode 100644 openvdb/tools/PointIndexGrid.h create mode 100644 openvdb/tools/PointPartitioner.h create mode 100644 openvdb/tools/PointScatter.h create mode 100644 openvdb/tools/PointsToMask.h create mode 100644 openvdb/tools/PoissonSolver.h create mode 100644 openvdb/tools/PotentialFlow.h create mode 100644 openvdb/tools/Prune.h create mode 100644 openvdb/tools/RayIntersector.h create mode 100644 openvdb/tools/RayTracer.h create mode 100644 openvdb/tools/SignedFloodFill.h create mode 100644 openvdb/tools/Statistics.h create mode 100644 openvdb/tools/TopologyToLevelSet.h create mode 100644 openvdb/tools/ValueTransformer.h create mode 100644 openvdb/tools/VectorTransformer.h create mode 100644 openvdb/tools/VelocityFields.h create mode 100644 openvdb/tools/VolumeAdvect.h create mode 100644 openvdb/tools/VolumeToMesh.h create mode 100644 openvdb/tools/VolumeToSpheres.h create mode 100644 openvdb/tree/InternalNode.h create mode 100644 openvdb/tree/Iterator.h create mode 100644 openvdb/tree/LeafBuffer.h create mode 100644 openvdb/tree/LeafManager.h create mode 100644 openvdb/tree/LeafNode.h create mode 100644 openvdb/tree/LeafNodeBool.h create mode 100644 openvdb/tree/LeafNodeMask.h create mode 100644 openvdb/tree/NodeManager.h create mode 100644 openvdb/tree/NodeUnion.h create mode 100644 openvdb/tree/RootNode.h create mode 100644 openvdb/tree/Tree.h create mode 100644 openvdb/tree/TreeIterator.h create mode 100644 openvdb/tree/ValueAccessor.h create mode 100644 openvdb/unittest/CMakeLists.txt create mode 100644 openvdb/unittest/TestAttributeArray.cc create mode 100644 openvdb/unittest/TestAttributeArrayString.cc create mode 100644 openvdb/unittest/TestAttributeGroup.cc create mode 100644 openvdb/unittest/TestAttributeSet.cc create mode 100644 openvdb/unittest/TestBBox.cc create mode 100644 openvdb/unittest/TestClip.cc create mode 100644 openvdb/unittest/TestConjGradient.cc create mode 100644 openvdb/unittest/TestCoord.cc create mode 100644 openvdb/unittest/TestCpt.cc create mode 100644 openvdb/unittest/TestCurl.cc create mode 100644 openvdb/unittest/TestDelayedLoadMetadata.cc create mode 100644 openvdb/unittest/TestDense.cc create mode 100644 openvdb/unittest/TestDenseSparseTools.cc create mode 100644 openvdb/unittest/TestDiagnostics.cc create mode 100644 openvdb/unittest/TestDivergence.cc create mode 100644 openvdb/unittest/TestDoubleMetadata.cc create mode 100644 openvdb/unittest/TestExceptions.cc create mode 100644 openvdb/unittest/TestFile.cc create mode 100644 openvdb/unittest/TestFindActiveValues.cc create mode 100644 openvdb/unittest/TestFloatMetadata.cc create mode 100644 openvdb/unittest/TestGradient.cc create mode 100644 openvdb/unittest/TestGrid.cc create mode 100644 openvdb/unittest/TestGridBbox.cc create mode 100644 openvdb/unittest/TestGridDescriptor.cc create mode 100644 openvdb/unittest/TestGridIO.cc create mode 100644 openvdb/unittest/TestGridTransformer.cc create mode 100644 openvdb/unittest/TestIndexFilter.cc create mode 100644 openvdb/unittest/TestIndexIterator.cc create mode 100644 openvdb/unittest/TestInit.cc create mode 100644 openvdb/unittest/TestInt32Metadata.cc create mode 100644 openvdb/unittest/TestInt64Metadata.cc create mode 100644 openvdb/unittest/TestInternalOrigin.cc create mode 100644 openvdb/unittest/TestLaplacian.cc create mode 100644 openvdb/unittest/TestLeaf.cc create mode 100644 openvdb/unittest/TestLeafBool.cc create mode 100644 openvdb/unittest/TestLeafIO.cc create mode 100644 openvdb/unittest/TestLeafManager.cc create mode 100644 openvdb/unittest/TestLeafMask.cc create mode 100644 openvdb/unittest/TestLeafOrigin.cc create mode 100644 openvdb/unittest/TestLevelSetRayIntersector.cc create mode 100644 openvdb/unittest/TestLevelSetUtil.cc create mode 100644 openvdb/unittest/TestLinearInterp.cc create mode 100644 openvdb/unittest/TestMaps.cc create mode 100644 openvdb/unittest/TestMat4Metadata.cc create mode 100644 openvdb/unittest/TestMath.cc create mode 100644 openvdb/unittest/TestMeanCurvature.cc create mode 100644 openvdb/unittest/TestMeshToVolume.cc create mode 100644 openvdb/unittest/TestMetaMap.cc create mode 100644 openvdb/unittest/TestMetadata.cc create mode 100644 openvdb/unittest/TestMetadataIO.cc create mode 100644 openvdb/unittest/TestMultiResGrid.cc create mode 100644 openvdb/unittest/TestName.cc create mode 100644 openvdb/unittest/TestNodeIterator.cc create mode 100644 openvdb/unittest/TestNodeManager.cc create mode 100644 openvdb/unittest/TestNodeMask.cc create mode 100644 openvdb/unittest/TestParticleAtlas.cc create mode 100644 openvdb/unittest/TestParticlesToLevelSet.cc create mode 100644 openvdb/unittest/TestPointAdvect.cc create mode 100644 openvdb/unittest/TestPointAttribute.cc create mode 100644 openvdb/unittest/TestPointConversion.cc create mode 100644 openvdb/unittest/TestPointCount.cc create mode 100644 openvdb/unittest/TestPointDataLeaf.cc create mode 100644 openvdb/unittest/TestPointDelete.cc create mode 100644 openvdb/unittest/TestPointGroup.cc create mode 100644 openvdb/unittest/TestPointIndexGrid.cc create mode 100644 openvdb/unittest/TestPointMask.cc create mode 100644 openvdb/unittest/TestPointMove.cc create mode 100644 openvdb/unittest/TestPointPartitioner.cc create mode 100644 openvdb/unittest/TestPointSample.cc create mode 100644 openvdb/unittest/TestPointScatter.cc create mode 100644 openvdb/unittest/TestPointsToMask.cc create mode 100644 openvdb/unittest/TestPoissonSolver.cc create mode 100644 openvdb/unittest/TestPotentialFlow.cc create mode 100644 openvdb/unittest/TestPrePostAPI.cc create mode 100644 openvdb/unittest/TestQuadraticInterp.cc create mode 100644 openvdb/unittest/TestQuantizedUnitVec.cc create mode 100644 openvdb/unittest/TestQuat.cc create mode 100644 openvdb/unittest/TestRay.cc create mode 100644 openvdb/unittest/TestStats.cc create mode 100644 openvdb/unittest/TestStream.cc create mode 100644 openvdb/unittest/TestStreamCompression.cc create mode 100644 openvdb/unittest/TestStringMetadata.cc create mode 100644 openvdb/unittest/TestTools.cc create mode 100644 openvdb/unittest/TestTopologyToLevelSet.cc create mode 100644 openvdb/unittest/TestTransform.cc create mode 100644 openvdb/unittest/TestTree.cc create mode 100644 openvdb/unittest/TestTreeCombine.cc create mode 100644 openvdb/unittest/TestTreeGetSetValues.cc create mode 100644 openvdb/unittest/TestTreeIterators.cc create mode 100644 openvdb/unittest/TestTreeVisitor.cc create mode 100644 openvdb/unittest/TestTypes.cc create mode 100644 openvdb/unittest/TestUtil.cc create mode 100644 openvdb/unittest/TestValueAccessor.cc create mode 100644 openvdb/unittest/TestVec2Metadata.cc create mode 100644 openvdb/unittest/TestVec3Metadata.cc create mode 100644 openvdb/unittest/TestVolumeRayIntersector.cc create mode 100644 openvdb/unittest/TestVolumeToMesh.cc create mode 100644 openvdb/unittest/TestVolumeToSpheres.cc create mode 100644 openvdb/unittest/main.cc create mode 100644 openvdb/unittest/util.h create mode 100644 openvdb/util/CpuTimer.h create mode 100644 openvdb/util/Formats.cc create mode 100644 openvdb/util/Formats.h create mode 100644 openvdb/util/MapsUtil.h create mode 100644 openvdb/util/Name.h create mode 100644 openvdb/util/NodeMasks.h create mode 100644 openvdb/util/NullInterrupter.h create mode 100644 openvdb/util/PagedArray.h create mode 100644 openvdb/util/Util.cc create mode 100644 openvdb/util/Util.h create mode 100644 openvdb/util/logging.h create mode 100644 openvdb/version.h create mode 100644 openvdb/viewer/Camera.cc create mode 100644 openvdb/viewer/Camera.h create mode 100644 openvdb/viewer/ClipBox.cc create mode 100644 openvdb/viewer/ClipBox.h create mode 100644 openvdb/viewer/Font.cc create mode 100644 openvdb/viewer/Font.h create mode 100644 openvdb/viewer/RenderModules.cc create mode 100644 openvdb/viewer/RenderModules.h create mode 100644 openvdb/viewer/Viewer.cc create mode 100644 openvdb/viewer/Viewer.h create mode 100644 scenes/bacilli_on_agar.py diff --git a/CMakeLists.txt b/CMakeLists.txt index c20803a2..8324169b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,8 +34,9 @@ endif() if(APPLE) if(NOT CMAKE_PREFIX_PATH) set(CMAKE_PREFIX_PATH "/usr/local/opt/qt5/") # mac/homebrew (version independent) - #set(CMAKE_PREFIX_PATH "/home/myname/qt/5.5/clang_64") # other... - #set(CMAKE_PREFIX_PATH "/Users/Cesar/Qt/5.15.0/clang_64/") + # set(CMAKE_PREFIX_PATH "/User/Cesar/anaconda3/") # Anaconda QT + # set(CMAKE_PREFIX_PATH "/home/myname/qt/5.5/clang_64") # other... + # set(CMAKE_PREFIX_PATH "/Users/Cesar/Qt/5.15.0/clang_64/") endif() endif() @@ -449,38 +450,38 @@ endif(DOXYGEN_FOUND) set(PYTHON_INCLUDE_DIRS "/Users/Cesar/anaconda3/include/python3.7m/") set(PYTHON_LIBRARIES "/Users/Cesar/anaconda3/lib/libpython3.7m.dylib") # auto block -if(WIN32) - # convenience override for windows, typically the auto find doesnt work, so fall back to WIN_PYTHON_PATH - find_package(PythonLibs ${PYTHON_VER_ID} QUIET) - if(NOT PYTHONLIBS_FOUND) - set(PYTHON_INCLUDE_DIRS "${WIN_PYTHON_PATH}/include") - set(PYTHON_LIBRARIES "${WIN_PYTHON_PATH}/libs/python${PYTHON_VER_ID}.lib") - endif() -else() - # try to configure from python itself first - execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('INCLUDEPY'))" OUTPUT_VARIABLE PYTHON_INCLUDE_DIRS) - execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('LIBDIR'))" OUTPUT_VARIABLE PYTHON_LIB1) - if(APPLE) - # LDLIBRARY doesnt seem to work for Macs... - set(PYTHON_LIB2 "../Python") - else() - execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('LDLIBRARY'))" OUTPUT_VARIABLE PYTHON_LIB2) - endif() - set(PYTHON_LIBRARIES "${PYTHON_LIB1}/${PYTHON_LIB2}") - if(EXISTS "${PYTHON_INCLUDE_DIRS}" AND EXISTS "${PYTHON_LIBRARIES}") - message("Found python configuration") - else() - # otherwise, fall back to cmake find - if(NOT PYTHON_VERSION) - find_package(PythonLibs REQUIRED) # use any - else() - find_package(PythonLibs ${PYTHON_VER_ID} EXACT QUIET) # fixed version - endif() - if(NOT PYTHONLIBS_FOUND) - message(FATAL_ERROR "No python found, please manually set PYTHON_VERSION for cmake, or PYTHON_ paths in CMakeLists.txt") - endif() - endif() -endif() +# if(WIN32) +# # convenience override for windows, typically the auto find doesnt work, so fall back to WIN_PYTHON_PATH +# find_package(PythonLibs ${PYTHON_VER_ID} QUIET) +# if(NOT PYTHONLIBS_FOUND) +# set(PYTHON_INCLUDE_DIRS "${WIN_PYTHON_PATH}/include") +# set(PYTHON_LIBRARIES "${WIN_PYTHON_PATH}/libs/python${PYTHON_VER_ID}.lib") +# endif() +# else() +# # try to configure from python itself first +# execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('INCLUDEPY'))" OUTPUT_VARIABLE PYTHON_INCLUDE_DIRS) +# execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('LIBDIR'))" OUTPUT_VARIABLE PYTHON_LIB1) +# if(APPLE) +# # LDLIBRARY doesnt seem to work for Macs... +# set(PYTHON_LIB2 "../Python") +# else() +# execute_process(COMMAND python${PYTHON_VER_ID} -c "import sys,sysconfig; sys.stdout.write(sysconfig.get_config_var('LDLIBRARY'))" OUTPUT_VARIABLE PYTHON_LIB2) +# endif() +# set(PYTHON_LIBRARIES "${PYTHON_LIB1}/${PYTHON_LIB2}") +# if(EXISTS "${PYTHON_INCLUDE_DIRS}" AND EXISTS "${PYTHON_LIBRARIES}") +# message("Found python configuration") +# else() +# # otherwise, fall back to cmake find +# if(NOT PYTHON_VERSION) +# find_package(PythonLibs REQUIRED) # use any +# else() +# find_package(PythonLibs ${PYTHON_VER_ID} EXACT QUIET) # fixed version +# endif() +# if(NOT PYTHONLIBS_FOUND) +# message(FATAL_ERROR "No python found, please manually set PYTHON_VERSION for cmake, or PYTHON_ paths in CMakeLists.txt") +# endif() +# endif() +# endif() # end auto block, python config done list(APPEND INCLUDE_PATHS ${PYTHON_INCLUDE_DIRS}) list(APPEND F_LIBS ${PYTHON_LIBRARIES}) diff --git a/openvdb/CMakeCache.txt b/openvdb/CMakeCache.txt new file mode 100644 index 00000000..443e9631 --- /dev/null +++ b/openvdb/CMakeCache.txt @@ -0,0 +1,380 @@ +# This is the CMakeCache file. +# For build in directory: /Users/Cesar/Repositories/mantaflow/openvdb +# It was generated by CMake: /Applications/CMake.app/Contents/bin/cmake +# You can edit this file to change values found and used by cmake. +# If you do not want to change any of the values, simply exit the editor. +# If you do want to change a value, simply edit, save, and exit the editor. +# The syntax for the file is as follows: +# KEY:TYPE=VALUE +# KEY is the name of a variable in the cache. +# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. +# VALUE is the current value for the KEY. + +######################## +# EXTERNAL cache entries +######################## + +//Path to a program. +CMAKE_ADDR2LINE:FILEPATH=CMAKE_ADDR2LINE-NOTFOUND + +//Path to a program. +CMAKE_AR:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar + +//Choose the type of build, options are: None Debug Release RelWithDebInfo +// MinSizeRel ... +CMAKE_BUILD_TYPE:STRING= + +//Enable/Disable color output during build. +CMAKE_COLOR_MAKEFILE:BOOL=ON + +//CXX compiler +CMAKE_CXX_COMPILER:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ + +//Flags used by the CXX compiler during all build types. +CMAKE_CXX_FLAGS:STRING= + +//Flags used by the CXX compiler during DEBUG builds. +CMAKE_CXX_FLAGS_DEBUG:STRING=-g + +//Flags used by the CXX compiler during MINSIZEREL builds. +CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG + +//Flags used by the CXX compiler during RELEASE builds. +CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG + +//Flags used by the CXX compiler during RELWITHDEBINFO builds. +CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG + +//C compiler +CMAKE_C_COMPILER:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc + +//Flags used by the C compiler during all build types. +CMAKE_C_FLAGS:STRING= + +//Flags used by the C compiler during DEBUG builds. +CMAKE_C_FLAGS_DEBUG:STRING=-g + +//Flags used by the C compiler during MINSIZEREL builds. +CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG + +//Flags used by the C compiler during RELEASE builds. +CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG + +//Flags used by the C compiler during RELWITHDEBINFO builds. +CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG + +//Path to a program. +CMAKE_DLLTOOL:FILEPATH=CMAKE_DLLTOOL-NOTFOUND + +//Executable file format +CMAKE_EXECUTABLE_FORMAT:STRING=MACHO + +//Flags used by the linker during all build types. +CMAKE_EXE_LINKER_FLAGS:STRING= + +//Flags used by the linker during DEBUG builds. +CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during MINSIZEREL builds. +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during RELEASE builds. +CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during RELWITHDEBINFO builds. +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Enable/Disable output of compile commands during generation. +CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=OFF + +//Path to a program. +CMAKE_INSTALL_NAME_TOOL:FILEPATH=/usr/bin/install_name_tool + +//Install path prefix, prepended onto install directories. +CMAKE_INSTALL_PREFIX:PATH=/usr/local + +//Path to a program. +CMAKE_LINKER:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld + +//Path to a program. +CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/make + +//Flags used by the linker during the creation of modules during +// all build types. +CMAKE_MODULE_LINKER_FLAGS:STRING= + +//Flags used by the linker during the creation of modules during +// DEBUG builds. +CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during the creation of modules during +// MINSIZEREL builds. +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during the creation of modules during +// RELEASE builds. +CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during the creation of modules during +// RELWITHDEBINFO builds. +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_NM:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/nm + +//Path to a program. +CMAKE_OBJCOPY:FILEPATH=CMAKE_OBJCOPY-NOTFOUND + +//Path to a program. +CMAKE_OBJDUMP:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/objdump + +//Build architectures for OSX +CMAKE_OSX_ARCHITECTURES:STRING= + +//Minimum OS X version to target for deployment (at runtime); newer +// APIs weak linked. Set to empty string for default value. +CMAKE_OSX_DEPLOYMENT_TARGET:STRING= + +//The product will be built against the headers and libraries located +// inside the indicated SDK. +CMAKE_OSX_SYSROOT:PATH=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk + +//Value Computed by CMake +CMAKE_PROJECT_DESCRIPTION:STATIC= + +//Value Computed by CMake +CMAKE_PROJECT_HOMEPAGE_URL:STATIC= + +//Value Computed by CMake +CMAKE_PROJECT_NAME:STATIC=OpenVDBCore + +//Path to a program. +CMAKE_RANLIB:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib + +//Path to a program. +CMAKE_READELF:FILEPATH=CMAKE_READELF-NOTFOUND + +//Flags used by the linker during the creation of shared libraries +// during all build types. +CMAKE_SHARED_LINKER_FLAGS:STRING= + +//Flags used by the linker during the creation of shared libraries +// during DEBUG builds. +CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during the creation of shared libraries +// during MINSIZEREL builds. +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during the creation of shared libraries +// during RELEASE builds. +CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during the creation of shared libraries +// during RELWITHDEBINFO builds. +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//If set, runtime paths are not added when installing shared libraries, +// but are added when building. +CMAKE_SKIP_INSTALL_RPATH:BOOL=OFF + +//If set, runtime paths are not added when using shared libraries. +CMAKE_SKIP_RPATH:BOOL=OFF + +//Flags used by the linker during the creation of static libraries +// during all build types. +CMAKE_STATIC_LINKER_FLAGS:STRING= + +//Flags used by the linker during the creation of static libraries +// during DEBUG builds. +CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during the creation of static libraries +// during MINSIZEREL builds. +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during the creation of static libraries +// during RELEASE builds. +CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during the creation of static libraries +// during RELWITHDEBINFO builds. +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_STRIP:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip + +//If this value is on, makefiles will be generated without the +// .SILENT directive, and all commands will be echoed to the console +// during the make. This is useful for debugging only. With Visual +// Studio IDE projects all commands are done without /nologo. +CMAKE_VERBOSE_MAKEFILE:BOOL=OFF + +//The directory containing a CMake configuration file for IlmBase. +IlmBase_DIR:PATH=/usr/local/lib/cmake/IlmBase + +//Build dynamically linked version of the core library. +OPENVDB_CORE_SHARED:BOOL=ON + +//Build statically linked version of the core library. +OPENVDB_CORE_STATIC:BOOL=ON + +//The base name of the built shared openvdb library. Prefixed by +// "lib" on UNIX platforms. +OPENVDB_SHARED_LIBRARY_NAME:STRING=openvdb + +//The base name of the built static openvdb library. Prefixed by +// "lib". +OPENVDB_STATIC_LIBRARY_NAME:STRING=openvdb + +//Value Computed by CMake +OpenVDBCore_BINARY_DIR:STATIC=/Users/Cesar/Repositories/mantaflow/openvdb + +//Value Computed by CMake +OpenVDBCore_SOURCE_DIR:STATIC=/Users/Cesar/Repositories/mantaflow/openvdb + + +######################## +# INTERNAL cache entries +######################## + +//ADVANCED property for variable: CMAKE_ADDR2LINE +CMAKE_ADDR2LINE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_AR +CMAKE_AR-ADVANCED:INTERNAL=1 +//This is the directory where this CMakeCache.txt was created +CMAKE_CACHEFILE_DIR:INTERNAL=/Users/Cesar/Repositories/mantaflow/openvdb +//Major version of cmake used to create the current loaded cache +CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 +//Minor version of cmake used to create the current loaded cache +CMAKE_CACHE_MINOR_VERSION:INTERNAL=18 +//Patch version of cmake used to create the current loaded cache +CMAKE_CACHE_PATCH_VERSION:INTERNAL=0 +//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE +CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1 +//Path to CMake executable. +CMAKE_COMMAND:INTERNAL=/Applications/CMake.app/Contents/bin/cmake +//Path to cpack program executable. +CMAKE_CPACK_COMMAND:INTERNAL=/Applications/CMake.app/Contents/bin/cpack +//Path to ctest program executable. +CMAKE_CTEST_COMMAND:INTERNAL=/Applications/CMake.app/Contents/bin/ctest +//ADVANCED property for variable: CMAKE_CXX_COMPILER +CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS +CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG +CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL +CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE +CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO +CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_COMPILER +CMAKE_C_COMPILER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS +CMAKE_C_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG +CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL +CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE +CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO +CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_DLLTOOL +CMAKE_DLLTOOL-ADVANCED:INTERNAL=1 +//Path to cache edit program executable. +CMAKE_EDIT_COMMAND:INTERNAL=/Applications/CMake.app/Contents/bin/cmake-gui +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS +CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG +CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE +CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS +CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1 +//Name of external makefile project generator. +CMAKE_EXTRA_GENERATOR:INTERNAL= +//Name of generator. +CMAKE_GENERATOR:INTERNAL=Unix Makefiles +//Generator instance identifier. +CMAKE_GENERATOR_INSTANCE:INTERNAL= +//Name of generator platform. +CMAKE_GENERATOR_PLATFORM:INTERNAL= +//Name of generator toolset. +CMAKE_GENERATOR_TOOLSET:INTERNAL= +//Source directory with the top level CMakeLists.txt file for this +// project +CMAKE_HOME_DIRECTORY:INTERNAL=/Users/Cesar/Repositories/mantaflow/openvdb +//ADVANCED property for variable: CMAKE_INSTALL_NAME_TOOL +CMAKE_INSTALL_NAME_TOOL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_LINKER +CMAKE_LINKER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MAKE_PROGRAM +CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS +CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG +CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE +CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_NM +CMAKE_NM-ADVANCED:INTERNAL=1 +//number of local generators +CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1 +//ADVANCED property for variable: CMAKE_OBJCOPY +CMAKE_OBJCOPY-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_OBJDUMP +CMAKE_OBJDUMP-ADVANCED:INTERNAL=1 +//Platform information initialized +CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RANLIB +CMAKE_RANLIB-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_READELF +CMAKE_READELF-ADVANCED:INTERNAL=1 +//Path to CMake installation. +CMAKE_ROOT:INTERNAL=/Applications/CMake.app/Contents/share/cmake-3.18 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS +CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG +CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE +CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH +CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_RPATH +CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS +CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG +CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE +CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STRIP +CMAKE_STRIP-ADVANCED:INTERNAL=1 +//uname command +CMAKE_UNAME:INTERNAL=/usr/bin/uname +//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE +CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: OPENVDB_SHARED_LIBRARY_NAME +OPENVDB_SHARED_LIBRARY_NAME-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: OPENVDB_STATIC_LIBRARY_NAME +OPENVDB_STATIC_LIBRARY_NAME-ADVANCED:INTERNAL=1 + diff --git a/openvdb/CMakeFiles/3.18.0-rc2/CMakeCCompiler.cmake b/openvdb/CMakeFiles/3.18.0-rc2/CMakeCCompiler.cmake new file mode 100644 index 00000000..a13b6acf --- /dev/null +++ b/openvdb/CMakeFiles/3.18.0-rc2/CMakeCCompiler.cmake @@ -0,0 +1,77 @@ +set(CMAKE_C_COMPILER "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc") +set(CMAKE_C_COMPILER_ARG1 "") +set(CMAKE_C_COMPILER_ID "AppleClang") +set(CMAKE_C_COMPILER_VERSION "11.0.3.11030032") +set(CMAKE_C_COMPILER_VERSION_INTERNAL "") +set(CMAKE_C_COMPILER_WRAPPER "") +set(CMAKE_C_STANDARD_COMPUTED_DEFAULT "11") +set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert") +set(CMAKE_C90_COMPILE_FEATURES "c_std_90;c_function_prototypes") +set(CMAKE_C99_COMPILE_FEATURES "c_std_99;c_restrict;c_variadic_macros") +set(CMAKE_C11_COMPILE_FEATURES "c_std_11;c_static_assert") + +set(CMAKE_C_PLATFORM_ID "Darwin") +set(CMAKE_C_SIMULATE_ID "") +set(CMAKE_C_COMPILER_FRONTEND_VARIANT "") +set(CMAKE_C_SIMULATE_VERSION "") + + + + +set(CMAKE_AR "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar") +set(CMAKE_C_COMPILER_AR "") +set(CMAKE_RANLIB "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib") +set(CMAKE_C_COMPILER_RANLIB "") +set(CMAKE_LINKER "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld") +set(CMAKE_MT "") +set(CMAKE_COMPILER_IS_GNUCC ) +set(CMAKE_C_COMPILER_LOADED 1) +set(CMAKE_C_COMPILER_WORKS TRUE) +set(CMAKE_C_ABI_COMPILED TRUE) +set(CMAKE_COMPILER_IS_MINGW ) +set(CMAKE_COMPILER_IS_CYGWIN ) +if(CMAKE_COMPILER_IS_CYGWIN) + set(CYGWIN 1) + set(UNIX 1) +endif() + +set(CMAKE_C_COMPILER_ENV_VAR "CC") + +if(CMAKE_COMPILER_IS_MINGW) + set(MINGW 1) +endif() +set(CMAKE_C_COMPILER_ID_RUN 1) +set(CMAKE_C_SOURCE_FILE_EXTENSIONS c;m) +set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC) +set(CMAKE_C_LINKER_PREFERENCE 10) + +# Save compiler ABI information. +set(CMAKE_C_SIZEOF_DATA_PTR "8") +set(CMAKE_C_COMPILER_ABI "") +set(CMAKE_C_LIBRARY_ARCHITECTURE "") + +if(CMAKE_C_SIZEOF_DATA_PTR) + set(CMAKE_SIZEOF_VOID_P "${CMAKE_C_SIZEOF_DATA_PTR}") +endif() + +if(CMAKE_C_COMPILER_ABI) + set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_C_COMPILER_ABI}") +endif() + +if(CMAKE_C_LIBRARY_ARCHITECTURE) + set(CMAKE_LIBRARY_ARCHITECTURE "") +endif() + +set(CMAKE_C_CL_SHOWINCLUDES_PREFIX "") +if(CMAKE_C_CL_SHOWINCLUDES_PREFIX) + set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_C_CL_SHOWINCLUDES_PREFIX}") +endif() + + + + + +set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include;/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include") +set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "") +set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib") +set(CMAKE_C_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks") diff --git a/openvdb/CMakeFiles/3.18.0-rc2/CMakeCXXCompiler.cmake b/openvdb/CMakeFiles/3.18.0-rc2/CMakeCXXCompiler.cmake new file mode 100644 index 00000000..9624209e --- /dev/null +++ b/openvdb/CMakeFiles/3.18.0-rc2/CMakeCXXCompiler.cmake @@ -0,0 +1,89 @@ +set(CMAKE_CXX_COMPILER "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++") +set(CMAKE_CXX_COMPILER_ARG1 "") +set(CMAKE_CXX_COMPILER_ID "AppleClang") +set(CMAKE_CXX_COMPILER_VERSION "11.0.3.11030032") +set(CMAKE_CXX_COMPILER_VERSION_INTERNAL "") +set(CMAKE_CXX_COMPILER_WRAPPER "") +set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "98") +set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17;cxx_std_20") +set(CMAKE_CXX98_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters") +set(CMAKE_CXX11_COMPILE_FEATURES "cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") +set(CMAKE_CXX14_COMPILE_FEATURES "cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") +set(CMAKE_CXX17_COMPILE_FEATURES "cxx_std_17") +set(CMAKE_CXX20_COMPILE_FEATURES "cxx_std_20") + +set(CMAKE_CXX_PLATFORM_ID "Darwin") +set(CMAKE_CXX_SIMULATE_ID "") +set(CMAKE_CXX_COMPILER_FRONTEND_VARIANT "") +set(CMAKE_CXX_SIMULATE_VERSION "") + + + + +set(CMAKE_AR "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar") +set(CMAKE_CXX_COMPILER_AR "") +set(CMAKE_RANLIB "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib") +set(CMAKE_CXX_COMPILER_RANLIB "") +set(CMAKE_LINKER "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld") +set(CMAKE_MT "") +set(CMAKE_COMPILER_IS_GNUCXX ) +set(CMAKE_CXX_COMPILER_LOADED 1) +set(CMAKE_CXX_COMPILER_WORKS TRUE) +set(CMAKE_CXX_ABI_COMPILED TRUE) +set(CMAKE_COMPILER_IS_MINGW ) +set(CMAKE_COMPILER_IS_CYGWIN ) +if(CMAKE_COMPILER_IS_CYGWIN) + set(CYGWIN 1) + set(UNIX 1) +endif() + +set(CMAKE_CXX_COMPILER_ENV_VAR "CXX") + +if(CMAKE_COMPILER_IS_MINGW) + set(MINGW 1) +endif() +set(CMAKE_CXX_COMPILER_ID_RUN 1) +set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;CPP) +set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC) + +foreach (lang C OBJC OBJCXX) + if (CMAKE_${lang}_COMPILER_ID_RUN) + foreach(extension IN LISTS CMAKE_${lang}_SOURCE_FILE_EXTENSIONS) + list(REMOVE_ITEM CMAKE_CXX_SOURCE_FILE_EXTENSIONS ${extension}) + endforeach() + endif() +endforeach() + +set(CMAKE_CXX_LINKER_PREFERENCE 30) +set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1) + +# Save compiler ABI information. +set(CMAKE_CXX_SIZEOF_DATA_PTR "8") +set(CMAKE_CXX_COMPILER_ABI "") +set(CMAKE_CXX_LIBRARY_ARCHITECTURE "") + +if(CMAKE_CXX_SIZEOF_DATA_PTR) + set(CMAKE_SIZEOF_VOID_P "${CMAKE_CXX_SIZEOF_DATA_PTR}") +endif() + +if(CMAKE_CXX_COMPILER_ABI) + set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_CXX_COMPILER_ABI}") +endif() + +if(CMAKE_CXX_LIBRARY_ARCHITECTURE) + set(CMAKE_LIBRARY_ARCHITECTURE "") +endif() + +set(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX "") +if(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX) + set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_CXX_CL_SHOWINCLUDES_PREFIX}") +endif() + + + + + +set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1;/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include;/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include") +set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "c++") +set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib") +set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks") diff --git a/openvdb/CMakeFiles/3.18.0-rc2/CMakeSystem.cmake b/openvdb/CMakeFiles/3.18.0-rc2/CMakeSystem.cmake new file mode 100644 index 00000000..f4feaf16 --- /dev/null +++ b/openvdb/CMakeFiles/3.18.0-rc2/CMakeSystem.cmake @@ -0,0 +1,15 @@ +set(CMAKE_HOST_SYSTEM "Darwin-19.6.0") +set(CMAKE_HOST_SYSTEM_NAME "Darwin") +set(CMAKE_HOST_SYSTEM_VERSION "19.6.0") +set(CMAKE_HOST_SYSTEM_PROCESSOR "x86_64") + + + +set(CMAKE_SYSTEM "Darwin-19.6.0") +set(CMAKE_SYSTEM_NAME "Darwin") +set(CMAKE_SYSTEM_VERSION "19.6.0") +set(CMAKE_SYSTEM_PROCESSOR "x86_64") + +set(CMAKE_CROSSCOMPILING "FALSE") + +set(CMAKE_SYSTEM_LOADED 1) diff --git a/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdC/CMakeCCompilerId.c b/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdC/CMakeCCompilerId.c new file mode 100644 index 00000000..d884b509 --- /dev/null +++ b/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdC/CMakeCCompilerId.c @@ -0,0 +1,671 @@ +#ifdef __cplusplus +# error "A C++ compiler has been selected for C." +#endif + +#if defined(__18CXX) +# define ID_VOID_MAIN +#endif +#if defined(__CLASSIC_C__) +/* cv-qualifiers did not exist in K&R C */ +# define const +# define volatile +#endif + + +/* Version number components: V=Version, R=Revision, P=Patch + Version date components: YYYY=Year, MM=Month, DD=Day */ + +#if defined(__INTEL_COMPILER) || defined(__ICC) +# define COMPILER_ID "Intel" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# if defined(__GNUC__) +# define SIMULATE_ID "GNU" +# endif + /* __INTEL_COMPILER = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100) +# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10) +# if defined(__INTEL_COMPILER_UPDATE) +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE) +# else +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10) +# endif +# if defined(__INTEL_COMPILER_BUILD_DATE) + /* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */ +# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE) +# endif +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif +# if defined(__GNUC__) +# define SIMULATE_VERSION_MAJOR DEC(__GNUC__) +# elif defined(__GNUG__) +# define SIMULATE_VERSION_MAJOR DEC(__GNUG__) +# endif +# if defined(__GNUC_MINOR__) +# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__) +# endif +# if defined(__GNUC_PATCHLEVEL__) +# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__) +# endif + +#elif defined(__PATHCC__) +# define COMPILER_ID "PathScale" +# define COMPILER_VERSION_MAJOR DEC(__PATHCC__) +# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__) +# if defined(__PATHCC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__) +# endif + +#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__) +# define COMPILER_ID "Embarcadero" +# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF) +# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF) +# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF) + +#elif defined(__BORLANDC__) +# define COMPILER_ID "Borland" + /* __BORLANDC__ = 0xVRR */ +# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8) +# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF) + +#elif defined(__WATCOMC__) && __WATCOMC__ < 1200 +# define COMPILER_ID "Watcom" + /* __WATCOMC__ = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__WATCOMC__) +# define COMPILER_ID "OpenWatcom" + /* __WATCOMC__ = VVRP + 1100 */ +# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__SUNPRO_C) +# define COMPILER_ID "SunPro" +# if __SUNPRO_C >= 0x5100 + /* __SUNPRO_C = 0xVRRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>12) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF) +# else + /* __SUNPRO_CC = 0xVRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>8) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF) +# endif + +#elif defined(__HP_cc) +# define COMPILER_ID "HP" + /* __HP_cc = VVRRPP */ +# define COMPILER_VERSION_MAJOR DEC(__HP_cc/10000) +# define COMPILER_VERSION_MINOR DEC(__HP_cc/100 % 100) +# define COMPILER_VERSION_PATCH DEC(__HP_cc % 100) + +#elif defined(__DECC) +# define COMPILER_ID "Compaq" + /* __DECC_VER = VVRRTPPPP */ +# define COMPILER_VERSION_MAJOR DEC(__DECC_VER/10000000) +# define COMPILER_VERSION_MINOR DEC(__DECC_VER/100000 % 100) +# define COMPILER_VERSION_PATCH DEC(__DECC_VER % 10000) + +#elif defined(__IBMC__) && defined(__COMPILER_VER__) +# define COMPILER_ID "zOS" + /* __IBMC__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10) + +#elif defined(__ibmxl__) && defined(__clang__) +# define COMPILER_ID "XLClang" +# define COMPILER_VERSION_MAJOR DEC(__ibmxl_version__) +# define COMPILER_VERSION_MINOR DEC(__ibmxl_release__) +# define COMPILER_VERSION_PATCH DEC(__ibmxl_modification__) +# define COMPILER_VERSION_TWEAK DEC(__ibmxl_ptf_fix_level__) + + +#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ >= 800 +# define COMPILER_ID "XL" + /* __IBMC__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10) + +#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ < 800 +# define COMPILER_ID "VisualAge" + /* __IBMC__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10) + +#elif defined(__PGI) +# define COMPILER_ID "PGI" +# define COMPILER_VERSION_MAJOR DEC(__PGIC__) +# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__) +# if defined(__PGIC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__) +# endif + +#elif defined(_CRAYC) +# define COMPILER_ID "Cray" +# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR) +# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR) + +#elif defined(__TI_COMPILER_VERSION__) +# define COMPILER_ID "TI" + /* __TI_COMPILER_VERSION__ = VVVRRRPPP */ +# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000) +# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000) +# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000) + +#elif defined(__FUJITSU) || defined(__FCC_VERSION) || defined(__fcc_version) +# define COMPILER_ID "Fujitsu" + +#elif defined(__ghs__) +# define COMPILER_ID "GHS" +/* __GHS_VERSION_NUMBER = VVVVRP */ +# ifdef __GHS_VERSION_NUMBER +# define COMPILER_VERSION_MAJOR DEC(__GHS_VERSION_NUMBER / 100) +# define COMPILER_VERSION_MINOR DEC(__GHS_VERSION_NUMBER / 10 % 10) +# define COMPILER_VERSION_PATCH DEC(__GHS_VERSION_NUMBER % 10) +# endif + +#elif defined(__TINYC__) +# define COMPILER_ID "TinyCC" + +#elif defined(__BCC__) +# define COMPILER_ID "Bruce" + +#elif defined(__SCO_VERSION__) +# define COMPILER_ID "SCO" + +#elif defined(__ARMCC_VERSION) && !defined(__clang__) +# define COMPILER_ID "ARMCC" +#if __ARMCC_VERSION >= 1000000 + /* __ARMCC_VERSION = VRRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#else + /* __ARMCC_VERSION = VRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#endif + + +#elif defined(__clang__) && defined(__apple_build_version__) +# define COMPILER_ID "AppleClang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif +# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__) + +#elif defined(__clang__) && defined(__ARMCOMPILER_VERSION) +# define COMPILER_ID "ARMClang" + # define COMPILER_VERSION_MAJOR DEC(__ARMCOMPILER_VERSION/1000000) + # define COMPILER_VERSION_MINOR DEC(__ARMCOMPILER_VERSION/10000 % 100) + # define COMPILER_VERSION_PATCH DEC(__ARMCOMPILER_VERSION % 10000) +# define COMPILER_VERSION_INTERNAL DEC(__ARMCOMPILER_VERSION) + +#elif defined(__clang__) +# define COMPILER_ID "Clang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif + +#elif defined(__GNUC__) +# define COMPILER_ID "GNU" +# define COMPILER_VERSION_MAJOR DEC(__GNUC__) +# if defined(__GNUC_MINOR__) +# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__) +# endif +# if defined(__GNUC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__) +# endif + +#elif defined(_MSC_VER) +# define COMPILER_ID "MSVC" + /* _MSC_VER = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100) +# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100) +# if defined(_MSC_FULL_VER) +# if _MSC_VER >= 1400 + /* _MSC_FULL_VER = VVRRPPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000) +# else + /* _MSC_FULL_VER = VVRRPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000) +# endif +# endif +# if defined(_MSC_BUILD) +# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD) +# endif + +#elif defined(__VISUALDSPVERSION__) || defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__) +# define COMPILER_ID "ADSP" +#if defined(__VISUALDSPVERSION__) + /* __VISUALDSPVERSION__ = 0xVVRRPP00 */ +# define COMPILER_VERSION_MAJOR HEX(__VISUALDSPVERSION__>>24) +# define COMPILER_VERSION_MINOR HEX(__VISUALDSPVERSION__>>16 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__VISUALDSPVERSION__>>8 & 0xFF) +#endif + +#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC) +# define COMPILER_ID "IAR" +# if defined(__VER__) && defined(__ICCARM__) +# define COMPILER_VERSION_MAJOR DEC((__VER__) / 1000000) +# define COMPILER_VERSION_MINOR DEC(((__VER__) / 1000) % 1000) +# define COMPILER_VERSION_PATCH DEC((__VER__) % 1000) +# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__) +# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__) || defined(__ICC8051__)) +# define COMPILER_VERSION_MAJOR DEC((__VER__) / 100) +# define COMPILER_VERSION_MINOR DEC((__VER__) - (((__VER__) / 100)*100)) +# define COMPILER_VERSION_PATCH DEC(__SUBVERSION__) +# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__) +# endif + +#elif defined(__SDCC_VERSION_MAJOR) || defined(SDCC) +# define COMPILER_ID "SDCC" +# if defined(__SDCC_VERSION_MAJOR) +# define COMPILER_VERSION_MAJOR DEC(__SDCC_VERSION_MAJOR) +# define COMPILER_VERSION_MINOR DEC(__SDCC_VERSION_MINOR) +# define COMPILER_VERSION_PATCH DEC(__SDCC_VERSION_PATCH) +# else + /* SDCC = VRP */ +# define COMPILER_VERSION_MAJOR DEC(SDCC/100) +# define COMPILER_VERSION_MINOR DEC(SDCC/10 % 10) +# define COMPILER_VERSION_PATCH DEC(SDCC % 10) +# endif + + +/* These compilers are either not known or too old to define an + identification macro. Try to identify the platform and guess that + it is the native compiler. */ +#elif defined(__hpux) || defined(__hpua) +# define COMPILER_ID "HP" + +#else /* unknown compiler */ +# define COMPILER_ID "" +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]"; +#ifdef SIMULATE_ID +char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; +#endif + +#ifdef __QNXNTO__ +char const* qnxnto = "INFO" ":" "qnxnto[]"; +#endif + +#if defined(__CRAYXE) || defined(__CRAYXC) +char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]"; +#endif + +#define STRINGIFY_HELPER(X) #X +#define STRINGIFY(X) STRINGIFY_HELPER(X) + +/* Identify known platforms by name. */ +#if defined(__linux) || defined(__linux__) || defined(linux) +# define PLATFORM_ID "Linux" + +#elif defined(__CYGWIN__) +# define PLATFORM_ID "Cygwin" + +#elif defined(__MINGW32__) +# define PLATFORM_ID "MinGW" + +#elif defined(__APPLE__) +# define PLATFORM_ID "Darwin" + +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define PLATFORM_ID "Windows" + +#elif defined(__FreeBSD__) || defined(__FreeBSD) +# define PLATFORM_ID "FreeBSD" + +#elif defined(__NetBSD__) || defined(__NetBSD) +# define PLATFORM_ID "NetBSD" + +#elif defined(__OpenBSD__) || defined(__OPENBSD) +# define PLATFORM_ID "OpenBSD" + +#elif defined(__sun) || defined(sun) +# define PLATFORM_ID "SunOS" + +#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__) +# define PLATFORM_ID "AIX" + +#elif defined(__hpux) || defined(__hpux__) +# define PLATFORM_ID "HP-UX" + +#elif defined(__HAIKU__) +# define PLATFORM_ID "Haiku" + +#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS) +# define PLATFORM_ID "BeOS" + +#elif defined(__QNX__) || defined(__QNXNTO__) +# define PLATFORM_ID "QNX" + +#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__) +# define PLATFORM_ID "Tru64" + +#elif defined(__riscos) || defined(__riscos__) +# define PLATFORM_ID "RISCos" + +#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__) +# define PLATFORM_ID "SINIX" + +#elif defined(__UNIX_SV__) +# define PLATFORM_ID "UNIX_SV" + +#elif defined(__bsdos__) +# define PLATFORM_ID "BSDOS" + +#elif defined(_MPRAS) || defined(MPRAS) +# define PLATFORM_ID "MP-RAS" + +#elif defined(__osf) || defined(__osf__) +# define PLATFORM_ID "OSF1" + +#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv) +# define PLATFORM_ID "SCO_SV" + +#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX) +# define PLATFORM_ID "ULTRIX" + +#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX) +# define PLATFORM_ID "Xenix" + +#elif defined(__WATCOMC__) +# if defined(__LINUX__) +# define PLATFORM_ID "Linux" + +# elif defined(__DOS__) +# define PLATFORM_ID "DOS" + +# elif defined(__OS2__) +# define PLATFORM_ID "OS2" + +# elif defined(__WINDOWS__) +# define PLATFORM_ID "Windows3x" + +# else /* unknown platform */ +# define PLATFORM_ID +# endif + +#elif defined(__INTEGRITY) +# if defined(INT_178B) +# define PLATFORM_ID "Integrity178" + +# else /* regular Integrity */ +# define PLATFORM_ID "Integrity" +# endif + +#else /* unknown platform */ +# define PLATFORM_ID + +#endif + +/* For windows compilers MSVC and Intel we can determine + the architecture of the compiler being used. This is because + the compilers do not have flags that can change the architecture, + but rather depend on which compiler is being used +*/ +#if defined(_WIN32) && defined(_MSC_VER) +# if defined(_M_IA64) +# define ARCHITECTURE_ID "IA64" + +# elif defined(_M_X64) || defined(_M_AMD64) +# define ARCHITECTURE_ID "x64" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# elif defined(_M_ARM64) +# define ARCHITECTURE_ID "ARM64" + +# elif defined(_M_ARM) +# if _M_ARM == 4 +# define ARCHITECTURE_ID "ARMV4I" +# elif _M_ARM == 5 +# define ARCHITECTURE_ID "ARMV5I" +# else +# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM) +# endif + +# elif defined(_M_MIPS) +# define ARCHITECTURE_ID "MIPS" + +# elif defined(_M_SH) +# define ARCHITECTURE_ID "SHx" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#elif defined(__WATCOMC__) +# if defined(_M_I86) +# define ARCHITECTURE_ID "I86" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC) +# if defined(__ICCARM__) +# define ARCHITECTURE_ID "ARM" + +# elif defined(__ICCRX__) +# define ARCHITECTURE_ID "RX" + +# elif defined(__ICCRH850__) +# define ARCHITECTURE_ID "RH850" + +# elif defined(__ICCRL78__) +# define ARCHITECTURE_ID "RL78" + +# elif defined(__ICCRISCV__) +# define ARCHITECTURE_ID "RISCV" + +# elif defined(__ICCAVR__) +# define ARCHITECTURE_ID "AVR" + +# elif defined(__ICC430__) +# define ARCHITECTURE_ID "MSP430" + +# elif defined(__ICCV850__) +# define ARCHITECTURE_ID "V850" + +# elif defined(__ICC8051__) +# define ARCHITECTURE_ID "8051" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#elif defined(__ghs__) +# if defined(__PPC64__) +# define ARCHITECTURE_ID "PPC64" + +# elif defined(__ppc__) +# define ARCHITECTURE_ID "PPC" + +# elif defined(__ARM__) +# define ARCHITECTURE_ID "ARM" + +# elif defined(__x86_64__) +# define ARCHITECTURE_ID "x64" + +# elif defined(__i386__) +# define ARCHITECTURE_ID "X86" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif +#else +# define ARCHITECTURE_ID +#endif + +/* Convert integer to decimal digit literals. */ +#define DEC(n) \ + ('0' + (((n) / 10000000)%10)), \ + ('0' + (((n) / 1000000)%10)), \ + ('0' + (((n) / 100000)%10)), \ + ('0' + (((n) / 10000)%10)), \ + ('0' + (((n) / 1000)%10)), \ + ('0' + (((n) / 100)%10)), \ + ('0' + (((n) / 10)%10)), \ + ('0' + ((n) % 10)) + +/* Convert integer to hex digit literals. */ +#define HEX(n) \ + ('0' + ((n)>>28 & 0xF)), \ + ('0' + ((n)>>24 & 0xF)), \ + ('0' + ((n)>>20 & 0xF)), \ + ('0' + ((n)>>16 & 0xF)), \ + ('0' + ((n)>>12 & 0xF)), \ + ('0' + ((n)>>8 & 0xF)), \ + ('0' + ((n)>>4 & 0xF)), \ + ('0' + ((n) & 0xF)) + +/* Construct a string literal encoding the version number components. */ +#ifdef COMPILER_VERSION_MAJOR +char const info_version[] = { + 'I', 'N', 'F', 'O', ':', + 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[', + COMPILER_VERSION_MAJOR, +# ifdef COMPILER_VERSION_MINOR + '.', COMPILER_VERSION_MINOR, +# ifdef COMPILER_VERSION_PATCH + '.', COMPILER_VERSION_PATCH, +# ifdef COMPILER_VERSION_TWEAK + '.', COMPILER_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct a string literal encoding the internal version number. */ +#ifdef COMPILER_VERSION_INTERNAL +char const info_version_internal[] = { + 'I', 'N', 'F', 'O', ':', + 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','_', + 'i','n','t','e','r','n','a','l','[', + COMPILER_VERSION_INTERNAL,']','\0'}; +#endif + +/* Construct a string literal encoding the version number components. */ +#ifdef SIMULATE_VERSION_MAJOR +char const info_simulate_version[] = { + 'I', 'N', 'F', 'O', ':', + 's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[', + SIMULATE_VERSION_MAJOR, +# ifdef SIMULATE_VERSION_MINOR + '.', SIMULATE_VERSION_MINOR, +# ifdef SIMULATE_VERSION_PATCH + '.', SIMULATE_VERSION_PATCH, +# ifdef SIMULATE_VERSION_TWEAK + '.', SIMULATE_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]"; +char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]"; + + + + +#if !defined(__STDC__) +# if (defined(_MSC_VER) && !defined(__clang__)) \ + || (defined(__ibmxl__) || defined(__IBMC__)) +# define C_DIALECT "90" +# else +# define C_DIALECT +# endif +#elif __STDC_VERSION__ >= 201000L +# define C_DIALECT "11" +#elif __STDC_VERSION__ >= 199901L +# define C_DIALECT "99" +#else +# define C_DIALECT "90" +#endif +const char* info_language_dialect_default = + "INFO" ":" "dialect_default[" C_DIALECT "]"; + +/*--------------------------------------------------------------------------*/ + +#ifdef ID_VOID_MAIN +void main() {} +#else +# if defined(__CLASSIC_C__) +int main(argc, argv) int argc; char *argv[]; +# else +int main(int argc, char* argv[]) +# endif +{ + int require = 0; + require += info_compiler[argc]; + require += info_platform[argc]; + require += info_arch[argc]; +#ifdef COMPILER_VERSION_MAJOR + require += info_version[argc]; +#endif +#ifdef COMPILER_VERSION_INTERNAL + require += info_version_internal[argc]; +#endif +#ifdef SIMULATE_ID + require += info_simulate[argc]; +#endif +#ifdef SIMULATE_VERSION_MAJOR + require += info_simulate_version[argc]; +#endif +#if defined(__CRAYXE) || defined(__CRAYXC) + require += info_cray[argc]; +#endif + require += info_language_dialect_default[argc]; + (void)argv; + return require; +} +#endif diff --git a/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdC/a.out b/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdC/a.out new file mode 100755 index 0000000000000000000000000000000000000000..e808db50fe49df4b04ec03f39c6dd3f372422ff3 GIT binary patch literal 8616 zcmeHN&1(}u6rb&f2CcS2(TX2z3rdxeCap+O5Mr7(MQw|1Kn!)*B-?gj^TB3gTeMIQ zYHJbdt;gcYgMWhv9(wTLU*OSO#2$-?_jjhQp(QuwFx{j4wkj}<&_`VLKzD#?yxxm-iuO2k8d@v( zoiNhc`_=aU&BijMlvoFJuvBygZ7VgHS+O&~B#V_ zCML(GZlv?sf@PadGEykmX4E!v3v*2L6l}wt&pX-VsNrCDCcTD}UQ9|mV;Q!Yc6r8} zH%hjf42S3FDZJ@^VE)*2idH_C3<(K`gPsuz`NnWC91abKLc>Ftj#Sxs5v9JR?|J&( zzlN5y2YWygvT2{tftg+24uaeirXv3SHZ1p%}MY{@?tVNdBSggw*f%%o&2n7rXUguTE&4)!i0)S8aVI$gHtLUl$G zx&sN_uT%3XoOEz;nlLJtU2qZZm6(c@ghLYdNIWHRK;lP)aVX-~;cdd0=>KoP0S2T{ vLUdHh4{{=^@mxHkXI3ciVAIF;x+^q!&?k}$rl}~*i*6~!Q>gpn1qk^AYzYxN literal 0 HcmV?d00001 diff --git a/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdCXX/CMakeCXXCompilerId.cpp b/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdCXX/CMakeCXXCompilerId.cpp new file mode 100644 index 00000000..69cfdba6 --- /dev/null +++ b/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdCXX/CMakeCXXCompilerId.cpp @@ -0,0 +1,660 @@ +/* This source file must have a .cpp extension so that all C++ compilers + recognize the extension without flags. Borland does not know .cxx for + example. */ +#ifndef __cplusplus +# error "A C compiler has been selected for C++." +#endif + + +/* Version number components: V=Version, R=Revision, P=Patch + Version date components: YYYY=Year, MM=Month, DD=Day */ + +#if defined(__COMO__) +# define COMPILER_ID "Comeau" + /* __COMO_VERSION__ = VRR */ +# define COMPILER_VERSION_MAJOR DEC(__COMO_VERSION__ / 100) +# define COMPILER_VERSION_MINOR DEC(__COMO_VERSION__ % 100) + +#elif defined(__INTEL_COMPILER) || defined(__ICC) +# define COMPILER_ID "Intel" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# if defined(__GNUC__) +# define SIMULATE_ID "GNU" +# endif + /* __INTEL_COMPILER = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100) +# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10) +# if defined(__INTEL_COMPILER_UPDATE) +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE) +# else +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10) +# endif +# if defined(__INTEL_COMPILER_BUILD_DATE) + /* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */ +# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE) +# endif +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif +# if defined(__GNUC__) +# define SIMULATE_VERSION_MAJOR DEC(__GNUC__) +# elif defined(__GNUG__) +# define SIMULATE_VERSION_MAJOR DEC(__GNUG__) +# endif +# if defined(__GNUC_MINOR__) +# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__) +# endif +# if defined(__GNUC_PATCHLEVEL__) +# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__) +# endif + +#elif defined(__PATHCC__) +# define COMPILER_ID "PathScale" +# define COMPILER_VERSION_MAJOR DEC(__PATHCC__) +# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__) +# if defined(__PATHCC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__) +# endif + +#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__) +# define COMPILER_ID "Embarcadero" +# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF) +# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF) +# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF) + +#elif defined(__BORLANDC__) +# define COMPILER_ID "Borland" + /* __BORLANDC__ = 0xVRR */ +# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8) +# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF) + +#elif defined(__WATCOMC__) && __WATCOMC__ < 1200 +# define COMPILER_ID "Watcom" + /* __WATCOMC__ = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__WATCOMC__) +# define COMPILER_ID "OpenWatcom" + /* __WATCOMC__ = VVRP + 1100 */ +# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__SUNPRO_CC) +# define COMPILER_ID "SunPro" +# if __SUNPRO_CC >= 0x5100 + /* __SUNPRO_CC = 0xVRRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>12) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF) +# else + /* __SUNPRO_CC = 0xVRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>8) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF) +# endif + +#elif defined(__HP_aCC) +# define COMPILER_ID "HP" + /* __HP_aCC = VVRRPP */ +# define COMPILER_VERSION_MAJOR DEC(__HP_aCC/10000) +# define COMPILER_VERSION_MINOR DEC(__HP_aCC/100 % 100) +# define COMPILER_VERSION_PATCH DEC(__HP_aCC % 100) + +#elif defined(__DECCXX) +# define COMPILER_ID "Compaq" + /* __DECCXX_VER = VVRRTPPPP */ +# define COMPILER_VERSION_MAJOR DEC(__DECCXX_VER/10000000) +# define COMPILER_VERSION_MINOR DEC(__DECCXX_VER/100000 % 100) +# define COMPILER_VERSION_PATCH DEC(__DECCXX_VER % 10000) + +#elif defined(__IBMCPP__) && defined(__COMPILER_VER__) +# define COMPILER_ID "zOS" + /* __IBMCPP__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) + +#elif defined(__ibmxl__) && defined(__clang__) +# define COMPILER_ID "XLClang" +# define COMPILER_VERSION_MAJOR DEC(__ibmxl_version__) +# define COMPILER_VERSION_MINOR DEC(__ibmxl_release__) +# define COMPILER_VERSION_PATCH DEC(__ibmxl_modification__) +# define COMPILER_VERSION_TWEAK DEC(__ibmxl_ptf_fix_level__) + + +#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ >= 800 +# define COMPILER_ID "XL" + /* __IBMCPP__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) + +#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ < 800 +# define COMPILER_ID "VisualAge" + /* __IBMCPP__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) + +#elif defined(__PGI) +# define COMPILER_ID "PGI" +# define COMPILER_VERSION_MAJOR DEC(__PGIC__) +# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__) +# if defined(__PGIC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__) +# endif + +#elif defined(_CRAYC) +# define COMPILER_ID "Cray" +# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR) +# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR) + +#elif defined(__TI_COMPILER_VERSION__) +# define COMPILER_ID "TI" + /* __TI_COMPILER_VERSION__ = VVVRRRPPP */ +# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000) +# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000) +# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000) + +#elif defined(__FUJITSU) || defined(__FCC_VERSION) || defined(__fcc_version) +# define COMPILER_ID "Fujitsu" + +#elif defined(__ghs__) +# define COMPILER_ID "GHS" +/* __GHS_VERSION_NUMBER = VVVVRP */ +# ifdef __GHS_VERSION_NUMBER +# define COMPILER_VERSION_MAJOR DEC(__GHS_VERSION_NUMBER / 100) +# define COMPILER_VERSION_MINOR DEC(__GHS_VERSION_NUMBER / 10 % 10) +# define COMPILER_VERSION_PATCH DEC(__GHS_VERSION_NUMBER % 10) +# endif + +#elif defined(__SCO_VERSION__) +# define COMPILER_ID "SCO" + +#elif defined(__ARMCC_VERSION) && !defined(__clang__) +# define COMPILER_ID "ARMCC" +#if __ARMCC_VERSION >= 1000000 + /* __ARMCC_VERSION = VRRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#else + /* __ARMCC_VERSION = VRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#endif + + +#elif defined(__clang__) && defined(__apple_build_version__) +# define COMPILER_ID "AppleClang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif +# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__) + +#elif defined(__clang__) && defined(__ARMCOMPILER_VERSION) +# define COMPILER_ID "ARMClang" + # define COMPILER_VERSION_MAJOR DEC(__ARMCOMPILER_VERSION/1000000) + # define COMPILER_VERSION_MINOR DEC(__ARMCOMPILER_VERSION/10000 % 100) + # define COMPILER_VERSION_PATCH DEC(__ARMCOMPILER_VERSION % 10000) +# define COMPILER_VERSION_INTERNAL DEC(__ARMCOMPILER_VERSION) + +#elif defined(__clang__) +# define COMPILER_ID "Clang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif + +#elif defined(__GNUC__) || defined(__GNUG__) +# define COMPILER_ID "GNU" +# if defined(__GNUC__) +# define COMPILER_VERSION_MAJOR DEC(__GNUC__) +# else +# define COMPILER_VERSION_MAJOR DEC(__GNUG__) +# endif +# if defined(__GNUC_MINOR__) +# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__) +# endif +# if defined(__GNUC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__) +# endif + +#elif defined(_MSC_VER) +# define COMPILER_ID "MSVC" + /* _MSC_VER = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100) +# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100) +# if defined(_MSC_FULL_VER) +# if _MSC_VER >= 1400 + /* _MSC_FULL_VER = VVRRPPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000) +# else + /* _MSC_FULL_VER = VVRRPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000) +# endif +# endif +# if defined(_MSC_BUILD) +# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD) +# endif + +#elif defined(__VISUALDSPVERSION__) || defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__) +# define COMPILER_ID "ADSP" +#if defined(__VISUALDSPVERSION__) + /* __VISUALDSPVERSION__ = 0xVVRRPP00 */ +# define COMPILER_VERSION_MAJOR HEX(__VISUALDSPVERSION__>>24) +# define COMPILER_VERSION_MINOR HEX(__VISUALDSPVERSION__>>16 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__VISUALDSPVERSION__>>8 & 0xFF) +#endif + +#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC) +# define COMPILER_ID "IAR" +# if defined(__VER__) && defined(__ICCARM__) +# define COMPILER_VERSION_MAJOR DEC((__VER__) / 1000000) +# define COMPILER_VERSION_MINOR DEC(((__VER__) / 1000) % 1000) +# define COMPILER_VERSION_PATCH DEC((__VER__) % 1000) +# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__) +# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__) || defined(__ICC8051__)) +# define COMPILER_VERSION_MAJOR DEC((__VER__) / 100) +# define COMPILER_VERSION_MINOR DEC((__VER__) - (((__VER__) / 100)*100)) +# define COMPILER_VERSION_PATCH DEC(__SUBVERSION__) +# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__) +# endif + + +/* These compilers are either not known or too old to define an + identification macro. Try to identify the platform and guess that + it is the native compiler. */ +#elif defined(__hpux) || defined(__hpua) +# define COMPILER_ID "HP" + +#else /* unknown compiler */ +# define COMPILER_ID "" +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]"; +#ifdef SIMULATE_ID +char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; +#endif + +#ifdef __QNXNTO__ +char const* qnxnto = "INFO" ":" "qnxnto[]"; +#endif + +#if defined(__CRAYXE) || defined(__CRAYXC) +char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]"; +#endif + +#define STRINGIFY_HELPER(X) #X +#define STRINGIFY(X) STRINGIFY_HELPER(X) + +/* Identify known platforms by name. */ +#if defined(__linux) || defined(__linux__) || defined(linux) +# define PLATFORM_ID "Linux" + +#elif defined(__CYGWIN__) +# define PLATFORM_ID "Cygwin" + +#elif defined(__MINGW32__) +# define PLATFORM_ID "MinGW" + +#elif defined(__APPLE__) +# define PLATFORM_ID "Darwin" + +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define PLATFORM_ID "Windows" + +#elif defined(__FreeBSD__) || defined(__FreeBSD) +# define PLATFORM_ID "FreeBSD" + +#elif defined(__NetBSD__) || defined(__NetBSD) +# define PLATFORM_ID "NetBSD" + +#elif defined(__OpenBSD__) || defined(__OPENBSD) +# define PLATFORM_ID "OpenBSD" + +#elif defined(__sun) || defined(sun) +# define PLATFORM_ID "SunOS" + +#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__) +# define PLATFORM_ID "AIX" + +#elif defined(__hpux) || defined(__hpux__) +# define PLATFORM_ID "HP-UX" + +#elif defined(__HAIKU__) +# define PLATFORM_ID "Haiku" + +#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS) +# define PLATFORM_ID "BeOS" + +#elif defined(__QNX__) || defined(__QNXNTO__) +# define PLATFORM_ID "QNX" + +#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__) +# define PLATFORM_ID "Tru64" + +#elif defined(__riscos) || defined(__riscos__) +# define PLATFORM_ID "RISCos" + +#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__) +# define PLATFORM_ID "SINIX" + +#elif defined(__UNIX_SV__) +# define PLATFORM_ID "UNIX_SV" + +#elif defined(__bsdos__) +# define PLATFORM_ID "BSDOS" + +#elif defined(_MPRAS) || defined(MPRAS) +# define PLATFORM_ID "MP-RAS" + +#elif defined(__osf) || defined(__osf__) +# define PLATFORM_ID "OSF1" + +#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv) +# define PLATFORM_ID "SCO_SV" + +#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX) +# define PLATFORM_ID "ULTRIX" + +#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX) +# define PLATFORM_ID "Xenix" + +#elif defined(__WATCOMC__) +# if defined(__LINUX__) +# define PLATFORM_ID "Linux" + +# elif defined(__DOS__) +# define PLATFORM_ID "DOS" + +# elif defined(__OS2__) +# define PLATFORM_ID "OS2" + +# elif defined(__WINDOWS__) +# define PLATFORM_ID "Windows3x" + +# else /* unknown platform */ +# define PLATFORM_ID +# endif + +#elif defined(__INTEGRITY) +# if defined(INT_178B) +# define PLATFORM_ID "Integrity178" + +# else /* regular Integrity */ +# define PLATFORM_ID "Integrity" +# endif + +#else /* unknown platform */ +# define PLATFORM_ID + +#endif + +/* For windows compilers MSVC and Intel we can determine + the architecture of the compiler being used. This is because + the compilers do not have flags that can change the architecture, + but rather depend on which compiler is being used +*/ +#if defined(_WIN32) && defined(_MSC_VER) +# if defined(_M_IA64) +# define ARCHITECTURE_ID "IA64" + +# elif defined(_M_X64) || defined(_M_AMD64) +# define ARCHITECTURE_ID "x64" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# elif defined(_M_ARM64) +# define ARCHITECTURE_ID "ARM64" + +# elif defined(_M_ARM) +# if _M_ARM == 4 +# define ARCHITECTURE_ID "ARMV4I" +# elif _M_ARM == 5 +# define ARCHITECTURE_ID "ARMV5I" +# else +# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM) +# endif + +# elif defined(_M_MIPS) +# define ARCHITECTURE_ID "MIPS" + +# elif defined(_M_SH) +# define ARCHITECTURE_ID "SHx" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#elif defined(__WATCOMC__) +# if defined(_M_I86) +# define ARCHITECTURE_ID "I86" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC) +# if defined(__ICCARM__) +# define ARCHITECTURE_ID "ARM" + +# elif defined(__ICCRX__) +# define ARCHITECTURE_ID "RX" + +# elif defined(__ICCRH850__) +# define ARCHITECTURE_ID "RH850" + +# elif defined(__ICCRL78__) +# define ARCHITECTURE_ID "RL78" + +# elif defined(__ICCRISCV__) +# define ARCHITECTURE_ID "RISCV" + +# elif defined(__ICCAVR__) +# define ARCHITECTURE_ID "AVR" + +# elif defined(__ICC430__) +# define ARCHITECTURE_ID "MSP430" + +# elif defined(__ICCV850__) +# define ARCHITECTURE_ID "V850" + +# elif defined(__ICC8051__) +# define ARCHITECTURE_ID "8051" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#elif defined(__ghs__) +# if defined(__PPC64__) +# define ARCHITECTURE_ID "PPC64" + +# elif defined(__ppc__) +# define ARCHITECTURE_ID "PPC" + +# elif defined(__ARM__) +# define ARCHITECTURE_ID "ARM" + +# elif defined(__x86_64__) +# define ARCHITECTURE_ID "x64" + +# elif defined(__i386__) +# define ARCHITECTURE_ID "X86" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif +#else +# define ARCHITECTURE_ID +#endif + +/* Convert integer to decimal digit literals. */ +#define DEC(n) \ + ('0' + (((n) / 10000000)%10)), \ + ('0' + (((n) / 1000000)%10)), \ + ('0' + (((n) / 100000)%10)), \ + ('0' + (((n) / 10000)%10)), \ + ('0' + (((n) / 1000)%10)), \ + ('0' + (((n) / 100)%10)), \ + ('0' + (((n) / 10)%10)), \ + ('0' + ((n) % 10)) + +/* Convert integer to hex digit literals. */ +#define HEX(n) \ + ('0' + ((n)>>28 & 0xF)), \ + ('0' + ((n)>>24 & 0xF)), \ + ('0' + ((n)>>20 & 0xF)), \ + ('0' + ((n)>>16 & 0xF)), \ + ('0' + ((n)>>12 & 0xF)), \ + ('0' + ((n)>>8 & 0xF)), \ + ('0' + ((n)>>4 & 0xF)), \ + ('0' + ((n) & 0xF)) + +/* Construct a string literal encoding the version number components. */ +#ifdef COMPILER_VERSION_MAJOR +char const info_version[] = { + 'I', 'N', 'F', 'O', ':', + 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[', + COMPILER_VERSION_MAJOR, +# ifdef COMPILER_VERSION_MINOR + '.', COMPILER_VERSION_MINOR, +# ifdef COMPILER_VERSION_PATCH + '.', COMPILER_VERSION_PATCH, +# ifdef COMPILER_VERSION_TWEAK + '.', COMPILER_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct a string literal encoding the internal version number. */ +#ifdef COMPILER_VERSION_INTERNAL +char const info_version_internal[] = { + 'I', 'N', 'F', 'O', ':', + 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','_', + 'i','n','t','e','r','n','a','l','[', + COMPILER_VERSION_INTERNAL,']','\0'}; +#endif + +/* Construct a string literal encoding the version number components. */ +#ifdef SIMULATE_VERSION_MAJOR +char const info_simulate_version[] = { + 'I', 'N', 'F', 'O', ':', + 's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[', + SIMULATE_VERSION_MAJOR, +# ifdef SIMULATE_VERSION_MINOR + '.', SIMULATE_VERSION_MINOR, +# ifdef SIMULATE_VERSION_PATCH + '.', SIMULATE_VERSION_PATCH, +# ifdef SIMULATE_VERSION_TWEAK + '.', SIMULATE_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]"; +char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]"; + + + + +#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG) && _MSVC_LANG < 201403L +# if defined(__INTEL_CXX11_MODE__) +# if defined(__cpp_aggregate_nsdmi) +# define CXX_STD 201402L +# else +# define CXX_STD 201103L +# endif +# else +# define CXX_STD 199711L +# endif +#elif defined(_MSC_VER) && defined(_MSVC_LANG) +# define CXX_STD _MSVC_LANG +#else +# define CXX_STD __cplusplus +#endif + +const char* info_language_dialect_default = "INFO" ":" "dialect_default[" +#if CXX_STD > 201703L + "20" +#elif CXX_STD >= 201703L + "17" +#elif CXX_STD >= 201402L + "14" +#elif CXX_STD >= 201103L + "11" +#else + "98" +#endif +"]"; + +/*--------------------------------------------------------------------------*/ + +int main(int argc, char* argv[]) +{ + int require = 0; + require += info_compiler[argc]; + require += info_platform[argc]; +#ifdef COMPILER_VERSION_MAJOR + require += info_version[argc]; +#endif +#ifdef COMPILER_VERSION_INTERNAL + require += info_version_internal[argc]; +#endif +#ifdef SIMULATE_ID + require += info_simulate[argc]; +#endif +#ifdef SIMULATE_VERSION_MAJOR + require += info_simulate_version[argc]; +#endif +#if defined(__CRAYXE) || defined(__CRAYXC) + require += info_cray[argc]; +#endif + require += info_language_dialect_default[argc]; + (void)argv; + return require; +} diff --git a/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdCXX/a.out b/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdCXX/a.out new file mode 100755 index 0000000000000000000000000000000000000000..cb4a5372753abb09600e35e146cdb9abc8e86491 GIT binary patch literal 8600 zcmeHN&rcIU6rQDkL=mbI0}+#jgFzvsr5^A{j3BEeA`pedP;uC{3v9Ce;dYB~Fd-%y z2r==Fo&?m?$J=S6hatNwU4VnR!qmvt<&bDN&z3#cE-Xo7eZ14{e!N*Z z4qPkXtX3#qu~4oiSRbz><6V=JfJ6D!2BSXbmBLfIkmGh?u_$nuzqc|T-Vp@2J|};~ z|HOOPCgpr=WM-shg!m46z8}N5#_v7m9=VM@e;`O^i?8HO9t+ zJ)+m;MHsRz6G0Q?1bZ)I>v42+##+!GAuT#N(iphvBz>*>{?Q)Vr-V<&71W-zzr}Xw_UOpKO1BED@um?2O2B zxW-@0Y4kM=`#)GKdsaR+DyNV)V;4w~&xY>vc~N5Ut3)xN7*Gr-1{4E|0mXn~Kr!$) z24+*6pLdO)aNB&gpW3W^+coxpQd>Icw3ZI9~e7@H`UsB@~^nSZ>)a7SeGciA2or#N$CH8A~MM$#^_D zL@{s%=$3J6Tl}8K@BPcvLeJ0{5Q09=39U3UDrUo4?EnPl+W~ow9F^^)Y~iCsF`yVw z3@8Q^1BwB~fMP%~pcqgLCg*AoU*^`Tm92OdU$)Gg<+8rK zX&2Z4>d%}daMuuFJLjot9o&(yrQlA4Ik*R5PjFpQF_$b}cNM}`B5Yl!`BWW@pyV21 z+q%ymB0?&pZFFf7)XA}4yXIFoasP1^m@1J`G6DBUjG`lOT;fiNrzCEX_%Sd!2LBU4 z{goh=5u#IN{4fWi`p(6}`Q3wO_u-Kb><=IC!v&Wp`|`3^$?yzS_K9N7=Oz+Ef?;_h G#C`)wH4ha4 literal 0 HcmV?d00001 diff --git a/openvdb/CMakeFiles/CMakeOutput.log b/openvdb/CMakeFiles/CMakeOutput.log new file mode 100644 index 00000000..e1ca5616 --- /dev/null +++ b/openvdb/CMakeFiles/CMakeOutput.log @@ -0,0 +1,266 @@ +The system is: Darwin - 19.6.0 - x86_64 +Compiling the C compiler identification source file "CMakeCCompilerId.c" succeeded. +Compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc +Build flags: +Id flags: + +The output was: +0 + + +Compilation of the C compiler identification source "CMakeCCompilerId.c" produced "a.out" + +The C compiler identification is AppleClang, found in "/Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdC/a.out" + +Compiling the CXX compiler identification source file "CMakeCXXCompilerId.cpp" succeeded. +Compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ +Build flags: +Id flags: + +The output was: +0 + + +Compilation of the CXX compiler identification source "CMakeCXXCompilerId.cpp" produced "a.out" + +The CXX compiler identification is AppleClang, found in "/Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/3.18.0-rc2/CompilerIdCXX/a.out" + +Detecting C compiler ABI info compiled with the following output: +Change Dir: /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp + +Run Build Command(s):/usr/bin/make cmTC_4b437/fast && /Applications/Xcode.app/Contents/Developer/usr/bin/make -f CMakeFiles/cmTC_4b437.dir/build.make CMakeFiles/cmTC_4b437.dir/build +Building C object CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o +/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -v -Wl,-v -o CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o -c /Applications/CMake.app/Contents/share/cmake-3.18/Modules/CMakeCCompilerABI.c +Apple clang version 11.0.3 (clang-1103.0.32.62) +Target: x86_64-apple-darwin19.6.0 +Thread model: posix +InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin +clang: warning: -Wl,-v: 'linker' input unused [-Wunused-command-line-argument] + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.15.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name CMakeCCompilerABI.c -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=all -fno-strict-return -masm-verbose -munwind-tables -target-sdk-version=10.15.6 -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -target-linker-version 556.6 -v -coverage-notes-file /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp/CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.gcno -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/local/include -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -Wno-objc-signed-char-bool-implicit-int-conversion -Wno-extra-semi-stmt -Wno-quoted-include-in-framework-header -fdebug-compilation-dir /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp -ferror-limit 19 -fmessage-length 0 -stack-protector 1 -fstack-check -mdarwin-stkchk-strong-link -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fobjc-runtime=macosx-10.15.0 -fmax-type-align=16 -fdiagnostics-show-option -o CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o -x c /Applications/CMake.app/Contents/share/cmake-3.18/Modules/CMakeCCompilerABI.c +clang -cc1 version 11.0.3 (clang-1103.0.32.62) default target x86_64-apple-darwin19.6.0 +ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/local/include" +ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/Library/Frameworks" +#include "..." search starts here: +#include <...> search starts here: + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks (framework directory) +End of search list. +Linking C executable cmTC_4b437 +/Applications/CMake.app/Contents/bin/cmake -E cmake_link_script CMakeFiles/cmTC_4b437.dir/link.txt --verbose=1 +/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names -v -Wl,-v CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o -o cmTC_4b437 +Apple clang version 11.0.3 (clang-1103.0.32.62) +Target: x86_64-apple-darwin19.6.0 +Thread model: posix +InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -arch x86_64 -platform_version macos 10.15.0 10.15.6 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -o cmTC_4b437 -search_paths_first -headerpad_max_install_names -v CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a +@(#)PROGRAM:ld PROJECT:ld64-556.6 +BUILD 18:54:40 Jun 2 2020 +configured to support archs: armv6 armv7 armv7s arm64 arm64e arm64_32 i386 x86_64 x86_64h armv6m armv7k armv7m armv7em +Library search paths: + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib +Framework search paths: + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/ + + + +Parsed C implicit include dir info from above output: rv=done + found start of include info + found start of implicit include info + add: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include] + add: [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include] + add: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] + end of search list found + collapse include dir [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include] ==> [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include] + collapse include dir [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include] ==> [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include] + collapse include dir [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] ==> [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] + implicit include dirs: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include;/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] + + +Parsed C implicit link information from above output: + link line regex: [^( *|.*[/\])(ld|CMAKE_LINK_STARTFILE-NOTFOUND|([^/\]+-)?ld|collect2)[^/\]*( |$)] + ignore line: [Change Dir: /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp] + ignore line: [] + ignore line: [Run Build Command(s):/usr/bin/make cmTC_4b437/fast && /Applications/Xcode.app/Contents/Developer/usr/bin/make -f CMakeFiles/cmTC_4b437.dir/build.make CMakeFiles/cmTC_4b437.dir/build] + ignore line: [Building C object CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o] + ignore line: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -v -Wl -v -o CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o -c /Applications/CMake.app/Contents/share/cmake-3.18/Modules/CMakeCCompilerABI.c] + ignore line: [Apple clang version 11.0.3 (clang-1103.0.32.62)] + ignore line: [Target: x86_64-apple-darwin19.6.0] + ignore line: [Thread model: posix] + ignore line: [InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin] + ignore line: [clang: warning: -Wl -v: 'linker' input unused [-Wunused-command-line-argument]] + ignore line: [ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.15.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name CMakeCCompilerABI.c -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=all -fno-strict-return -masm-verbose -munwind-tables -target-sdk-version=10.15.6 -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -target-linker-version 556.6 -v -coverage-notes-file /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp/CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.gcno -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/local/include -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -Wno-objc-signed-char-bool-implicit-int-conversion -Wno-extra-semi-stmt -Wno-quoted-include-in-framework-header -fdebug-compilation-dir /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp -ferror-limit 19 -fmessage-length 0 -stack-protector 1 -fstack-check -mdarwin-stkchk-strong-link -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fobjc-runtime=macosx-10.15.0 -fmax-type-align=16 -fdiagnostics-show-option -o CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o -x c /Applications/CMake.app/Contents/share/cmake-3.18/Modules/CMakeCCompilerABI.c] + ignore line: [clang -cc1 version 11.0.3 (clang-1103.0.32.62) default target x86_64-apple-darwin19.6.0] + ignore line: [ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/local/include"] + ignore line: [ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/Library/Frameworks"] + ignore line: [#include "..." search starts here:] + ignore line: [#include <...> search starts here:] + ignore line: [ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include] + ignore line: [ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include] + ignore line: [ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] + ignore line: [ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks (framework directory)] + ignore line: [End of search list.] + ignore line: [Linking C executable cmTC_4b437] + ignore line: [/Applications/CMake.app/Contents/bin/cmake -E cmake_link_script CMakeFiles/cmTC_4b437.dir/link.txt --verbose=1] + ignore line: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -Wl -search_paths_first -Wl -headerpad_max_install_names -v -Wl -v CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o -o cmTC_4b437 ] + ignore line: [Apple clang version 11.0.3 (clang-1103.0.32.62)] + ignore line: [Target: x86_64-apple-darwin19.6.0] + ignore line: [Thread model: posix] + ignore line: [InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin] + link line: [ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -arch x86_64 -platform_version macos 10.15.0 10.15.6 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -o cmTC_4b437 -search_paths_first -headerpad_max_install_names -v CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a] + arg [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld] ==> ignore + arg [-demangle] ==> ignore + arg [-lto_library] ==> ignore, skip following value + arg [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib] ==> skip value of -lto_library + arg [-dynamic] ==> ignore + arg [-arch] ==> ignore + arg [x86_64] ==> ignore + arg [-platform_version] ==> ignore + arg [macos] ==> ignore + arg [10.15.0] ==> ignore + arg [10.15.6] ==> ignore + arg [-syslibroot] ==> ignore + arg [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk] ==> ignore + arg [-o] ==> ignore + arg [cmTC_4b437] ==> ignore + arg [-search_paths_first] ==> ignore + arg [-headerpad_max_install_names] ==> ignore + arg [-v] ==> ignore + arg [CMakeFiles/cmTC_4b437.dir/CMakeCCompilerABI.c.o] ==> ignore + arg [-lSystem] ==> lib [System] + arg [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a] ==> lib [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a] + Library search paths: [;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib] + Framework search paths: [;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/] + remove lib [System] + remove lib [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a] + collapse library dir [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib] ==> [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib] + collapse framework dir [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/] ==> [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks] + implicit libs: [] + implicit dirs: [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib] + implicit fwks: [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks] + + +Detecting CXX compiler ABI info compiled with the following output: +Change Dir: /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp + +Run Build Command(s):/usr/bin/make cmTC_5ff60/fast && /Applications/Xcode.app/Contents/Developer/usr/bin/make -f CMakeFiles/cmTC_5ff60.dir/build.make CMakeFiles/cmTC_5ff60.dir/build +Building CXX object CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o +/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -v -Wl,-v -o CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o -c /Applications/CMake.app/Contents/share/cmake-3.18/Modules/CMakeCXXCompilerABI.cpp +Apple clang version 11.0.3 (clang-1103.0.32.62) +Target: x86_64-apple-darwin19.6.0 +Thread model: posix +InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin +clang: warning: -Wl,-v: 'linker' input unused [-Wunused-command-line-argument] + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.15.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name CMakeCXXCompilerABI.cpp -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=all -fno-strict-return -masm-verbose -munwind-tables -target-sdk-version=10.15.6 -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -target-linker-version 556.6 -v -coverage-notes-file /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp/CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.gcno -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/local/include -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -Wno-objc-signed-char-bool-implicit-int-conversion -Wno-extra-semi-stmt -Wno-quoted-include-in-framework-header -fdeprecated-macro -fdebug-compilation-dir /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp -ferror-limit 19 -fmessage-length 0 -stack-protector 1 -fstack-check -mdarwin-stkchk-strong-link -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fobjc-runtime=macosx-10.15.0 -fcxx-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -o CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o -x c++ /Applications/CMake.app/Contents/share/cmake-3.18/Modules/CMakeCXXCompilerABI.cpp +clang -cc1 version 11.0.3 (clang-1103.0.32.62) default target x86_64-apple-darwin19.6.0 +ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include/c++/v1" +ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/local/include" +ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/Library/Frameworks" +#include "..." search starts here: +#include <...> search starts here: + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1 + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks (framework directory) +End of search list. +Linking CXX executable cmTC_5ff60 +/Applications/CMake.app/Contents/bin/cmake -E cmake_link_script CMakeFiles/cmTC_5ff60.dir/link.txt --verbose=1 +/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names -v -Wl,-v CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_5ff60 +Apple clang version 11.0.3 (clang-1103.0.32.62) +Target: x86_64-apple-darwin19.6.0 +Thread model: posix +InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -arch x86_64 -platform_version macos 10.15.0 10.15.6 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -o cmTC_5ff60 -search_paths_first -headerpad_max_install_names -v CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o -lc++ -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a +@(#)PROGRAM:ld PROJECT:ld64-556.6 +BUILD 18:54:40 Jun 2 2020 +configured to support archs: armv6 armv7 armv7s arm64 arm64e arm64_32 i386 x86_64 x86_64h armv6m armv7k armv7m armv7em +Library search paths: + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib +Framework search paths: + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/ + + + +Parsed CXX implicit include dir info from above output: rv=done + found start of include info + found start of implicit include info + add: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1] + add: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include] + add: [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include] + add: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] + end of search list found + collapse include dir [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1] ==> [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1] + collapse include dir [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include] ==> [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include] + collapse include dir [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include] ==> [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include] + collapse include dir [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] ==> [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] + implicit include dirs: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1;/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include;/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] + + +Parsed CXX implicit link information from above output: + link line regex: [^( *|.*[/\])(ld|CMAKE_LINK_STARTFILE-NOTFOUND|([^/\]+-)?ld|collect2)[^/\]*( |$)] + ignore line: [Change Dir: /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp] + ignore line: [] + ignore line: [Run Build Command(s):/usr/bin/make cmTC_5ff60/fast && /Applications/Xcode.app/Contents/Developer/usr/bin/make -f CMakeFiles/cmTC_5ff60.dir/build.make CMakeFiles/cmTC_5ff60.dir/build] + ignore line: [Building CXX object CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o] + ignore line: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -v -Wl -v -o CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o -c /Applications/CMake.app/Contents/share/cmake-3.18/Modules/CMakeCXXCompilerABI.cpp] + ignore line: [Apple clang version 11.0.3 (clang-1103.0.32.62)] + ignore line: [Target: x86_64-apple-darwin19.6.0] + ignore line: [Thread model: posix] + ignore line: [InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin] + ignore line: [clang: warning: -Wl -v: 'linker' input unused [-Wunused-command-line-argument]] + ignore line: [ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.15.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name CMakeCXXCompilerABI.cpp -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=all -fno-strict-return -masm-verbose -munwind-tables -target-sdk-version=10.15.6 -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -target-linker-version 556.6 -v -coverage-notes-file /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp/CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.gcno -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/local/include -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -Wno-objc-signed-char-bool-implicit-int-conversion -Wno-extra-semi-stmt -Wno-quoted-include-in-framework-header -fdeprecated-macro -fdebug-compilation-dir /Users/Cesar/Repositories/mantaflow/openvdb/CMakeFiles/CMakeTmp -ferror-limit 19 -fmessage-length 0 -stack-protector 1 -fstack-check -mdarwin-stkchk-strong-link -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fobjc-runtime=macosx-10.15.0 -fcxx-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -o CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o -x c++ /Applications/CMake.app/Contents/share/cmake-3.18/Modules/CMakeCXXCompilerABI.cpp] + ignore line: [clang -cc1 version 11.0.3 (clang-1103.0.32.62) default target x86_64-apple-darwin19.6.0] + ignore line: [ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include/c++/v1"] + ignore line: [ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/local/include"] + ignore line: [ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/Library/Frameworks"] + ignore line: [#include "..." search starts here:] + ignore line: [#include <...> search starts here:] + ignore line: [ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1] + ignore line: [ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/include] + ignore line: [ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include] + ignore line: [ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include] + ignore line: [ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks (framework directory)] + ignore line: [End of search list.] + ignore line: [Linking CXX executable cmTC_5ff60] + ignore line: [/Applications/CMake.app/Contents/bin/cmake -E cmake_link_script CMakeFiles/cmTC_5ff60.dir/link.txt --verbose=1] + ignore line: [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -Wl -search_paths_first -Wl -headerpad_max_install_names -v -Wl -v CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_5ff60 ] + ignore line: [Apple clang version 11.0.3 (clang-1103.0.32.62)] + ignore line: [Target: x86_64-apple-darwin19.6.0] + ignore line: [Thread model: posix] + ignore line: [InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin] + link line: [ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -arch x86_64 -platform_version macos 10.15.0 10.15.6 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -o cmTC_5ff60 -search_paths_first -headerpad_max_install_names -v CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o -lc++ -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a] + arg [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld] ==> ignore + arg [-demangle] ==> ignore + arg [-lto_library] ==> ignore, skip following value + arg [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib] ==> skip value of -lto_library + arg [-dynamic] ==> ignore + arg [-arch] ==> ignore + arg [x86_64] ==> ignore + arg [-platform_version] ==> ignore + arg [macos] ==> ignore + arg [10.15.0] ==> ignore + arg [10.15.6] ==> ignore + arg [-syslibroot] ==> ignore + arg [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk] ==> ignore + arg [-o] ==> ignore + arg [cmTC_5ff60] ==> ignore + arg [-search_paths_first] ==> ignore + arg [-headerpad_max_install_names] ==> ignore + arg [-v] ==> ignore + arg [CMakeFiles/cmTC_5ff60.dir/CMakeCXXCompilerABI.cpp.o] ==> ignore + arg [-lc++] ==> lib [c++] + arg [-lSystem] ==> lib [System] + arg [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a] ==> lib [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a] + Library search paths: [;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib] + Framework search paths: [;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/] + remove lib [System] + remove lib [/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.3/lib/darwin/libclang_rt.osx.a] + collapse library dir [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib] ==> [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib] + collapse framework dir [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/] ==> [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks] + implicit libs: [c++] + implicit dirs: [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib] + implicit fwks: [/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks] + + diff --git a/openvdb/CMakeFiles/cmake.check_cache b/openvdb/CMakeFiles/cmake.check_cache new file mode 100644 index 00000000..3dccd731 --- /dev/null +++ b/openvdb/CMakeFiles/cmake.check_cache @@ -0,0 +1 @@ +# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/openvdb/CMakeLists.txt b/openvdb/CMakeLists.txt new file mode 100644 index 00000000..9501ff1a --- /dev/null +++ b/openvdb/CMakeLists.txt @@ -0,0 +1,531 @@ +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: MPL-2.0 +# +#[=======================================================================[ + + CMake Configuration for OpenVDB Core + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.3) +project(OpenVDBCore) + +# Monitoring _ROOT variables +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif() + +###### OpenVDB Core Options + +option(OPENVDB_CORE_SHARED "Build dynamically linked version of the core library." ON) +option(OPENVDB_CORE_STATIC "Build statically linked version of the core library." ON) + +set(OPENVDB_SHARED_LIBRARY_NAME "openvdb" CACHE STRING + "The base name of the built shared openvdb library. Prefixed by \"lib\" on UNIX platforms." +) +set(OPENVDB_STATIC_LIBRARY_NAME "openvdb" CACHE STRING + "The base name of the built static openvdb library. Prefixed by \"lib\"." +) + +mark_as_advanced( + OPENVDB_STATIC_LIBRARY_NAME + OPENVDB_SHARED_LIBRARY_NAME +) + +if(NOT OPENVDB_CORE_SHARED AND NOT OPENVDB_CORE_STATIC) + message(FATAL_ERROR "Both static and shared core OpenVDB libraries have been disabled. " + "At least one must be enabled when building the core library." + ) +endif() + +######################################################################### + +message(STATUS "----------------------------------------------------") +message(STATUS "------------- Configuring OpenVDBCore --------------") +message(STATUS "----------------------------------------------------") + +########################################################################## + +# Collect and configure lib dependencies + +if(USE_EXR) + find_package(IlmBase ${MINIMUM_ILMBASE_VERSION} REQUIRED) + find_package(OpenEXR ${MINIMUM_OPENEXR_VERSION} REQUIRED) + if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_OPENEXR_VERSION) + if(${OpenEXR_VERSION} VERSION_LESS FUTURE_MINIMUM_OPENEXR_VERSION) + message(DEPRECATION "Support for OpenEXR versions < ${FUTURE_MINIMUM_OPENEXR_VERSION} " + "is deprecated and will be removed.") + endif() + endif() +else() + find_package(IlmBase ${MINIMUM_ILMBASE_VERSION} REQUIRED COMPONENTS Half) +endif() + +if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_ILMBASE_VERSION) + if(${IlmBase_VERSION} VERSION_LESS FUTURE_MINIMUM_ILMBASE_VERSION) + message(DEPRECATION "Support for IlmBase versions < ${FUTURE_MINIMUM_ILMBASE_VERSION} " + "is deprecated and will be removed.") + endif() +endif() + +find_package(TBB ${MINIMUM_TBB_VERSION} REQUIRED COMPONENTS tbb) + +if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_TBB_VERSION) + if(${Tbb_VERSION} VERSION_LESS FUTURE_MINIMUM_TBB_VERSION) + message(DEPRECATION "Support for TBB versions < ${FUTURE_MINIMUM_TBB_VERSION} " + "is deprecated and will be removed.") + endif() +endif() + +find_package(ZLIB ${MINIMUM_ZLIB_VERSION} REQUIRED) + +if(USE_LOG4CPLUS) + find_package(Log4cplus ${MINIMUM_LOG4CPLUS_VERSION} REQUIRED) +endif() + +if(USE_BLOSC) + find_package(Blosc ${MINIMUM_BLOSC_VERSION} REQUIRED) + if(Blosc_FOUND AND Blosc_VERSION VERSION_GREATER MINIMUM_BLOSC_VERSION) + message(WARNING "The version of Blosc located is greater than ${MINIMUM_BLOSC_VERSION}. " + "There have been reported issues with using later versions of Blosc with OpenVDB. " + "OpenVDB has been tested fully against Blosc ${MINIMUM_BLOSC_VERSION}, it is " + "recommended that you use this version where possible." + ) + endif() +else() + message(WARNING "Blosc support is disabled. It is strongly recommended to " + "enable blosc for optimal builds of OpenVDB and to support compatible " + "serialization of other OpenVDB installations." + ) +endif() + +if(OPENVDB_CORE_SHARED) + # @note Both of these must be set for Boost 1.70 (VFX2020) to link against + # boost shared libraries (more specifically libraries built with -fPIC). + # http://boost.2283326.n4.nabble.com/CMake-config-scripts-broken-in-1-70-td4708957.html + # https://github.com/boostorg/boost_install/commit/160c7cb2b2c720e74463865ef0454d4c4cd9ae7c + set(BUILD_SHARED_LIBS ON) + set(Boost_USE_STATIC_LIBS OFF) +endif() + +find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS iostreams system) + +if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_BOOST_VERSION) + if(${Boost_LIB_VERSION} VERSION_LESS FUTURE_MINIMUM_BOOST_VERSION) + message(DEPRECATION "Support for Boost versions < ${FUTURE_MINIMUM_BOOST_VERSION} " + "is deprecated and will be removed.") + endif() +endif() + +if(UNIX) + find_package(Threads REQUIRED) +endif() + +if(CONCURRENT_MALLOC STREQUAL "Jemalloc") + find_package(Jemalloc) + if(NOT TARGET Jemalloc::jemalloc) + message(WARNING "Unable to find Jemalloc, attempting to fall back to TBB malloc. + It is recommended to use Jemalloc for optimum performance." + ) + find_package(TBB ${MINIMUM_TBB_VERSION} REQUIRED COMPONENTS tbbmalloc tbbmalloc_proxy) + endif() +elseif(CONCURRENT_MALLOC STREQUAL "Tbbmalloc") + find_package(TBB ${MINIMUM_TBB_VERSION} REQUIRED COMPONENTS tbbmalloc tbbmalloc_proxy) +endif() + +# Set deps. Note that the order here is important. If we're building against +# Houdini 17.5 we must include OpenEXR and IlmBase deps first to ensure the +# users chosen namespaced headers are correctly prioritized. Otherwise other +# include paths from shared installs (including houdini) may pull in the wrong +# headers + +set(OPENVDB_CORE_DEPENDENT_LIBS + Boost::iostreams + Boost::system + IlmBase::Half +) + +if(USE_EXR) + list(APPEND OPENVDB_CORE_DEPENDENT_LIBS + IlmBase::IlmThread + IlmBase::Iex + IlmBase::Imath + OpenEXR::IlmImf + ) +endif() + +if(USE_LOG4CPLUS) + list(APPEND OPENVDB_CORE_DEPENDENT_LIBS Log4cplus::log4cplus) +endif() + +# @todo blosc and zlib should be hidden (privately linked in): +# See FindOpenVDB.cmake + +if(USE_BLOSC) + list(APPEND OPENVDB_CORE_DEPENDENT_LIBS Blosc::blosc) +endif() + +list(APPEND OPENVDB_CORE_DEPENDENT_LIBS + TBB::tbb + ZLIB::ZLIB +) + +if(UNIX) + list(APPEND OPENVDB_CORE_DEPENDENT_LIBS Threads::Threads) +endif() + +if(TARGET Jemalloc::jemalloc) + list(APPEND OPENVDB_CORE_DEPENDENT_LIBS Jemalloc::jemalloc) +endif() + +if(TARGET TBB::tbbmalloc AND TARGET TBB::tbbmalloc_proxy) + list(APPEND OPENVDB_CORE_DEPENDENT_LIBS + TBB::tbbmalloc + TBB::tbbmalloc_proxy + ) +endif() + +########################################################################## + +if(WIN32) + # Because of implicit linking! + link_directories(${Boost_LIBRARY_DIR}) + if(OPENVDB_DISABLE_BOOST_IMPLICIT_LINKING) + add_definitions(-DBOOST_ALL_NO_LIB) + endif() +endif() + +# @todo Should be target definitions +if(WIN32) + add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_DLL) +endif() + +##### Core library configuration + +set(OPENVDB_LIBRARY_SOURCE_FILES + Grid.cc + io/Archive.cc + io/Compression.cc + io/DelayedLoadMetadata.cc + io/File.cc + io/GridDescriptor.cc + io/Queue.cc + io/Stream.cc + io/TempFile.cc + math/Maps.cc + math/Proximity.cc + math/QuantizedUnitVec.cc + math/Transform.cc + Metadata.cc + MetaMap.cc + openvdb.cc + Platform.cc + points/AttributeArray.cc + points/AttributeArrayString.cc + points/AttributeGroup.cc + points/AttributeSet.cc + points/StreamCompression.cc + points/points.cc + util/Formats.cc + util/Util.cc +) + +set(OPENVDB_LIBRARY_INCLUDE_FILES + Exceptions.h + Grid.h + Metadata.h + MetaMap.h + openvdb.h + Platform.h + PlatformConfig.h + Types.h + version.h +) + +set(OPENVDB_LIBRARY_IO_INCLUDE_FILES + io/Archive.h + io/Compression.h + io/DelayedLoadMetadata.h + io/File.h + io/GridDescriptor.h + io/io.h + io/Queue.h + io/Stream.h + io/TempFile.h +) + +set(OPENVDB_LIBRARY_MATH_INCLUDE_FILES + math/BBox.h + math/ConjGradient.h + math/Coord.h + math/DDA.h + math/FiniteDifference.h + math/LegacyFrustum.h + math/Maps.h + math/Mat.h + math/Mat3.h + math/Mat4.h + math/Math.h + math/Operators.h + math/Proximity.h + math/QuantizedUnitVec.h + math/Quat.h + math/Ray.h + math/Stats.h + math/Stencils.h + math/Transform.h + math/Tuple.h + math/Vec2.h + math/Vec3.h + math/Vec4.h +) + +set(OPENVDB_LIBRARY_POINTS_INCLUDE_FILES + points/AttributeArray.h + points/AttributeArrayString.h + points/AttributeGroup.h + points/AttributeSet.h + points/IndexFilter.h + points/IndexIterator.h + points/PointAdvect.h + points/PointAttribute.h + points/PointConversion.h + points/PointCount.h + points/PointDataGrid.h + points/PointDelete.h + points/PointGroup.h + points/PointMask.h + points/PointMove.h + points/PointSample.h + points/PointScatter.h + points/StreamCompression.h +) + +set(OPENVDB_LIBRARY_TOOLS_INCLUDE_FILES + tools/ChangeBackground.h + tools/Clip.h + tools/Composite.h + tools/Dense.h + tools/DenseSparseTools.h + tools/Diagnostics.h + tools/Filter.h + tools/FindActiveValues.h + tools/GridOperators.h + tools/GridTransformer.h + tools/Interpolation.h + tools/LevelSetAdvect.h + tools/LevelSetFilter.h + tools/LevelSetFracture.h + tools/LevelSetMeasure.h + tools/LevelSetMorph.h + tools/LevelSetPlatonic.h + tools/LevelSetRebuild.h + tools/LevelSetSphere.h + tools/LevelSetTracker.h + tools/LevelSetUtil.h + tools/Mask.h + tools/MeshToVolume.h + tools/Morphology.h + tools/MultiResGrid.h + tools/ParticleAtlas.h + tools/ParticlesToLevelSet.h + tools/PointAdvect.h + tools/PointIndexGrid.h + tools/PointPartitioner.h + tools/PointScatter.h + tools/PointsToMask.h + tools/PoissonSolver.h + tools/PotentialFlow.h + tools/Prune.h + tools/RayIntersector.h + tools/RayTracer.h + tools/SignedFloodFill.h + tools/Statistics.h + tools/TopologyToLevelSet.h + tools/ValueTransformer.h + tools/VectorTransformer.h + tools/VelocityFields.h + tools/VolumeAdvect.h + tools/VolumeToMesh.h + tools/VolumeToSpheres.h +) + +set(OPENVDB_LIBRARY_TREE_INCLUDE_FILES + tree/InternalNode.h + tree/Iterator.h + tree/LeafBuffer.h + tree/LeafManager.h + tree/LeafNode.h + tree/LeafNodeBool.h + tree/LeafNodeMask.h + tree/NodeManager.h + tree/NodeUnion.h + tree/RootNode.h + tree/Tree.h + tree/TreeIterator.h + tree/ValueAccessor.h +) + +set(OPENVDB_LIBRARY_UTIL_INCLUDE_FILES + util/CpuTimer.h + util/Formats.h + util/logging.h + util/MapsUtil.h + util/Name.h + util/NodeMasks.h + util/NullInterrupter.h + util/PagedArray.h + util/Util.h +) + +# @todo CMake >= 3.12, use an object library to consolidate shared/static +# builds. There are limitations with earlier versions of CMake when used with +# imported targets. + +if(OPENVDB_CORE_SHARED) + add_library(openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES}) +endif() + +if(OPENVDB_CORE_STATIC) + add_library(openvdb_static STATIC ${OPENVDB_LIBRARY_SOURCE_FILES}) +endif() + +# Alias either the shared or static library to the generic OpenVDB +# target. Dependent components should use this target to build against +# such that they are always able to find a valid build of OpenVDB + +if(OPENVDB_CORE_SHARED) + add_library(openvdb ALIAS openvdb_shared) +else() + add_library(openvdb ALIAS openvdb_static) +endif() + + +########################################################################## + +# Configure c flags + +set(OPENVDB_CORE_PRIVATE_DEFINES) +set(OPENVDB_CORE_PUBLIC_DEFINES) + +# Private defines (not inherited by dependent projects) + +list(APPEND OPENVDB_CORE_PRIVATE_DEFINES "-DOPENVDB_PRIVATE") +if(USE_BLOSC) + list(APPEND OPENVDB_CORE_PRIVATE_DEFINES "-DOPENVDB_USE_BLOSC") +endif() + +# Public defines + +if(USE_EXR) + list(APPEND OPENVDB_CORE_PUBLIC_DEFINES "-DOPENVDB_TOOLS_RAYTRACER_USE_EXR") +endif() +if(USE_LOG4CPLUS) + list(APPEND OPENVDB_CORE_PUBLIC_DEFINES "-DOPENVDB_USE_LOG4CPLUS") +endif() + +########################################################################## + +# Configure static build + +if(OPENVDB_CORE_STATIC) + target_include_directories(openvdb_static + PUBLIC ../ + PRIVATE . + ) + + target_compile_definitions(openvdb_static PRIVATE ${OPENVDB_CORE_PRIVATE_DEFINES}) + target_compile_definitions(openvdb_static PUBLIC ${OPENVDB_CORE_PUBLIC_DEFINES}) + + set_target_properties(openvdb_static + PROPERTIES OUTPUT_NAME ${OPENVDB_STATIC_LIBRARY_NAME} + ) + + if(WIN32) + set_target_properties(openvdb_static + PROPERTIES PREFIX "lib" + ) + endif() + + target_link_libraries(openvdb_static + ${OPENVDB_CORE_DEPENDENT_LIBS} + ) +endif() + +# Configure shared build + +if(OPENVDB_CORE_SHARED) + target_include_directories(openvdb_shared + PUBLIC ../ + PRIVATE . + ) + + target_compile_definitions(openvdb_shared PRIVATE ${OPENVDB_CORE_PRIVATE_DEFINES}) + target_compile_definitions(openvdb_shared PUBLIC ${OPENVDB_CORE_PUBLIC_DEFINES}) + + set_target_properties(openvdb_shared + PROPERTIES + OUTPUT_NAME ${OPENVDB_SHARED_LIBRARY_NAME} + SOVERSION ${OpenVDB_MAJOR_VERSION}.${OpenVDB_MINOR_VERSION} + VERSION ${OpenVDB_MAJOR_VERSION}.${OpenVDB_MINOR_VERSION}.${OpenVDB_PATCH_VERSION} + ) + + target_link_libraries(openvdb_shared + ${OPENVDB_CORE_DEPENDENT_LIBS} + ) + + if(OPENVDB_ENABLE_RPATH) + # @todo There is probably a better way to do this for imported targets + list(APPEND RPATHS + ${Boost_LIBRARY_DIRS} + ${IlmBase_LIBRARY_DIRS} + ${OpenEXR_LIBRARY_DIRS} + ${Log4cplus_LIBRARY_DIRS} + ${Blosc_LIBRARY_DIRS} + ${Tbb_LIBRARY_DIRS} + ) + list(REMOVE_DUPLICATES RPATHS) + set_target_properties(openvdb_shared + PROPERTIES INSTALL_RPATH "${RPATHS}" + ) + endif() +endif() + +unset(OPENVDB_CORE_PRIVATE_DEFINES) +unset(OPENVDB_CORE_PUBLIC_DEFINES) + +########################################################################## + +# Installation + +if(OPENVDB_CORE_STATIC) + install(TARGETS + openvdb_static + DESTINATION + ${CMAKE_INSTALL_LIBDIR} + ) +endif() + +if(OPENVDB_CORE_SHARED) + if(WIN32) + install(TARGETS + openvdb_shared + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Runtime + ) + else() + install(TARGETS + openvdb_shared + DESTINATION + ${CMAKE_INSTALL_LIBDIR} + ) + endif() +endif() + +install(FILES ${OPENVDB_LIBRARY_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}) +install(FILES ${OPENVDB_LIBRARY_IO_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/io) +install(FILES ${OPENVDB_LIBRARY_MATH_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/math) +install(FILES ${OPENVDB_LIBRARY_POINTS_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/points) +install(FILES ${OPENVDB_LIBRARY_TOOLS_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/tools) +install(FILES ${OPENVDB_LIBRARY_TREE_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/tree) +install(FILES ${OPENVDB_LIBRARY_UTIL_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/util) diff --git a/openvdb/COPYRIGHT b/openvdb/COPYRIGHT new file mode 100644 index 00000000..4f8f1897 --- /dev/null +++ b/openvdb/COPYRIGHT @@ -0,0 +1,2 @@ +Copyright Contributors to the OpenVDB Project +SPDX-License-Identifier: MPL-2.0 \ No newline at end of file diff --git a/openvdb/Exceptions.h b/openvdb/Exceptions.h new file mode 100644 index 00000000..68e4e593 --- /dev/null +++ b/openvdb/Exceptions.h @@ -0,0 +1,93 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_EXCEPTIONS_HAS_BEEN_INCLUDED +#define OPENVDB_EXCEPTIONS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +class OPENVDB_API Exception: public std::exception +{ +public: + Exception(const Exception&) = default; + Exception(Exception&&) = default; + Exception& operator=(const Exception&) = default; + Exception& operator=(Exception&&) = default; + ~Exception() override = default; + + const char* what() const noexcept override + { + try { return mMessage.c_str(); } catch (...) {} + return nullptr; + } + +protected: + Exception() noexcept {} + explicit Exception(const char* eType, const std::string* const msg = nullptr) noexcept + { + try { + if (eType) mMessage = eType; + if (msg) mMessage += ": " + (*msg); + } catch (...) {} + } + +private: + std::string mMessage; +}; + + +#define OPENVDB_EXCEPTION(_classname) \ +class OPENVDB_API _classname: public Exception \ +{ \ +public: \ + _classname() noexcept: Exception( #_classname ) {} \ + explicit _classname(const std::string& msg) noexcept: Exception( #_classname , &msg) {} \ +} + + +OPENVDB_EXCEPTION(ArithmeticError); +OPENVDB_EXCEPTION(IndexError); +OPENVDB_EXCEPTION(IoError); +OPENVDB_EXCEPTION(KeyError); +OPENVDB_EXCEPTION(LookupError); +OPENVDB_EXCEPTION(NotImplementedError); +OPENVDB_EXCEPTION(ReferenceError); +OPENVDB_EXCEPTION(RuntimeError); +OPENVDB_EXCEPTION(TypeError); +OPENVDB_EXCEPTION(ValueError); + +#undef OPENVDB_EXCEPTION + + +/// @deprecated Use ValueError instead. +class OPENVDB_API IllegalValueException: public Exception { +public: + OPENVDB_DEPRECATED IllegalValueException() noexcept: Exception("IllegalValueException") {} + OPENVDB_DEPRECATED explicit IllegalValueException(const std::string& msg) noexcept: + Exception("IllegalValueException", &msg) {} +}; + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#define OPENVDB_THROW(exception, message) \ +{ \ + std::string _openvdb_throw_msg; \ + try { \ + std::ostringstream _openvdb_throw_os; \ + _openvdb_throw_os << message; \ + _openvdb_throw_msg = _openvdb_throw_os.str(); \ + } catch (...) {} \ + throw exception(_openvdb_throw_msg); \ +} // OPENVDB_THROW + +#endif // OPENVDB_EXCEPTIONS_HAS_BEEN_INCLUDED diff --git a/openvdb/Grid.cc b/openvdb/Grid.cc new file mode 100644 index 00000000..45374efe --- /dev/null +++ b/openvdb/Grid.cc @@ -0,0 +1,433 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Grid.h" + +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +/// @note For Houdini compatibility, boolean-valued metadata names +/// should begin with "is_". +const char + * const GridBase::META_GRID_CLASS = "class", + * const GridBase::META_GRID_CREATOR = "creator", + * const GridBase::META_GRID_NAME = "name", + * const GridBase::META_SAVE_HALF_FLOAT = "is_saved_as_half_float", + * const GridBase::META_IS_LOCAL_SPACE = "is_local_space", + * const GridBase::META_VECTOR_TYPE = "vector_type", + * const GridBase::META_FILE_BBOX_MIN = "file_bbox_min", + * const GridBase::META_FILE_BBOX_MAX = "file_bbox_max", + * const GridBase::META_FILE_COMPRESSION = "file_compression", + * const GridBase::META_FILE_MEM_BYTES = "file_mem_bytes", + * const GridBase::META_FILE_VOXEL_COUNT = "file_voxel_count", + * const GridBase::META_FILE_DELAYED_LOAD = "file_delayed_load"; + + +//////////////////////////////////////// + + +namespace { + +using GridFactoryMap = std::map; +using GridFactoryMapCIter = GridFactoryMap::const_iterator; + +using Mutex = tbb::mutex; +using Lock = Mutex::scoped_lock; + +struct LockedGridRegistry { + LockedGridRegistry() {} + Mutex mMutex; + GridFactoryMap mMap; +}; + + +// Global function for accessing the registry +LockedGridRegistry* +getGridRegistry() +{ + static LockedGridRegistry registry; + return ®istry; +} + +} // unnamed namespace + + +bool +GridBase::isRegistered(const Name& name) +{ + LockedGridRegistry* registry = getGridRegistry(); + Lock lock(registry->mMutex); + + return (registry->mMap.find(name) != registry->mMap.end()); +} + + +void +GridBase::registerGrid(const Name& name, GridFactory factory) +{ + LockedGridRegistry* registry = getGridRegistry(); + Lock lock(registry->mMutex); + + if (registry->mMap.find(name) != registry->mMap.end()) { + OPENVDB_THROW(KeyError, "Grid type " << name << " is already registered"); + } + + registry->mMap[name] = factory; +} + + +void +GridBase::unregisterGrid(const Name& name) +{ + LockedGridRegistry* registry = getGridRegistry(); + Lock lock(registry->mMutex); + + registry->mMap.erase(name); +} + + +GridBase::Ptr +GridBase::createGrid(const Name& name) +{ + LockedGridRegistry* registry = getGridRegistry(); + Lock lock(registry->mMutex); + + GridFactoryMapCIter iter = registry->mMap.find(name); + + if (iter == registry->mMap.end()) { + OPENVDB_THROW(LookupError, "Cannot create grid of unregistered type " << name); + } + + return (iter->second)(); +} + + +void +GridBase::clearRegistry() +{ + LockedGridRegistry* registry = getGridRegistry(); + Lock lock(registry->mMutex); + + registry->mMap.clear(); +} + + +//////////////////////////////////////// + + +GridClass +GridBase::stringToGridClass(const std::string& s) +{ + GridClass ret = GRID_UNKNOWN; + std::string str = s; + boost::trim(str); + boost::to_lower(str); + if (str == gridClassToString(GRID_LEVEL_SET)) { + ret = GRID_LEVEL_SET; + } else if (str == gridClassToString(GRID_FOG_VOLUME)) { + ret = GRID_FOG_VOLUME; + } else if (str == gridClassToString(GRID_STAGGERED)) { + ret = GRID_STAGGERED; + } + return ret; +} + + +std::string +GridBase::gridClassToString(GridClass cls) +{ + std::string ret; + switch (cls) { + case GRID_UNKNOWN: ret = "unknown"; break; + case GRID_LEVEL_SET: ret = "level set"; break; + case GRID_FOG_VOLUME: ret = "fog volume"; break; + case GRID_STAGGERED: ret = "staggered"; break; + } + return ret; +} + +std::string +GridBase::gridClassToMenuName(GridClass cls) +{ + std::string ret; + switch (cls) { + case GRID_UNKNOWN: ret = "Other"; break; + case GRID_LEVEL_SET: ret = "Level Set"; break; + case GRID_FOG_VOLUME: ret = "Fog Volume"; break; + case GRID_STAGGERED: ret = "Staggered Vector Field"; break; + } + return ret; +} + + + +GridClass +GridBase::getGridClass() const +{ + GridClass cls = GRID_UNKNOWN; + if (StringMetadata::ConstPtr s = this->getMetadata(META_GRID_CLASS)) { + cls = stringToGridClass(s->value()); + } + return cls; +} + + +void +GridBase::setGridClass(GridClass cls) +{ + this->insertMeta(META_GRID_CLASS, StringMetadata(gridClassToString(cls))); +} + + +void +GridBase::clearGridClass() +{ + this->removeMeta(META_GRID_CLASS); +} + + +//////////////////////////////////////// + + +VecType +GridBase::stringToVecType(const std::string& s) +{ + VecType ret = VEC_INVARIANT; + std::string str = s; + boost::trim(str); + boost::to_lower(str); + if (str == vecTypeToString(VEC_COVARIANT)) { + ret = VEC_COVARIANT; + } else if (str == vecTypeToString(VEC_COVARIANT_NORMALIZE)) { + ret = VEC_COVARIANT_NORMALIZE; + } else if (str == vecTypeToString(VEC_CONTRAVARIANT_RELATIVE)) { + ret = VEC_CONTRAVARIANT_RELATIVE; + } else if (str == vecTypeToString(VEC_CONTRAVARIANT_ABSOLUTE)) { + ret = VEC_CONTRAVARIANT_ABSOLUTE; + } + return ret; +} + + +std::string +GridBase::vecTypeToString(VecType typ) +{ + std::string ret; + switch (typ) { + case VEC_INVARIANT: ret = "invariant"; break; + case VEC_COVARIANT: ret = "covariant"; break; + case VEC_COVARIANT_NORMALIZE: ret = "covariant normalize"; break; + case VEC_CONTRAVARIANT_RELATIVE: ret = "contravariant relative"; break; + case VEC_CONTRAVARIANT_ABSOLUTE: ret = "contravariant absolute"; break; + } + return ret; +} + + +std::string +GridBase::vecTypeExamples(VecType typ) +{ + std::string ret; + switch (typ) { + case VEC_INVARIANT: ret = "Tuple/Color/UVW"; break; + case VEC_COVARIANT: ret = "Gradient/Normal"; break; + case VEC_COVARIANT_NORMALIZE: ret = "Unit Normal"; break; + case VEC_CONTRAVARIANT_RELATIVE: ret = "Displacement/Velocity/Acceleration"; break; + case VEC_CONTRAVARIANT_ABSOLUTE: ret = "Position"; break; + } + return ret; +} + + +std::string +GridBase::vecTypeDescription(VecType typ) +{ + std::string ret; + switch (typ) { + case VEC_INVARIANT: + ret = "Does not transform"; + break; + case VEC_COVARIANT: + ret = "Apply the inverse-transpose transform matrix but ignore translation"; + break; + case VEC_COVARIANT_NORMALIZE: + ret = "Apply the inverse-transpose transform matrix but ignore translation" + " and renormalize vectors"; + break; + case VEC_CONTRAVARIANT_RELATIVE: + ret = "Apply the forward transform matrix but ignore translation"; + break; + case VEC_CONTRAVARIANT_ABSOLUTE: + ret = "Apply the forward transform matrix, including translation"; + break; + } + return ret; +} + + +VecType +GridBase::getVectorType() const +{ + VecType typ = VEC_INVARIANT; + if (StringMetadata::ConstPtr s = this->getMetadata(META_VECTOR_TYPE)) { + typ = stringToVecType(s->value()); + } + return typ; +} + + +void +GridBase::setVectorType(VecType typ) +{ + this->insertMeta(META_VECTOR_TYPE, StringMetadata(vecTypeToString(typ))); +} + + +void +GridBase::clearVectorType() +{ + this->removeMeta(META_VECTOR_TYPE); +} + + +//////////////////////////////////////// + + +std::string +GridBase::getName() const +{ + if (Metadata::ConstPtr meta = (*this)[META_GRID_NAME]) return meta->str(); + return ""; +} + + +void +GridBase::setName(const std::string& name) +{ + this->removeMeta(META_GRID_NAME); + this->insertMeta(META_GRID_NAME, StringMetadata(name)); +} + + +//////////////////////////////////////// + + +std::string +GridBase::getCreator() const +{ + if (Metadata::ConstPtr meta = (*this)[META_GRID_CREATOR]) return meta->str(); + return ""; +} + + +void +GridBase::setCreator(const std::string& creator) +{ + this->removeMeta(META_GRID_CREATOR); + this->insertMeta(META_GRID_CREATOR, StringMetadata(creator)); +} + + +//////////////////////////////////////// + + +bool +GridBase::saveFloatAsHalf() const +{ + if (Metadata::ConstPtr meta = (*this)[META_SAVE_HALF_FLOAT]) { + return meta->asBool(); + } + return false; +} + + +void +GridBase::setSaveFloatAsHalf(bool saveAsHalf) +{ + this->removeMeta(META_SAVE_HALF_FLOAT); + this->insertMeta(META_SAVE_HALF_FLOAT, BoolMetadata(saveAsHalf)); +} + + +//////////////////////////////////////// + + +bool +GridBase::isInWorldSpace() const +{ + bool local = false; + if (Metadata::ConstPtr meta = (*this)[META_IS_LOCAL_SPACE]) { + local = meta->asBool(); + } + return !local; +} + + +void +GridBase::setIsInWorldSpace(bool world) +{ + this->removeMeta(META_IS_LOCAL_SPACE); + this->insertMeta(META_IS_LOCAL_SPACE, BoolMetadata(!world)); +} + + +//////////////////////////////////////// + + +void +GridBase::addStatsMetadata() +{ + const CoordBBox bbox = this->evalActiveVoxelBoundingBox(); + this->removeMeta(META_FILE_BBOX_MIN); + this->removeMeta(META_FILE_BBOX_MAX); + this->removeMeta(META_FILE_MEM_BYTES); + this->removeMeta(META_FILE_VOXEL_COUNT); + this->insertMeta(META_FILE_BBOX_MIN, Vec3IMetadata(bbox.min().asVec3i())); + this->insertMeta(META_FILE_BBOX_MAX, Vec3IMetadata(bbox.max().asVec3i())); + this->insertMeta(META_FILE_MEM_BYTES, Int64Metadata(this->memUsage())); + this->insertMeta(META_FILE_VOXEL_COUNT, Int64Metadata(this->activeVoxelCount())); +} + + +MetaMap::Ptr +GridBase::getStatsMetadata() const +{ + const char* const fields[] = { + META_FILE_BBOX_MIN, + META_FILE_BBOX_MAX, + META_FILE_MEM_BYTES, + META_FILE_VOXEL_COUNT, + nullptr + }; + + /// @todo Check that the fields are of the correct type? + MetaMap::Ptr ret(new MetaMap); + for (int i = 0; fields[i] != nullptr; ++i) { + if (Metadata::ConstPtr m = (*this)[fields[i]]) { + ret->insertMeta(fields[i], *m); + } + } + return ret; +} + + +//////////////////////////////////////// + + +void +GridBase::clipGrid(const BBoxd& worldBBox) +{ + const CoordBBox indexBBox = + this->constTransform().worldToIndexNodeCentered(worldBBox); + this->clip(indexBBox); +} + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/Grid.h b/openvdb/Grid.h new file mode 100644 index 00000000..87d7fbe8 --- /dev/null +++ b/openvdb/Grid.h @@ -0,0 +1,1827 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_GRID_HAS_BEEN_INCLUDED +#define OPENVDB_GRID_HAS_BEEN_INCLUDED + +#include "Exceptions.h" +#include "MetaMap.h" +#include "Types.h" +#include "io/io.h" +#include "math/Transform.h" +#include "tree/Tree.h" +#include "util/logging.h" +#include "util/Name.h" +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +using TreeBase = tree::TreeBase; + +template class Grid; // forward declaration + + +/// @brief Create a new grid of type @c GridType with a given background value. +/// +/// @note Calling createGrid(background) is equivalent to calling +/// GridType::create(background). +template +inline typename GridType::Ptr createGrid(const typename GridType::ValueType& background); + + +/// @brief Create a new grid of type @c GridType with background value zero. +/// +/// @note Calling createGrid() is equivalent to calling GridType::create(). +template +inline typename GridType::Ptr createGrid(); + + +/// @brief Create a new grid of the appropriate type that wraps the given tree. +/// +/// @note This function can be called without specifying the template argument, +/// i.e., as createGrid(tree). +template +inline typename Grid::Ptr createGrid(TreePtrType); + + +/// @brief Create a new grid of type @c GridType classified as a "Level Set", +/// i.e., a narrow-band level set. +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +/// +/// @param voxelSize the size of a voxel in world units +/// @param halfWidth the half width of the narrow band in voxel units +/// +/// @details The voxel size and the narrow band half width define the grid's +/// background value as halfWidth*voxelWidth. The transform is linear +/// with a uniform scaling only corresponding to the specified voxel size. +/// +/// @note It is generally advisable to specify a half-width of the narrow band +/// that is larger than one voxel unit, otherwise zero crossings are not guaranteed. +template +typename GridType::Ptr createLevelSet( + Real voxelSize = 1.0, Real halfWidth = LEVEL_SET_HALF_WIDTH); + + +//////////////////////////////////////// + + +/// @brief Abstract base class for typed grids +class OPENVDB_API GridBase: public MetaMap +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + using GridFactory = Ptr (*)(); + + + ~GridBase() override {} + + + /// @name Copying + /// @{ + + /// @brief Return a new grid of the same type as this grid whose metadata is a + /// deep copy of this grid's and whose tree and transform are shared with this grid. + virtual GridBase::Ptr copyGrid() = 0; + /// @brief Return a new grid of the same type as this grid whose metadata is a + /// deep copy of this grid's and whose tree and transform are shared with this grid. + virtual GridBase::ConstPtr copyGrid() const = 0; + /// @brief Return a new grid of the same type as this grid whose metadata and + /// transform are deep copies of this grid's and whose tree is default-constructed. + virtual GridBase::Ptr copyGridWithNewTree() const = 0; + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + /// @brief Return a new grid of the same type as this grid whose tree and transform + /// is shared with this grid and whose metadata is provided as an argument. + virtual GridBase::ConstPtr copyGridReplacingMetadata(const MetaMap& meta) const = 0; + /// @brief Return a new grid of the same type as this grid whose tree is shared with + /// this grid, whose metadata is a deep copy of this grid's and whose transform is + /// provided as an argument. + /// @throw ValueError if the transform pointer is null + virtual GridBase::ConstPtr copyGridReplacingTransform(math::Transform::Ptr xform) const = 0; + /// @brief Return a new grid of the same type as this grid whose tree is shared with + /// this grid and whose transform and metadata are provided as arguments. + /// @throw ValueError if the transform pointer is null + virtual GridBase::ConstPtr copyGridReplacingMetadataAndTransform(const MetaMap& meta, + math::Transform::Ptr xform) const = 0; +#endif + + /// Return a new grid whose metadata, transform and tree are deep copies of this grid's. + virtual GridBase::Ptr deepCopyGrid() const = 0; + + /// @} + + + /// @name Registry + /// @{ + + /// Create a new grid of the given (registered) type. + static Ptr createGrid(const Name& type); + + /// Return @c true if the given grid type name is registered. + static bool isRegistered(const Name &type); + + /// Clear the grid type registry. + static void clearRegistry(); + + /// @} + + /// @name Type access + /// @{ + + /// Return the name of this grid's type. + virtual Name type() const = 0; + /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d"). + virtual Name valueType() const = 0; + + /// Return @c true if this grid is of the same type as the template parameter. + template + bool isType() const { return (this->type() == GridType::gridType()); } + + /// @} + + //@{ + /// @brief Return the result of downcasting a GridBase pointer to a Grid pointer + /// of the specified type, or return a null pointer if the types are incompatible. + template + static typename GridType::Ptr grid(const GridBase::Ptr&); + template + static typename GridType::ConstPtr grid(const GridBase::ConstPtr&); + template + static typename GridType::ConstPtr constGrid(const GridBase::Ptr&); + template + static typename GridType::ConstPtr constGrid(const GridBase::ConstPtr&); + //@} + + /// @name Tree + /// @{ + + /// @brief Return a pointer to this grid's tree, which might be + /// shared with other grids. The pointer is guaranteed to be non-null. + TreeBase::Ptr baseTreePtr(); + /// @brief Return a pointer to this grid's tree, which might be + /// shared with other grids. The pointer is guaranteed to be non-null. + TreeBase::ConstPtr baseTreePtr() const { return this->constBaseTreePtr(); } + /// @brief Return a pointer to this grid's tree, which might be + /// shared with other grids. The pointer is guaranteed to be non-null. + virtual TreeBase::ConstPtr constBaseTreePtr() const = 0; + + /// @brief Return a reference to this grid's tree, which might be + /// shared with other grids. + /// @note Calling @vdblink::GridBase::setTree() setTree@endlink + /// on this grid invalidates all references previously returned by this method. + TreeBase& baseTree() { return const_cast(this->constBaseTree()); } + /// @brief Return a reference to this grid's tree, which might be + /// shared with other grids. + /// @note Calling @vdblink::GridBase::setTree() setTree@endlink + /// on this grid invalidates all references previously returned by this method. + const TreeBase& baseTree() const { return this->constBaseTree(); } + /// @brief Return a reference to this grid's tree, which might be + /// shared with other grids. + /// @note Calling @vdblink::GridBase::setTree() setTree@endlink + /// on this grid invalidates all references previously returned by this method. + const TreeBase& constBaseTree() const { return *(this->constBaseTreePtr()); } + + /// @brief Associate the given tree with this grid, in place of its existing tree. + /// @throw ValueError if the tree pointer is null + /// @throw TypeError if the tree is not of the appropriate type + /// @note Invalidates all references previously returned by + /// @vdblink::GridBase::baseTree() baseTree@endlink + /// or @vdblink::GridBase::constBaseTree() constBaseTree@endlink. + virtual void setTree(TreeBase::Ptr) = 0; + + /// Set a new tree with the same background value as the previous tree. + virtual void newTree() = 0; + + /// @} + + /// Return @c true if this grid contains only background voxels. + virtual bool empty() const = 0; + /// Empty this grid, setting all voxels to the background. + virtual void clear() = 0; + + + /// @name Tools + /// @{ + + /// @brief Reduce the memory footprint of this grid by increasing its sparseness + /// either losslessly (@a tolerance = 0) or lossily (@a tolerance > 0). + /// @details With @a tolerance > 0, sparsify regions where voxels have the same + /// active state and have values that differ by no more than the tolerance + /// (converted to this grid's value type). + virtual void pruneGrid(float tolerance = 0.0) = 0; + + /// @brief Clip this grid to the given world-space bounding box. + /// @details Voxels that lie outside the bounding box are set to the background. + /// @warning Clipping a level set will likely produce a grid that is + /// no longer a valid level set. + void clipGrid(const BBoxd&); + + /// @brief Clip this grid to the given index-space bounding box. + /// @details Voxels that lie outside the bounding box are set to the background. + /// @warning Clipping a level set will likely produce a grid that is + /// no longer a valid level set. + virtual void clip(const CoordBBox&) = 0; + + /// @} + + /// @{ + /// @brief If this grid resolves to one of the listed grid types, + /// invoke the given functor on the resolved grid. + /// @return @c false if this grid's type is not one of the listed types + /// + /// @par Example: + /// @code + /// using AllowedGridTypes = openvdb::TypeList< + /// openvdb::Int32Grid, openvdb::Int64Grid, + /// openvdb::FloatGrid, openvdb::DoubleGrid>; + /// + /// const openvdb::CoordBBox bbox{ + /// openvdb::Coord{0,0,0}, openvdb::Coord{10,10,10}}; + /// + /// // Fill the grid if it is one of the allowed types. + /// myGridBasePtr->apply( + /// [&bbox](auto& grid) { // C++14 + /// using GridType = typename std::decay::type; + /// grid.fill(bbox, typename GridType::ValueType(1)); + /// } + /// ); + /// @endcode + /// + /// @see @vdblink::TypeList TypeList@endlink + template inline bool apply(OpT&) const; + template inline bool apply(OpT&); + template inline bool apply(const OpT&) const; + template inline bool apply(const OpT&); + /// @} + + /// @name Metadata + /// @{ + + /// Return this grid's user-specified name. + std::string getName() const; + /// Specify a name for this grid. + void setName(const std::string&); + + /// Return the user-specified description of this grid's creator. + std::string getCreator() const; + /// Provide a description of this grid's creator. + void setCreator(const std::string&); + + /// @brief Return @c true if this grid should be written out with floating-point + /// voxel values (including components of vectors) quantized to 16 bits. + bool saveFloatAsHalf() const; + void setSaveFloatAsHalf(bool); + + /// @brief Return the class of volumetric data (level set, fog volume, etc.) + /// that is stored in this grid. + /// @sa gridClassToString, gridClassToMenuName, stringToGridClass + GridClass getGridClass() const; + /// @brief Specify the class of volumetric data (level set, fog volume, etc.) + /// that is stored in this grid. + /// @sa gridClassToString, gridClassToMenuName, stringToGridClass + void setGridClass(GridClass); + /// Remove the setting specifying the class of this grid's volumetric data. + void clearGridClass(); + + /// @} + + /// Return the metadata string value for the given class of volumetric data. + static std::string gridClassToString(GridClass); + /// Return a formatted string version of the grid class. + static std::string gridClassToMenuName(GridClass); + /// @brief Return the class of volumetric data specified by the given string. + /// @details If the string is not one of the ones returned by + /// @vdblink::GridBase::gridClassToString() gridClassToString@endlink, + /// return @c GRID_UNKNOWN. + static GridClass stringToGridClass(const std::string&); + + /// @name Metadata + /// @{ + + /// @brief Return the type of vector data (invariant, covariant, etc.) stored + /// in this grid, assuming that this grid contains a vector-valued tree. + /// @sa vecTypeToString, vecTypeExamples, vecTypeDescription, stringToVecType + VecType getVectorType() const; + /// @brief Specify the type of vector data (invariant, covariant, etc.) stored + /// in this grid, assuming that this grid contains a vector-valued tree. + /// @sa vecTypeToString, vecTypeExamples, vecTypeDescription, stringToVecType + void setVectorType(VecType); + /// Remove the setting specifying the type of vector data stored in this grid. + void clearVectorType(); + + /// @} + + /// Return the metadata string value for the given type of vector data. + static std::string vecTypeToString(VecType); + /// Return a string listing examples of the given type of vector data + /// (e.g., "Gradient/Normal", given VEC_COVARIANT). + static std::string vecTypeExamples(VecType); + /// @brief Return a string describing how the given type of vector data is affected + /// by transformations (e.g., "Does not transform", given VEC_INVARIANT). + static std::string vecTypeDescription(VecType); + static VecType stringToVecType(const std::string&); + + /// @name Metadata + /// @{ + + /// Return @c true if this grid's voxel values are in world space and should be + /// affected by transformations, @c false if they are in local space and should + /// not be affected by transformations. + bool isInWorldSpace() const; + /// Specify whether this grid's voxel values are in world space or in local space. + void setIsInWorldSpace(bool); + + /// @} + + // Standard metadata field names + // (These fields should normally not be accessed directly, but rather + // via the accessor methods above, when available.) + // Note: Visual C++ requires these declarations to be separate statements. + static const char* const META_GRID_CLASS; + static const char* const META_GRID_CREATOR; + static const char* const META_GRID_NAME; + static const char* const META_SAVE_HALF_FLOAT; + static const char* const META_IS_LOCAL_SPACE; + static const char* const META_VECTOR_TYPE; + static const char* const META_FILE_BBOX_MIN; + static const char* const META_FILE_BBOX_MAX; + static const char* const META_FILE_COMPRESSION; + static const char* const META_FILE_MEM_BYTES; + static const char* const META_FILE_VOXEL_COUNT; + static const char* const META_FILE_DELAYED_LOAD; + + + /// @name Statistics + /// @{ + + /// Return the number of active voxels. + virtual Index64 activeVoxelCount() const = 0; + + /// Return the axis-aligned bounding box of all active voxels. If + /// the grid is empty a default bbox is returned. + virtual CoordBBox evalActiveVoxelBoundingBox() const = 0; + + /// Return the dimensions of the axis-aligned bounding box of all active voxels. + virtual Coord evalActiveVoxelDim() const = 0; + + /// Return the number of bytes of memory used by this grid. + virtual Index64 memUsage() const = 0; + + /// @brief Add metadata to this grid comprising the current values + /// of statistics like the active voxel count and bounding box. + /// @note This metadata is not automatically kept up-to-date with + /// changes to this grid. + void addStatsMetadata(); + /// @brief Return a new MetaMap containing just the metadata that + /// was added to this grid with @vdblink::GridBase::addStatsMetadata() + /// addStatsMetadata@endlink. + /// @details If @vdblink::GridBase::addStatsMetadata() addStatsMetadata@endlink + /// was never called on this grid, return an empty MetaMap. + MetaMap::Ptr getStatsMetadata() const; + + /// @} + + + /// @name Transform + /// @{ + + //@{ + /// @brief Return a pointer to this grid's transform, which might be + /// shared with other grids. + math::Transform::Ptr transformPtr() { return mTransform; } + math::Transform::ConstPtr transformPtr() const { return mTransform; } + math::Transform::ConstPtr constTransformPtr() const { return mTransform; } + //@} + //@{ + /// @brief Return a reference to this grid's transform, which might be + /// shared with other grids. + /// @note Calling @vdblink::GridBase::setTransform() setTransform@endlink + /// on this grid invalidates all references previously returned by this method. + math::Transform& transform() { return *mTransform; } + const math::Transform& transform() const { return *mTransform; } + const math::Transform& constTransform() const { return *mTransform; } + //@} + + /// @} + + /// @name Transform + /// @{ + + /// @brief Associate the given transform with this grid, in place of + /// its existing transform. + /// @throw ValueError if the transform pointer is null + /// @note Invalidates all references previously returned by + /// @vdblink::GridBase::transform() transform@endlink + /// or @vdblink::GridBase::constTransform() constTransform@endlink. + void setTransform(math::Transform::Ptr); + + /// Return the size of this grid's voxels. + Vec3d voxelSize() const { return transform().voxelSize(); } + /// @brief Return the size of this grid's voxel at position (x, y, z). + /// @note Frustum and perspective transforms have position-dependent voxel size. + Vec3d voxelSize(const Vec3d& xyz) const { return transform().voxelSize(xyz); } + /// Return true if the voxels in world space are uniformly sized cubes + bool hasUniformVoxels() const { return mTransform->hasUniformScale(); } + /// Apply this grid's transform to the given coordinates. + Vec3d indexToWorld(const Vec3d& xyz) const { return transform().indexToWorld(xyz); } + /// Apply this grid's transform to the given coordinates. + Vec3d indexToWorld(const Coord& ijk) const { return transform().indexToWorld(ijk); } + /// Apply the inverse of this grid's transform to the given coordinates. + Vec3d worldToIndex(const Vec3d& xyz) const { return transform().worldToIndex(xyz); } + + /// @} + + + /// @name I/O + /// @{ + + /// @brief Read the grid topology from a stream. + /// This will read only the grid structure, not the actual data buffers. + virtual void readTopology(std::istream&) = 0; + /// @brief Write the grid topology to a stream. + /// This will write only the grid structure, not the actual data buffers. + virtual void writeTopology(std::ostream&) const = 0; + + /// Read all data buffers for this grid. + virtual void readBuffers(std::istream&) = 0; + /// Read all of this grid's data buffers that intersect the given index-space bounding box. + virtual void readBuffers(std::istream&, const CoordBBox&) = 0; + /// @brief Read all of this grid's data buffers that are not yet resident in memory + /// (because delayed loading is in effect). + /// @details If this grid was read from a memory-mapped file, this operation + /// disconnects the grid from the file. + /// @sa io::File::open, io::MappedFile + virtual void readNonresidentBuffers() const = 0; + /// Write out all data buffers for this grid. + virtual void writeBuffers(std::ostream&) const = 0; + + /// Read in the transform for this grid. + void readTransform(std::istream& is) { transform().read(is); } + /// Write out the transform for this grid. + void writeTransform(std::ostream& os) const { transform().write(os); } + + /// Output a human-readable description of this grid. + virtual void print(std::ostream& = std::cout, int verboseLevel = 1) const = 0; + + /// @} + + +protected: + /// @brief Initialize with an identity linear transform. + GridBase(): mTransform(math::Transform::createLinearTransform()) {} + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + /// @brief Initialize with metadata and a transform. + /// @throw ValueError if the transform pointer is null + GridBase(const MetaMap& meta, math::Transform::Ptr xform); +#endif + + /// @brief Deep copy another grid's metadata and transform. + GridBase(const GridBase& other): MetaMap(other), mTransform(other.mTransform->copy()) {} + + /// @brief Copy another grid's metadata but share its transform. + GridBase(GridBase& other, ShallowCopy): MetaMap(other), mTransform(other.mTransform) {} + + /// Register a grid type along with a factory function. + static void registerGrid(const Name& type, GridFactory); + /// Remove a grid type from the registry. + static void unregisterGrid(const Name& type); + + +private: + math::Transform::Ptr mTransform; +}; // class GridBase + + +//////////////////////////////////////// + + +using GridPtrVec = std::vector; +using GridPtrVecIter = GridPtrVec::iterator; +using GridPtrVecCIter = GridPtrVec::const_iterator; +using GridPtrVecPtr = SharedPtr; + +using GridCPtrVec = std::vector; +using GridCPtrVecIter = GridCPtrVec::iterator; +using GridCPtrVecCIter = GridCPtrVec::const_iterator; +using GridCPtrVecPtr = SharedPtr; + +using GridPtrSet = std::set; +using GridPtrSetIter = GridPtrSet::iterator; +using GridPtrSetCIter = GridPtrSet::const_iterator; +using GridPtrSetPtr = SharedPtr; + +using GridCPtrSet = std::set; +using GridCPtrSetIter = GridCPtrSet::iterator; +using GridCPtrSetCIter = GridCPtrSet::const_iterator; +using GridCPtrSetPtr = SharedPtr; + + +/// @brief Predicate functor that returns @c true for grids that have a specified name +struct OPENVDB_API GridNamePred +{ + GridNamePred(const Name& _name): name(_name) {} + bool operator()(const GridBase::ConstPtr& g) const { return g && g->getName() == name; } + Name name; +}; + +/// Return the first grid in the given container whose name is @a name. +template +inline typename GridPtrContainerT::value_type +findGridByName(const GridPtrContainerT& container, const Name& name) +{ + using GridPtrT = typename GridPtrContainerT::value_type; + typename GridPtrContainerT::const_iterator it = + std::find_if(container.begin(), container.end(), GridNamePred(name)); + return (it == container.end() ? GridPtrT() : *it); +} + +/// Return the first grid in the given map whose name is @a name. +template +inline GridPtrT +findGridByName(const std::map& container, const Name& name) +{ + using GridPtrMapT = std::map; + for (typename GridPtrMapT::const_iterator it = container.begin(), end = container.end(); + it != end; ++it) + { + const GridPtrT& grid = it->second; + if (grid && grid->getName() == name) return grid; + } + return GridPtrT(); +} +//@} + + +//////////////////////////////////////// + + +/// @brief Container class that associates a tree with a transform and metadata +template +class Grid: public GridBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + using TreeType = _TreeType; + using TreePtrType = typename _TreeType::Ptr; + using ConstTreePtrType = typename _TreeType::ConstPtr; + using ValueType = typename _TreeType::ValueType; + using BuildType = typename _TreeType::BuildType; + + using ValueOnIter = typename _TreeType::ValueOnIter; + using ValueOnCIter = typename _TreeType::ValueOnCIter; + using ValueOffIter = typename _TreeType::ValueOffIter; + using ValueOffCIter = typename _TreeType::ValueOffCIter; + using ValueAllIter = typename _TreeType::ValueAllIter; + using ValueAllCIter = typename _TreeType::ValueAllCIter; + + using Accessor = typename tree::ValueAccessor<_TreeType, true>; + using ConstAccessor = typename tree::ValueAccessor; + using UnsafeAccessor = typename tree::ValueAccessor<_TreeType, false>; + using ConstUnsafeAccessor = typename tree::ValueAccessor; + + /// @brief ValueConverter::Type is the type of a grid having the same + /// hierarchy as this grid but a different value type, T. + /// + /// For example, FloatGrid::ValueConverter::Type is equivalent to DoubleGrid. + /// @note If the source grid type is a template argument, it might be necessary + /// to write "typename SourceGrid::template ValueConverter::Type". + template + struct ValueConverter { + using Type = Grid::Type>; + }; + + /// Return a new grid with the given background value. + static Ptr create(const ValueType& background); + /// Return a new grid with background value zero. + static Ptr create(); + /// @brief Return a new grid that contains the given tree. + /// @throw ValueError if the tree pointer is null + static Ptr create(TreePtrType); + /// @brief Return a new, empty grid with the same transform and metadata as the + /// given grid and with background value zero. + static Ptr create(const GridBase& other); + + + /// Construct a new grid with background value zero. + Grid(); + /// Construct a new grid with the given background value. + explicit Grid(const ValueType& background); + /// @brief Construct a new grid that shares the given tree and associates with it + /// an identity linear transform. + /// @throw ValueError if the tree pointer is null + explicit Grid(TreePtrType); + /// Deep copy another grid's metadata, transform and tree. + Grid(const Grid&); + /// @brief Deep copy the metadata, transform and tree of another grid whose tree + /// configuration is the same as this grid's but whose value type is different. + /// Cast the other grid's values to this grid's value type. + /// @throw TypeError if the other grid's tree configuration doesn't match this grid's + /// or if this grid's ValueType is not constructible from the other grid's ValueType. + template + explicit Grid(const Grid&); + /// Deep copy another grid's metadata and transform, but share its tree. + Grid(Grid&, ShallowCopy); + /// @brief Deep copy another grid's metadata and transform, but construct a new tree + /// with background value zero. + explicit Grid(const GridBase&); + + ~Grid() override {} + + /// Disallow assignment, since it wouldn't be obvious whether the copy is deep or shallow. + Grid& operator=(const Grid&) = delete; + + /// @name Copying + /// @{ + + /// @brief Return a new grid of the same type as this grid whose metadata and + /// transform are deep copies of this grid's and whose tree is shared with this grid. + Ptr copy(); + /// @brief Return a new grid of the same type as this grid whose metadata and + /// transform are deep copies of this grid's and whose tree is shared with this grid. + ConstPtr copy() const; + /// @brief Return a new grid of the same type as this grid whose metadata and + /// transform are deep copies of this grid's and whose tree is default-constructed. + Ptr copyWithNewTree() const; + + /// @brief Return a new grid of the same type as this grid whose metadata is a + /// deep copy of this grid's and whose tree and transform are shared with this grid. + GridBase::Ptr copyGrid() override; + /// @brief Return a new grid of the same type as this grid whose metadata is a + /// deep copy of this grid's and whose tree and transform are shared with this grid. + GridBase::ConstPtr copyGrid() const override; + /// @brief Return a new grid of the same type as this grid whose metadata and + /// transform are deep copies of this grid's and whose tree is default-constructed. + GridBase::Ptr copyGridWithNewTree() const override; + //@} + + /// @name Copying + /// @{ + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + /// @brief Return a new grid of the same type as this grid whose tree and transform + /// is shared with this grid and whose metadata is provided as an argument. + ConstPtr copyReplacingMetadata(const MetaMap& meta) const; + /// @brief Return a new grid of the same type as this grid whose tree is shared with + /// this grid, whose metadata is a deep copy of this grid's and whose transform is + /// provided as an argument. + /// @throw ValueError if the transform pointer is null + ConstPtr copyReplacingTransform(math::Transform::Ptr xform) const; + /// @brief Return a new grid of the same type as this grid whose tree is shared with + /// this grid and whose transform and metadata are provided as arguments. + /// @throw ValueError if the transform pointer is null + ConstPtr copyReplacingMetadataAndTransform(const MetaMap& meta, + math::Transform::Ptr xform) const; + + /// @brief Return a new grid of the same type as this grid whose tree and transform + /// is shared with this grid and whose metadata is provided as an argument. + GridBase::ConstPtr copyGridReplacingMetadata(const MetaMap& meta) const override; + /// @brief Return a new grid of the same type as this grid whose tree is shared with + /// this grid, whose metadata is a deep copy of this grid's and whose transform is + /// provided as an argument. + /// @throw ValueError if the transform pointer is null + GridBase::ConstPtr copyGridReplacingTransform(math::Transform::Ptr xform) const override; + /// @brief Return a new grid of the same type as this grid whose tree is shared with + /// this grid and whose transform and metadata are provided as arguments. + /// @throw ValueError if the transform pointer is null + GridBase::ConstPtr copyGridReplacingMetadataAndTransform(const MetaMap& meta, + math::Transform::Ptr xform) const override; +#endif + + /// @brief Return a new grid whose metadata, transform and tree are deep copies of this grid's. + Ptr deepCopy() const { return Ptr(new Grid(*this)); } + /// @brief Return a new grid whose metadata, transform and tree are deep copies of this grid's. + GridBase::Ptr deepCopyGrid() const override { return this->deepCopy(); } + + //@} + + + /// Return the name of this grid's type. + Name type() const override { return this->gridType(); } + /// Return the name of this type of grid. + static Name gridType() { return TreeType::treeType(); } + + /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d"). + Name valueType() const override { return tree().valueType(); } + + + /// @name Voxel access + /// @{ + + /// @brief Return this grid's background value. + /// @note Use tools::changeBackground to efficiently modify the background value. + const ValueType& background() const { return mTree->background(); } + + /// Return @c true if this grid contains only inactive background voxels. + bool empty() const override { return tree().empty(); } + /// Empty this grid, so that all voxels become inactive background voxels. + void clear() override { tree().clear(); } + + /// @brief Return an accessor that provides random read and write access + /// to this grid's voxels. + /// @details The accessor is safe in the sense that it is registered with this grid's tree. + Accessor getAccessor() { return Accessor(tree()); } + /// @brief Return an unsafe accessor that provides random read and write access + /// to this grid's voxels. + /// @details The accessor is unsafe in the sense that it is not registered + /// with this grid's tree. In some rare cases this can give a performance advantage + /// over a registered accessor, but it is unsafe if the tree topology is modified. + /// @warning Only use this method if you're an expert and know the + /// risks of using an unregistered accessor (see tree/ValueAccessor.h) + UnsafeAccessor getUnsafeAccessor() { return UnsafeAccessor(tree()); } + /// Return an accessor that provides random read-only access to this grid's voxels. + ConstAccessor getAccessor() const { return ConstAccessor(tree()); } + /// Return an accessor that provides random read-only access to this grid's voxels. + ConstAccessor getConstAccessor() const { return ConstAccessor(tree()); } + /// @brief Return an unsafe accessor that provides random read-only access + /// to this grid's voxels. + /// @details The accessor is unsafe in the sense that it is not registered + /// with this grid's tree. In some rare cases this can give a performance advantage + /// over a registered accessor, but it is unsafe if the tree topology is modified. + /// @warning Only use this method if you're an expert and know the + /// risks of using an unregistered accessor (see tree/ValueAccessor.h) + ConstUnsafeAccessor getConstUnsafeAccessor() const { return ConstUnsafeAccessor(tree()); } + + /// Return an iterator over all of this grid's active values (tile and voxel). + ValueOnIter beginValueOn() { return tree().beginValueOn(); } + /// Return an iterator over all of this grid's active values (tile and voxel). + ValueOnCIter beginValueOn() const { return tree().cbeginValueOn(); } + /// Return an iterator over all of this grid's active values (tile and voxel). + ValueOnCIter cbeginValueOn() const { return tree().cbeginValueOn(); } + /// Return an iterator over all of this grid's inactive values (tile and voxel). + ValueOffIter beginValueOff() { return tree().beginValueOff(); } + /// Return an iterator over all of this grid's inactive values (tile and voxel). + ValueOffCIter beginValueOff() const { return tree().cbeginValueOff(); } + /// Return an iterator over all of this grid's inactive values (tile and voxel). + ValueOffCIter cbeginValueOff() const { return tree().cbeginValueOff(); } + /// Return an iterator over all of this grid's values (tile and voxel). + ValueAllIter beginValueAll() { return tree().beginValueAll(); } + /// Return an iterator over all of this grid's values (tile and voxel). + ValueAllCIter beginValueAll() const { return tree().cbeginValueAll(); } + /// Return an iterator over all of this grid's values (tile and voxel). + ValueAllCIter cbeginValueAll() const { return tree().cbeginValueAll(); } + + /// @} + + /// @name Tools + /// @{ + + /// @brief Set all voxels within a given axis-aligned box to a constant value. + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box + /// @param value the value to which to set voxels within the box + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive + /// @note This operation generates a sparse, but not always optimally sparse, + /// representation of the filled box. Follow fill operations with a prune() + /// operation for optimal sparseness. + void sparseFill(const CoordBBox& bbox, const ValueType& value, bool active = true); + /// @brief Set all voxels within a given axis-aligned box to a constant value. + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box + /// @param value the value to which to set voxels within the box + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive + /// @note This operation generates a sparse, but not always optimally sparse, + /// representation of the filled box. Follow fill operations with a prune() + /// operation for optimal sparseness. + void fill(const CoordBBox& bbox, const ValueType& value, bool active = true); + + /// @brief Set all voxels within a given axis-aligned box to a constant value + /// and ensure that those voxels are all represented at the leaf level. + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box. + /// @param value the value to which to set voxels within the box. + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive. + void denseFill(const CoordBBox& bbox, const ValueType& value, bool active = true); + + /// Reduce the memory footprint of this grid by increasing its sparseness. + void pruneGrid(float tolerance = 0.0) override; + + /// @brief Clip this grid to the given index-space bounding box. + /// @details Voxels that lie outside the bounding box are set to the background. + /// @warning Clipping a level set will likely produce a grid that is + /// no longer a valid level set. + void clip(const CoordBBox&) override; + + /// @brief Efficiently merge another grid into this grid using one of several schemes. + /// @details This operation is primarily intended to combine grids that are mostly + /// non-overlapping (for example, intermediate grids from computations that are + /// parallelized across disjoint regions of space). + /// @warning This operation always empties the other grid. + void merge(Grid& other, MergePolicy policy = MERGE_ACTIVE_STATES); + + /// @brief Union this grid's set of active values with the active values + /// of the other grid, whose value type may be different. + /// @details The resulting state of a value is active if the corresponding value + /// was already active OR if it is active in the other grid. Also, a resulting + /// value maps to a voxel if the corresponding value already mapped to a voxel + /// OR if it is a voxel in the other grid. Thus, a resulting value can only + /// map to a tile if the corresponding value already mapped to a tile + /// AND if it is a tile value in the other grid. + /// + /// @note This operation modifies only active states, not values. + /// Specifically, active tiles and voxels in this grid are not changed, and + /// tiles or voxels that were inactive in this grid but active in the other grid + /// are marked as active in this grid but left with their original values. + template + void topologyUnion(const Grid& other); + + /// @brief Intersect this grid's set of active values with the active values + /// of the other grid, whose value type may be different. + /// @details The resulting state of a value is active only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches of this grid that overlap with + /// inactive tiles in the other grid. Also, because it can deactivate voxels, + /// it can create leaf nodes with no active values. Thus, it is recommended + /// to prune this grid after calling this method. + template + void topologyIntersection(const Grid& other); + + /// @brief Difference this grid's set of active values with the active values + /// of the other grid, whose value type may be different. + /// @details After this method is called, voxels in this grid will be active + /// only if they were active to begin with and if the corresponding voxels + /// in the other grid were inactive. + /// + /// @note This operation can delete branches of this grid that overlap with + /// active tiles in the other grid. Also, because it can deactivate voxels, + /// it can create leaf nodes with no active values. Thus, it is recommended + /// to prune this grid after calling this method. + template + void topologyDifference(const Grid& other); + + /// @} + + /// @name Statistics + /// @{ + + /// Return the number of active voxels. + Index64 activeVoxelCount() const override { return tree().activeVoxelCount(); } + /// Return the axis-aligned bounding box of all active voxels. + CoordBBox evalActiveVoxelBoundingBox() const override; + /// Return the dimensions of the axis-aligned bounding box of all active voxels. + Coord evalActiveVoxelDim() const override; + /// Return the minimum and maximum active values in this grid. + void evalMinMax(ValueType& minVal, ValueType& maxVal) const; + + /// Return the number of bytes of memory used by this grid. + /// @todo Add transform().memUsage() + Index64 memUsage() const override { return tree().memUsage(); } + + /// @} + + + /// @name Tree + /// @{ + + //@{ + /// @brief Return a pointer to this grid's tree, which might be + /// shared with other grids. The pointer is guaranteed to be non-null. + TreePtrType treePtr() { return mTree; } + ConstTreePtrType treePtr() const { return mTree; } + ConstTreePtrType constTreePtr() const { return mTree; } + TreeBase::ConstPtr constBaseTreePtr() const override { return mTree; } + //@} + //@{ + /// @brief Return a reference to this grid's tree, which might be + /// shared with other grids. + /// @note Calling setTree() on this grid invalidates all references + /// previously returned by this method. + TreeType& tree() { return *mTree; } + const TreeType& tree() const { return *mTree; } + const TreeType& constTree() const { return *mTree; } + //@} + + /// @} + + /// @name Tree + /// @{ + + /// @brief Associate the given tree with this grid, in place of its existing tree. + /// @throw ValueError if the tree pointer is null + /// @throw TypeError if the tree is not of type TreeType + /// @note Invalidates all references previously returned by baseTree(), + /// constBaseTree(), tree() or constTree(). + void setTree(TreeBase::Ptr) override; + + /// @brief Associate a new, empty tree with this grid, in place of its existing tree. + /// @note The new tree has the same background value as the existing tree. + void newTree() override; + + /// @} + + + /// @name I/O + /// @{ + + /// @brief Read the grid topology from a stream. + /// This will read only the grid structure, not the actual data buffers. + void readTopology(std::istream&) override; + /// @brief Write the grid topology to a stream. + /// This will write only the grid structure, not the actual data buffers. + void writeTopology(std::ostream&) const override; + + /// Read all data buffers for this grid. + void readBuffers(std::istream&) override; + /// Read all of this grid's data buffers that intersect the given index-space bounding box. + void readBuffers(std::istream&, const CoordBBox&) override; + /// @brief Read all of this grid's data buffers that are not yet resident in memory + /// (because delayed loading is in effect). + /// @details If this grid was read from a memory-mapped file, this operation + /// disconnects the grid from the file. + /// @sa io::File::open, io::MappedFile + void readNonresidentBuffers() const override; + /// Write out all data buffers for this grid. + void writeBuffers(std::ostream&) const override; + + /// Output a human-readable description of this grid. + void print(std::ostream& = std::cout, int verboseLevel = 1) const override; + + /// @} + + /// @brief Return @c true if grids of this type require multiple I/O passes + /// to read and write data buffers. + /// @sa HasMultiPassIO + static inline bool hasMultiPassIO(); + + + /// @name Registry + /// @{ + + /// Return @c true if this grid type is registered. + static bool isRegistered() { return GridBase::isRegistered(Grid::gridType()); } + /// Register this grid type along with a factory function. + static void registerGrid() + { + GridBase::registerGrid(Grid::gridType(), Grid::factory); + if (!tree::internal::LeafBufferFlags::IsAtomic) { + OPENVDB_LOG_WARN("delayed loading of grids of type " << Grid::gridType() + << " might not be threadsafe on this platform"); + } + } + /// Remove this grid type from the registry. + static void unregisterGrid() { GridBase::unregisterGrid(Grid::gridType()); } + + /// @} + + +private: +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + /// Deep copy metadata, but share tree and transform. + Grid(TreePtrType tree, const MetaMap& meta, math::Transform::Ptr xform); +#endif + + /// Helper function for use with registerGrid() + static GridBase::Ptr factory() { return Grid::create(); } + + TreePtrType mTree; +}; // class Grid + + +//////////////////////////////////////// + + +/// @brief Cast a generic grid pointer to a pointer to a grid of a concrete class. +/// +/// Return a null pointer if the input pointer is null or if it +/// points to a grid that is not of type @c GridType. +/// +/// @note Calling gridPtrCast(grid) is equivalent to calling +/// GridBase::grid(grid). +template +inline typename GridType::Ptr +gridPtrCast(const GridBase::Ptr& grid) +{ + return GridBase::grid(grid); +} + + +/// @brief Cast a generic const grid pointer to a const pointer to a grid +/// of a concrete class. +/// +/// Return a null pointer if the input pointer is null or if it +/// points to a grid that is not of type @c GridType. +/// +/// @note Calling gridConstPtrCast(grid) is equivalent to calling +/// GridBase::constGrid(grid). +template +inline typename GridType::ConstPtr +gridConstPtrCast(const GridBase::ConstPtr& grid) +{ + return GridBase::constGrid(grid); +} + + +//////////////////////////////////////// + + +/// @{ +/// @brief Return a pointer to a deep copy of the given grid, provided that +/// the grid's concrete type is @c GridType. +/// +/// Return a null pointer if the input pointer is null or if it +/// points to a grid that is not of type @c GridType. +template +inline typename GridType::Ptr +deepCopyTypedGrid(const GridBase::ConstPtr& grid) +{ + if (!grid || !grid->isType()) return typename GridType::Ptr(); + return gridPtrCast(grid->deepCopyGrid()); +} + + +template +inline typename GridType::Ptr +deepCopyTypedGrid(const GridBase& grid) +{ + if (!grid.isType()) return typename GridType::Ptr(); + return gridPtrCast(grid.deepCopyGrid()); +} +/// @} + + +//////////////////////////////////////// + + +//@{ +/// @brief This adapter allows code that is templated on a Tree type to +/// accept either a Tree type or a Grid type. +template +struct TreeAdapter +{ + using TreeType = _TreeType; + using NonConstTreeType = typename std::remove_const::type; + using TreePtrType = typename TreeType::Ptr; + using ConstTreePtrType = typename TreeType::ConstPtr; + using NonConstTreePtrType = typename NonConstTreeType::Ptr; + using GridType = Grid; + using NonConstGridType = Grid; + using GridPtrType = typename GridType::Ptr; + using NonConstGridPtrType = typename NonConstGridType::Ptr; + using ConstGridPtrType = typename GridType::ConstPtr; + using ValueType = typename TreeType::ValueType; + using AccessorType = typename tree::ValueAccessor; + using ConstAccessorType = typename tree::ValueAccessor; + using NonConstAccessorType = typename tree::ValueAccessor; + + static TreeType& tree(TreeType& t) { return t; } + static TreeType& tree(GridType& g) { return g.tree(); } + static const TreeType& tree(const TreeType& t) { return t; } + static const TreeType& tree(const GridType& g) { return g.tree(); } + static const TreeType& constTree(TreeType& t) { return t; } + static const TreeType& constTree(GridType& g) { return g.constTree(); } + static const TreeType& constTree(const TreeType& t) { return t; } + static const TreeType& constTree(const GridType& g) { return g.constTree(); } +}; + + +/// Partial specialization for Grid types +template +struct TreeAdapter > +{ + using TreeType = _TreeType; + using NonConstTreeType = typename std::remove_const::type; + using TreePtrType = typename TreeType::Ptr; + using ConstTreePtrType = typename TreeType::ConstPtr; + using NonConstTreePtrType = typename NonConstTreeType::Ptr; + using GridType = Grid; + using NonConstGridType = Grid; + using GridPtrType = typename GridType::Ptr; + using NonConstGridPtrType = typename NonConstGridType::Ptr; + using ConstGridPtrType = typename GridType::ConstPtr; + using ValueType = typename TreeType::ValueType; + using AccessorType = typename tree::ValueAccessor; + using ConstAccessorType = typename tree::ValueAccessor; + using NonConstAccessorType = typename tree::ValueAccessor; + + static TreeType& tree(TreeType& t) { return t; } + static TreeType& tree(GridType& g) { return g.tree(); } + static const TreeType& tree(const TreeType& t) { return t; } + static const TreeType& tree(const GridType& g) { return g.tree(); } + static const TreeType& constTree(TreeType& t) { return t; } + static const TreeType& constTree(GridType& g) { return g.constTree(); } + static const TreeType& constTree(const TreeType& t) { return t; } + static const TreeType& constTree(const GridType& g) { return g.constTree(); } +}; + +/// Partial specialization for ValueAccessor types +template +struct TreeAdapter > +{ + using TreeType = _TreeType; + using NonConstTreeType = typename std::remove_const::type; + using TreePtrType = typename TreeType::Ptr; + using ConstTreePtrType = typename TreeType::ConstPtr; + using NonConstTreePtrType = typename NonConstTreeType::Ptr; + using GridType = Grid; + using NonConstGridType = Grid; + using GridPtrType = typename GridType::Ptr; + using NonConstGridPtrType = typename NonConstGridType::Ptr; + using ConstGridPtrType = typename GridType::ConstPtr; + using ValueType = typename TreeType::ValueType; + using AccessorType = typename tree::ValueAccessor; + using ConstAccessorType = typename tree::ValueAccessor; + using NonConstAccessorType = typename tree::ValueAccessor; + + static TreeType& tree(TreeType& t) { return t; } + static TreeType& tree(GridType& g) { return g.tree(); } + static TreeType& tree(AccessorType& a) { return a.tree(); } + static const TreeType& tree(const TreeType& t) { return t; } + static const TreeType& tree(const GridType& g) { return g.tree(); } + static const TreeType& tree(const AccessorType& a) { return a.tree(); } + static const TreeType& constTree(TreeType& t) { return t; } + static const TreeType& constTree(GridType& g) { return g.constTree(); } + static const TreeType& constTree(const TreeType& t) { return t; } + static const TreeType& constTree(const GridType& g) { return g.constTree(); } +}; + +//@} + + +//////////////////////////////////////// + + +/// @brief Metafunction that specifies whether a given leaf node, tree, or grid type +/// requires multiple passes to read and write voxel data +/// @details Multi-pass I/O allows one to optimize the data layout of leaf nodes +/// for certain access patterns during delayed loading. +/// @sa io::MultiPass +template +struct HasMultiPassIO { + static const bool value = std::is_base_of::value; +}; + +// Partial specialization for Tree types +template +struct HasMultiPassIO> { + // A tree is multi-pass if its (root node's) leaf node type is multi-pass. + static const bool value = HasMultiPassIO::value; +}; + +// Partial specialization for Grid types +template +struct HasMultiPassIO> { + // A grid is multi-pass if its tree's leaf node type is multi-pass. + static const bool value = HasMultiPassIO::value; +}; + + +//////////////////////////////////////// + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 +inline GridBase::GridBase(const MetaMap& meta, math::Transform::Ptr xform) + : MetaMap(meta) + , mTransform(xform) +{ + if (!xform) OPENVDB_THROW(ValueError, "Transform pointer is null"); +} +#endif + +template +inline typename GridType::Ptr +GridBase::grid(const GridBase::Ptr& grid) +{ + // The string comparison on type names is slower than a dynamic pointer cast, but + // it is safer when pointers cross DSO boundaries, as they do in many Houdini nodes. + if (grid && grid->type() == GridType::gridType()) { + return StaticPtrCast(grid); + } + return typename GridType::Ptr(); +} + + +template +inline typename GridType::ConstPtr +GridBase::grid(const GridBase::ConstPtr& grid) +{ + return ConstPtrCast( + GridBase::grid(ConstPtrCast(grid))); +} + + +template +inline typename GridType::ConstPtr +GridBase::constGrid(const GridBase::Ptr& grid) +{ + return ConstPtrCast(GridBase::grid(grid)); +} + + +template +inline typename GridType::ConstPtr +GridBase::constGrid(const GridBase::ConstPtr& grid) +{ + return ConstPtrCast( + GridBase::grid(ConstPtrCast(grid))); +} + + +inline TreeBase::Ptr +GridBase::baseTreePtr() +{ + return ConstPtrCast(this->constBaseTreePtr()); +} + + +inline void +GridBase::setTransform(math::Transform::Ptr xform) +{ + if (!xform) OPENVDB_THROW(ValueError, "Transform pointer is null"); + mTransform = xform; +} + + +//////////////////////////////////////// + + +template +inline Grid::Grid(): mTree(new TreeType) +{ +} + + +template +inline Grid::Grid(const ValueType &background): mTree(new TreeType(background)) +{ +} + + +template +inline Grid::Grid(TreePtrType tree): mTree(tree) +{ + if (!tree) OPENVDB_THROW(ValueError, "Tree pointer is null"); +} + + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 +template +inline Grid::Grid(TreePtrType tree, const MetaMap& meta, math::Transform::Ptr xform): + GridBase(meta, xform), + mTree(tree) +{ + if (!tree) OPENVDB_THROW(ValueError, "Tree pointer is null"); +} +#endif + + +template +inline Grid::Grid(const Grid& other): + GridBase(other), + mTree(StaticPtrCast(other.mTree->copy())) +{ +} + + +template +template +inline Grid::Grid(const Grid& other): + GridBase(other), + mTree(new TreeType(other.constTree())) +{ +} + + +template +inline Grid::Grid(Grid& other, ShallowCopy): + GridBase(other), + mTree(other.mTree) +{ +} + + +template +inline Grid::Grid(const GridBase& other): + GridBase(other), + mTree(new TreeType) +{ +} + + +//static +template +inline typename Grid::Ptr +Grid::create() +{ + return Grid::create(zeroVal()); +} + + +//static +template +inline typename Grid::Ptr +Grid::create(const ValueType& background) +{ + return Ptr(new Grid(background)); +} + + +//static +template +inline typename Grid::Ptr +Grid::create(TreePtrType tree) +{ + return Ptr(new Grid(tree)); +} + + +//static +template +inline typename Grid::Ptr +Grid::create(const GridBase& other) +{ + return Ptr(new Grid(other)); +} + + +//////////////////////////////////////// + + +template +inline typename Grid::ConstPtr +Grid::copy() const +{ + return ConstPtr{new Grid{*const_cast(this), ShallowCopy{}}}; +} + + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 +template +inline typename Grid::ConstPtr +Grid::copyReplacingMetadata(const MetaMap& meta) const +{ + math::Transform::Ptr transformPtr = ConstPtrCast( + this->constTransformPtr()); + TreePtrType treePtr = ConstPtrCast(this->constTreePtr()); + return ConstPtr{new Grid{treePtr, meta, transformPtr}}; +} + +template +inline typename Grid::ConstPtr +Grid::copyReplacingTransform(math::Transform::Ptr xform) const +{ + return this->copyReplacingMetadataAndTransform(*this, xform); +} + +template +inline typename Grid::ConstPtr +Grid::copyReplacingMetadataAndTransform(const MetaMap& meta, + math::Transform::Ptr xform) const +{ + TreePtrType treePtr = ConstPtrCast(this->constTreePtr()); + return ConstPtr{new Grid{treePtr, meta, xform}}; +} +#endif + + +template +inline typename Grid::Ptr +Grid::copy() +{ + return Ptr{new Grid{*this, ShallowCopy{}}}; +} + + +template +inline typename Grid::Ptr +Grid::copyWithNewTree() const +{ + Ptr result{new Grid{*const_cast(this), ShallowCopy{}}}; + result->newTree(); + return result; +} + + +template +inline GridBase::Ptr +Grid::copyGrid() +{ + return this->copy(); +} + +template +inline GridBase::ConstPtr +Grid::copyGrid() const +{ + return this->copy(); +} + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 +template +inline GridBase::ConstPtr +Grid::copyGridReplacingMetadata(const MetaMap& meta) const +{ + return this->copyReplacingMetadata(meta); +} + +template +inline GridBase::ConstPtr +Grid::copyGridReplacingTransform(math::Transform::Ptr xform) const +{ + return this->copyReplacingTransform(xform); +} + +template +inline GridBase::ConstPtr +Grid::copyGridReplacingMetadataAndTransform(const MetaMap& meta, + math::Transform::Ptr xform) const +{ + return this->copyReplacingMetadataAndTransform(meta, xform); +} +#endif + +template +inline GridBase::Ptr +Grid::copyGridWithNewTree() const +{ + return this->copyWithNewTree(); +} + + +//////////////////////////////////////// + + +template +inline void +Grid::setTree(TreeBase::Ptr tree) +{ + if (!tree) OPENVDB_THROW(ValueError, "Tree pointer is null"); + if (tree->type() != TreeType::treeType()) { + OPENVDB_THROW(TypeError, "Cannot assign a tree of type " + + tree->type() + " to a grid of type " + this->type()); + } + mTree = StaticPtrCast(tree); +} + + +template +inline void +Grid::newTree() +{ + mTree.reset(new TreeType(this->background())); +} + + +//////////////////////////////////////// + + +template +inline void +Grid::sparseFill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + tree().sparseFill(bbox, value, active); +} + + +template +inline void +Grid::fill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + this->sparseFill(bbox, value, active); +} + +template +inline void +Grid::denseFill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + tree().denseFill(bbox, value, active); +} + +template +inline void +Grid::pruneGrid(float tolerance) +{ + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto value = zeroVal() + tolerance; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + this->tree().prune(static_cast(value)); +} + +template +inline void +Grid::clip(const CoordBBox& bbox) +{ + tree().clip(bbox); +} + +template +inline void +Grid::merge(Grid& other, MergePolicy policy) +{ + tree().merge(other.tree(), policy); +} + + +template +template +inline void +Grid::topologyUnion(const Grid& other) +{ + tree().topologyUnion(other.tree()); +} + + +template +template +inline void +Grid::topologyIntersection(const Grid& other) +{ + tree().topologyIntersection(other.tree()); +} + + +template +template +inline void +Grid::topologyDifference(const Grid& other) +{ + tree().topologyDifference(other.tree()); +} + + +//////////////////////////////////////// + + +template +inline void +Grid::evalMinMax(ValueType& minVal, ValueType& maxVal) const +{ + tree().evalMinMax(minVal, maxVal); +} + + +template +inline CoordBBox +Grid::evalActiveVoxelBoundingBox() const +{ + CoordBBox bbox; + tree().evalActiveVoxelBoundingBox(bbox); + return bbox; +} + + +template +inline Coord +Grid::evalActiveVoxelDim() const +{ + Coord dim; + const bool nonempty = tree().evalActiveVoxelDim(dim); + return (nonempty ? dim : Coord()); +} + + +//////////////////////////////////////// + + +/// @internal Consider using the stream tagging mechanism (see io::Archive) +/// to specify the float precision, but note that the setting is per-grid. + +template +inline void +Grid::readTopology(std::istream& is) +{ + tree().readTopology(is, saveFloatAsHalf()); +} + + +template +inline void +Grid::writeTopology(std::ostream& os) const +{ + tree().writeTopology(os, saveFloatAsHalf()); +} + + +template +inline void +Grid::readBuffers(std::istream& is) +{ + if (!hasMultiPassIO() || (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_MULTIPASS_IO)) { + tree().readBuffers(is, saveFloatAsHalf()); + } else { + uint16_t numPasses = 1; + is.read(reinterpret_cast(&numPasses), sizeof(uint16_t)); + const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(is); + assert(bool(meta)); + for (uint16_t passIndex = 0; passIndex < numPasses; ++passIndex) { + uint32_t pass = (uint32_t(numPasses) << 16) | uint32_t(passIndex); + meta->setPass(pass); + tree().readBuffers(is, saveFloatAsHalf()); + } + } +} + + +/// @todo Refactor this and the readBuffers() above +/// once support for ABI 2 compatibility is dropped. +template +inline void +Grid::readBuffers(std::istream& is, const CoordBBox& bbox) +{ + if (!hasMultiPassIO() || (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_MULTIPASS_IO)) { + tree().readBuffers(is, bbox, saveFloatAsHalf()); + } else { + uint16_t numPasses = 1; + is.read(reinterpret_cast(&numPasses), sizeof(uint16_t)); + const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(is); + assert(bool(meta)); + for (uint16_t passIndex = 0; passIndex < numPasses; ++passIndex) { + uint32_t pass = (uint32_t(numPasses) << 16) | uint32_t(passIndex); + meta->setPass(pass); + tree().readBuffers(is, saveFloatAsHalf()); + } + // Cannot clip inside readBuffers() when using multiple passes, + // so instead clip afterwards. + tree().clip(bbox); + } +} + + +template +inline void +Grid::readNonresidentBuffers() const +{ + tree().readNonresidentBuffers(); +} + + +template +inline void +Grid::writeBuffers(std::ostream& os) const +{ + if (!hasMultiPassIO()) { + tree().writeBuffers(os, saveFloatAsHalf()); + } else { + // Determine how many leaf buffer passes are required for this grid + const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(os); + assert(bool(meta)); + uint16_t numPasses = 1; + meta->setCountingPasses(true); + meta->setPass(0); + tree().writeBuffers(os, saveFloatAsHalf()); + numPasses = static_cast(meta->pass()); + os.write(reinterpret_cast(&numPasses), sizeof(uint16_t)); + meta->setCountingPasses(false); + + // Save out the data blocks of the grid. + for (uint16_t passIndex = 0; passIndex < numPasses; ++passIndex) { + uint32_t pass = (uint32_t(numPasses) << 16) | uint32_t(passIndex); + meta->setPass(pass); + tree().writeBuffers(os, saveFloatAsHalf()); + } + } +} + + +//static +template +inline bool +Grid::hasMultiPassIO() +{ + return HasMultiPassIO::value; +} + + +template +inline void +Grid::print(std::ostream& os, int verboseLevel) const +{ + tree().print(os, verboseLevel); + + if (metaCount() > 0) { + os << "Additional metadata:" << std::endl; + for (ConstMetaIterator it = beginMeta(), end = endMeta(); it != end; ++it) { + os << " " << it->first; + if (it->second) { + const std::string value = it->second->str(); + if (!value.empty()) os << ": " << value; + } + os << "\n"; + } + } + + os << "Transform:" << std::endl; + transform().print(os, /*indent=*/" "); + os << std::endl; +} + + +//////////////////////////////////////// + + +template +inline typename GridType::Ptr +createGrid(const typename GridType::ValueType& background) +{ + return GridType::create(background); +} + + +template +inline typename GridType::Ptr +createGrid() +{ + return GridType::create(); +} + + +template +inline typename Grid::Ptr +createGrid(TreePtrType tree) +{ + using TreeType = typename TreePtrType::element_type; + return Grid::create(tree); +} + + +template +typename GridType::Ptr +createLevelSet(Real voxelSize, Real halfWidth) +{ + using ValueType = typename GridType::ValueType; + + // GridType::ValueType is required to be a floating-point scalar. + static_assert(std::is_floating_point::value, + "level-set grids must be floating-point-valued"); + + typename GridType::Ptr grid = GridType::create( + /*background=*/static_cast(voxelSize * halfWidth)); + grid->setTransform(math::Transform::createLinearTransform(voxelSize)); + grid->setGridClass(GRID_LEVEL_SET); + return grid; +} + + +//////////////////////////////////////// + + +namespace internal { + +/// @private +template +struct GridApplyImpl { static bool apply(GridBaseT&, OpT&) { return false; } }; + +// Partial specialization for (nonempty) TypeLists +/// @private +template +struct GridApplyImpl> +{ + static bool apply(GridBaseT& grid, OpT& op) + { + if (grid.template isType()) { + op(static_cast::Type&>(grid)); + return true; + } + return GridApplyImpl>::apply(grid, op); + } +}; + +} // namespace internal + + +template +inline bool +GridBase::apply(OpT& op) const +{ + return internal::GridApplyImpl::apply(*this, op); +} + +template +inline bool +GridBase::apply(OpT& op) +{ + return internal::GridApplyImpl::apply(*this, op); +} + +template +inline bool +GridBase::apply(const OpT& op) const +{ + return internal::GridApplyImpl::apply(*this, op); +} + +template +inline bool +GridBase::apply(const OpT& op) +{ + return internal::GridApplyImpl::apply(*this, op); +} + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_GRID_HAS_BEEN_INCLUDED diff --git a/openvdb/LICENSE b/openvdb/LICENSE new file mode 100644 index 00000000..a612ad98 --- /dev/null +++ b/openvdb/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/openvdb/Makefile b/openvdb/Makefile new file mode 100644 index 00000000..71f3d8ef --- /dev/null +++ b/openvdb/Makefile @@ -0,0 +1,1057 @@ +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: MPL-2.0 +# +# Makefile for the OpenVDB library + +# See INSTALL for a list of requirements. +# +# Targets: +# lib the OpenVDB library +# +# doc HTML documentation (doc/html/index.html) +# pdfdoc PDF documentation (doc/latex/refman.pdf; +# requires LaTeX and ghostscript) +# python OpenVDB Python module +# pytest unit tests for the Python module +# pydoc HTML documentation for the Python module +# (doc/html/python/index.html) +# vdb_lod command-line tool to generate volume mipmaps +# vdb_print command-line tool to inspect OpenVDB files +# vdb_render command-line tool to ray-trace OpenVDB files +# vdb_view command-line tool to view OpenVDB files +# vdb_test unit tests for the OpenVDB library +# +# all [default target] all of the above +# install install all of the above except vdb_test +# into subdirectories of DESTDIR +# install_lib install just the OpenVDB library and its headers +# into subdirectories of DESTDIR +# depend recompute source file header dependencies +# header_test check for missing or indirectly included headers +# clean delete generated files from the local directory +# test run tests +# +# Options: +# abi=N build for compatibility with version N of the +# OpenVDB Grid ABI, where N is 2, 3, 4, etc. +# (some newer features will be disabled) +# shared=no link executables against static OpenVDB libraries +# (default: link against shared libraries) +# debug=yes build with debugging symbols and without optimization +# verbose=yes run commands (e.g., doxygen) in verbose mode +# strict=yes Enable a collection of pre defined compiler warnings +# for GCC and clang + + +# +# The following variables must be defined, either here or on the command line +# (e.g., "make install DESTDIR=/usr/local"): +# +# Note that if you plan to build the Houdini OpenVDB tools (distributed +# separately), you must build the OpenVDB library and the Houdini tools +# against compatible versions of the Boost, OpenEXR and TBB libraries. +# Until Houdini 16.5, all three were included in the HDK, so the relevant +# variables below point by default to the HDK library and header directories: +# $(HDSO) and $(HT)/include, respectively. (Source the houdini_setup script +# to set those two environment variables.) You must provide your own +# distribution of Boost. +# +# To build the OpenVDB Python module, you will need local distributions of +# Python, Boost.Python, and optionally NumPy. The Houdini HDK includes +# Python 2.x, but not the libboost_python library or NumPy or the Boost.Python +# headers, so both Boost.Python and NumPy have to be built separately. Point +# the variables $(BOOST_PYTHON_LIB_DIR), $(BOOST_PYTHON_LIB) and +# $(NUMPY_INCL_DIR) below to your local distributions of those libraries. +# + +# The directory into which to install libraries, executables and header files +DESTDIR := /tmp/OpenVDB + +# The directory into which to install libraries (e.g., for Linux multiarch support) +DESTDIR_LIB_DIR := $(DESTDIR)/lib + +# The parent directory of the boost/ header directory +BOOST_INCL_DIR := $(HT)/include +# The directory containing libboost_iostreams, libboost_system, etc. +BOOST_LIB_DIR := $(HDSO) +BOOST_LIB := -lboost_iostreams -lboost_system +BOOST_THREAD_LIB := -lboost_thread + +# The parent directory of the OpenEXR/ header directory +EXR_INCL_DIR := $(HT)/include +# The directory containing IlmImf +EXR_LIB_DIR := $(HDSO) +EXR_LIB := -lIlmImf + +# The parent directory of the OpenEXR/ header directory (which contains half.h) +ILMBASE_INCL_DIR := $(EXR_INCL_DIR) +# The directory containing libIlmThread, libIlmThread, libHalf etc. +ILMBASE_LIB_DIR := $(EXR_LIB_DIR) +ILMBASE_LIB := -lIlmThread -lIex -lImath +HALF_LIB := -lHalf + +# The parent directory of the tbb/ header directory +TBB_INCL_DIR := $(HT)/include +# The directory containing libtbb +TBB_LIB_DIR := $(HDSO) +TBB_LIB := -ltbb + +# The parent directory of the blosc.h header +# (leave blank if Blosc is unavailable) +BLOSC_INCL_DIR := $(HT)/include +# The directory containing libblosc +BLOSC_LIB_DIR := $(HDSO) +BLOSC_LIB := -lblosc + +# A scalable, concurrent malloc replacement library +# such as jemalloc (included in the Houdini HDK) or TBB malloc +# (leave blank if unavailable) +CONCURRENT_MALLOC_LIB := -ljemalloc +#CONCURRENT_MALLOC_LIB := -ltbbmalloc_proxy -ltbbmalloc +# The directory containing the malloc replacement library +CONCURRENT_MALLOC_LIB_DIR := $(HDSO) + +# The parent directory of the cppunit/ header directory +# (leave blank if CppUnit is unavailable) +CPPUNIT_INCL_DIR := +# The directory containing libcppunit +CPPUNIT_LIB_DIR := +CPPUNIT_LIB := -lcppunit + +# The parent directory of the log4cplus/ header directory +# (leave blank if log4cplus is unavailable) +LOG4CPLUS_INCL_DIR := /rel/folio/log4cplus/log4cplus-1.1.2-latest/sys_include +# The directory containing liblog4cplus +LOG4CPLUS_LIB_DIR := /rel/folio/log4cplus/log4cplus-1.1.2-latest/library/c++11 +LOG4CPLUS_LIB := -llog4cplus + +# The directory containing glfw.h +# (leave blank if GLFW is unavailable) +GLFW_INCL_DIR := /rel/third_party/glfw/glfw-3.0.1/include +# The directory containing libglfw +GLFW_LIB_DIR := /rel/third_party/glfw/glfw-3.0.1/lib +GLFW_LIB := -lglfw +# The major version number of the GLFW library +# (header filenames changed between GLFW 2 and 3, so this must be specified explicitly) +GLFW_MAJOR_VERSION := 3 + +# The version of Python for which to build the OpenVDB module +# (leave blank if Python is unavailable) +PYTHON_VERSION := 2.7 +# The directory containing Python.h +PYTHON_INCL_DIR := $(HFS)/python/include/python$(PYTHON_VERSION) +# The directory containing pyconfig.h +PYCONFIG_INCL_DIR := $(PYTHON_INCL_DIR) +# The directory containing libpython +PYTHON_LIB_DIR := $(HFS)/python/lib +PYTHON_LIB := -lpython$(PYTHON_VERSION) +# The directory containing libboost_python +BOOST_PYTHON_LIB_DIR := /rel/config/studio/baseline/boost/1.55.0.0/gcc-4.8.1.0/python-2.7.10.0/ext/lib/ +BOOST_PYTHON_LIB := -lboost_python-mt +# The directory containing arrayobject.h +# (leave blank if NumPy is unavailable) +NUMPY_INCL_DIR := /rel/lang/python/2.7.9-1/lib/python2.7/site-packages/numpy/core/include/numpy/ +# The Epydoc executable +# (leave blank if Epydoc is unavailable) +EPYDOC := /rel/map/generic_default-2014.24.237/bin/epydoc +# Set PYTHON_WRAP_ALL_GRID_TYPES to "yes" to specify that the Python module +# should expose (almost) all of the grid types defined in openvdb.h +# Otherwise, only FloatGrid, BoolGrid and Vec3SGrid will be exposed +# (see, e.g., exportIntGrid() in python/pyIntGrid.cc). +# Compiling the Python module with PYTHON_WRAP_ALL_GRID_TYPES set to "yes" +# can be very memory-intensive. +PYTHON_WRAP_ALL_GRID_TYPES := no + +# The Doxygen executable +# (leave blank if Doxygen is unavailable) +DOXYGEN := doxygen + + +# +# Ideally, users shouldn't need to change anything below this line. +# + +SHELL = /bin/bash + +# Turn off implicit rules for speed. +.SUFFIXES: + +ifneq (,$(INSTALL_DIR)) + $(warning Warning: $$(INSTALL_DIR) is no longer used; set $$(DESTDIR) instead.) +endif + +# Determine the platform. +ifeq ("$(OS)","Windows_NT") + WINDOWS_NT := 1 +else + UNAME_S := $(shell uname -s) + ifeq ("$(UNAME_S)","Linux") + LINUX := 1 + else + ifeq ("$(UNAME_S)","Darwin") + MBSD := 1 + endif + endif +endif + +ifeq (yes,$(strip $(debug))) + OPTIMIZE := -g +else + OPTIMIZE := -O3 -DNDEBUG +endif + +ifeq (yes,$(strip $(verbose))) + QUIET := + QUIET_TEST := -v +else + QUIET := > /dev/null + QUIET_TEST := $(QUIET) +endif + +has_blosc := no +ifneq (,$(and $(BLOSC_LIB_DIR),$(BLOSC_INCL_DIR),$(BLOSC_LIB))) + has_blosc := yes +endif + +has_cppunit := no +ifneq (,$(and $(CPPUNIT_INCL_DIR),$(CPPUNIT_LIB_DIR),$(CPPUNIT_LIB))) + has_cppunit := yes +endif + +has_glfw := no +ifneq (,$(and $(GLFW_LIB_DIR),$(GLFW_INCL_DIR),$(GLFW_LIB))) + has_glfw := yes +endif + +has_log4cplus := no +ifneq (,$(and $(LOG4CPLUS_LIB_DIR),$(LOG4CPLUS_INCL_DIR),$(LOG4CPLUS_LIB))) + has_log4cplus := yes +endif + +has_python := no +ifneq (,$(and $(PYTHON_VERSION),$(PYTHON_LIB_DIR),$(PYTHON_INCL_DIR), \ + $(PYCONFIG_INCL_DIR),$(PYTHON_LIB),$(BOOST_PYTHON_LIB_DIR),$(BOOST_PYTHON_LIB))) + has_python := yes +endif + +INCLDIRS := -I . -I .. -isystem $(BOOST_INCL_DIR) -isystem $(ILMBASE_INCL_DIR) -isystem $(TBB_INCL_DIR) +ifeq (yes,$(has_blosc)) + INCLDIRS += -isystem $(BLOSC_INCL_DIR) +endif +ifeq (yes,$(has_log4cplus)) + INCLDIRS += -isystem $(LOG4CPLUS_INCL_DIR) +endif + +CXXFLAGS += -std=c++11 + +CXXFLAGS += -pthread $(OPTIMIZE) $(INCLDIRS) +ifeq (yes,$(has_blosc)) + CXXFLAGS += -DOPENVDB_USE_BLOSC +endif +ifeq (yes,$(has_log4cplus)) + CXXFLAGS += -DOPENVDB_USE_LOG4CPLUS +endif +abi := $(strip $(abi)) +ifneq (,$(abi)) + CXXFLAGS += -DOPENVDB_3_ABI_COMPATIBLE # TODO: deprecated + CXXFLAGS += -DOPENVDB_ABI_VERSION_NUMBER=$(abi) +endif +ifneq (2,$(strip $(GLFW_MAJOR_VERSION))) + CXXFLAGS += -DOPENVDB_USE_GLFW_3 +endif + +ifeq (yes,$(strip $(strict))) + USING_CLANG=$(shell ${CXX} --version | grep clang) + USING_GCC=$(shell ${CXX} --version | grep GCC) + ifneq (,$(USING_CLANG)) + CXXFLAGS += \ + -Werror \ + -Wall \ + -Wextra \ + -Wconversion \ + -Wno-sign-conversion \ + # + else ifneq (,$(USING_GCC)) + CXXFLAGS += \ + -Werror \ + -Wall \ + -Wextra \ + -pedantic \ + -Wcast-align \ + -Wcast-qual \ + -Wconversion \ + -Wdisabled-optimization \ + -Woverloaded-virtual \ + # + endif +endif + +LIBS := \ + -ldl -lm -lz \ + -L$(ILMBASE_LIB_DIR) $(HALF_LIB) \ + -L$(TBB_LIB_DIR) $(TBB_LIB) \ + -L$(BOOST_LIB_DIR) $(BOOST_LIB) \ +# +LIBS_RPATH := \ + -ldl -lm -lz \ + -Wl,-rpath,$(ILMBASE_LIB_DIR) -L$(ILMBASE_LIB_DIR) $(HALF_LIB) \ + -Wl,-rpath,$(TBB_LIB_DIR) -L$(TBB_LIB_DIR) $(TBB_LIB) \ + -Wl,-rpath,$(BOOST_LIB_DIR) -L$(BOOST_LIB_DIR) $(BOOST_LIB) \ +# +ifeq (yes,$(has_blosc)) + LIBS += -L$(BLOSC_LIB_DIR) $(BLOSC_LIB) + LIBS_RPATH += -Wl,-rpath,$(BLOSC_LIB_DIR) -L$(BLOSC_LIB_DIR) $(BLOSC_LIB) +endif +ifeq (yes,$(has_log4cplus)) + LIBS += -L$(LOG4CPLUS_LIB_DIR) $(LOG4CPLUS_LIB) + LIBS_RPATH += -Wl,-rpath,$(LOG4CPLUS_LIB_DIR) -L$(LOG4CPLUS_LIB_DIR) $(LOG4CPLUS_LIB) +endif +ifneq (,$(strip $(CONCURRENT_MALLOC_LIB))) +ifneq (,$(strip $(CONCURRENT_MALLOC_LIB_DIR))) + LIBS_RPATH += -Wl,-rpath,$(CONCURRENT_MALLOC_LIB_DIR) -L$(CONCURRENT_MALLOC_LIB_DIR) +endif +endif +ifdef LINUX + LIBS += -lrt + LIBS_RPATH += -lrt +endif + +INCLUDE_NAMES := \ + Exceptions.h \ + Grid.h \ + io/Archive.h \ + io/Compression.h \ + io/DelayedLoadMetadata.h \ + io/File.h \ + io/GridDescriptor.h \ + io/io.h \ + io/Queue.h \ + io/Stream.h \ + io/TempFile.h \ + math/BBox.h \ + math/ConjGradient.h \ + math/Coord.h \ + math/DDA.h \ + math/FiniteDifference.h \ + math/LegacyFrustum.h \ + math/Maps.h \ + math/Mat.h \ + math/Mat3.h \ + math/Mat4.h \ + math/Math.h \ + math/Operators.h \ + math/Proximity.h \ + math/QuantizedUnitVec.h \ + math/Quat.h \ + math/Ray.h \ + math/Stats.h \ + math/Stencils.h \ + math/Transform.h\ + math/Tuple.h\ + math/Vec2.h \ + math/Vec3.h \ + math/Vec4.h \ + Metadata.h \ + MetaMap.h \ + openvdb.h \ + Platform.h \ + PlatformConfig.h \ + points/AttributeArray.h \ + points/AttributeArrayString.h \ + points/AttributeGroup.h \ + points/AttributeSet.h \ + points/IndexFilter.h \ + points/IndexIterator.h \ + points/PointAdvect.h \ + points/PointAttribute.h \ + points/PointConversion.h \ + points/PointCount.h \ + points/PointDataGrid.h \ + points/PointDelete.h \ + points/PointGroup.h \ + points/PointMask.h \ + points/PointMove.h \ + points/PointSample.h \ + points/PointScatter.h \ + points/StreamCompression.h \ + tools/ChangeBackground.h \ + tools/Clip.h \ + tools/Composite.h \ + tools/Dense.h \ + tools/DenseSparseTools.h \ + tools/Diagnostics.h \ + tools/Filter.h \ + tools/FindActiveValues.h \ + tools/GridOperators.h \ + tools/GridTransformer.h \ + tools/Interpolation.h \ + tools/LevelSetAdvect.h \ + tools/LevelSetFilter.h \ + tools/LevelSetFracture.h \ + tools/LevelSetMeasure.h \ + tools/LevelSetMorph.h \ + tools/LevelSetPlatonic.h \ + tools/LevelSetRebuild.h \ + tools/LevelSetSphere.h \ + tools/LevelSetTracker.h \ + tools/LevelSetUtil.h \ + tools/Mask.h \ + tools/MeshToVolume.h \ + tools/Morphology.h \ + tools/MultiResGrid.h \ + tools/ParticleAtlas.h \ + tools/ParticlesToLevelSet.h \ + tools/PointAdvect.h \ + tools/PointIndexGrid.h \ + tools/PointPartitioner.h \ + tools/PointScatter.h \ + tools/PointsToMask.h \ + tools/PoissonSolver.h \ + tools/PotentialFlow.h \ + tools/Prune.h \ + tools/RayIntersector.h \ + tools/RayTracer.h \ + tools/SignedFloodFill.h \ + tools/Statistics.h \ + tools/TopologyToLevelSet.h \ + tools/ValueTransformer.h \ + tools/VectorTransformer.h \ + tools/VelocityFields.h \ + tools/VolumeAdvect.h \ + tools/VolumeToMesh.h \ + tools/VolumeToSpheres.h \ + tree/InternalNode.h \ + tree/Iterator.h \ + tree/LeafBuffer.h \ + tree/LeafManager.h \ + tree/LeafNode.h \ + tree/LeafNodeBool.h \ + tree/LeafNodeMask.h \ + tree/NodeManager.h \ + tree/NodeUnion.h \ + tree/RootNode.h \ + tree/Tree.h \ + tree/TreeIterator.h \ + tree/ValueAccessor.h \ + Types.h \ + util/CpuTimer.h \ + util/Formats.h \ + util/logging.h \ + util/MapsUtil.h \ + util/Name.h \ + util/NodeMasks.h \ + util/NullInterrupter.h \ + util/PagedArray.h \ + util/Util.h \ + version.h \ +# + +SRC_NAMES := \ + Grid.cc \ + io/Archive.cc \ + io/Compression.cc \ + io/DelayedLoadMetadata.cc \ + io/File.cc \ + io/GridDescriptor.cc \ + io/Queue.cc \ + io/Stream.cc \ + io/TempFile.cc \ + math/Maps.cc \ + math/Proximity.cc \ + math/QuantizedUnitVec.cc \ + math/Transform.cc \ + Metadata.cc \ + MetaMap.cc \ + openvdb.cc \ + Platform.cc \ + points/AttributeArray.cc \ + points/AttributeArrayString.cc \ + points/AttributeGroup.cc \ + points/AttributeSet.cc \ + points/points.cc \ + points/StreamCompression.cc \ + util/Formats.cc \ + util/Util.cc \ +# + +UNITTEST_INCLUDE_NAMES := \ + unittest/util.h \ +# + +UNITTEST_SRC_NAMES := \ + unittest/main.cc \ + unittest/TestAttributeArray.cc \ + unittest/TestAttributeArrayString.cc \ + unittest/TestAttributeSet.cc \ + unittest/TestAttributeGroup.cc \ + unittest/TestBBox.cc \ + unittest/TestConjGradient.cc \ + unittest/TestCoord.cc \ + unittest/TestCpt.cc \ + unittest/TestCurl.cc \ + unittest/TestDelayedLoadMetadata.cc \ + unittest/TestDense.cc \ + unittest/TestDenseSparseTools.cc \ + unittest/TestDiagnostics.cc \ + unittest/TestDivergence.cc \ + unittest/TestDoubleMetadata.cc \ + unittest/TestExceptions.cc \ + unittest/TestFile.cc \ + unittest/TestFindActiveValues.cc \ + unittest/TestFloatMetadata.cc \ + unittest/TestGradient.cc \ + unittest/TestGrid.cc \ + unittest/TestGridBbox.cc \ + unittest/TestGridDescriptor.cc \ + unittest/TestGridIO.cc \ + unittest/TestGridTransformer.cc \ + unittest/TestIndexFilter.cc \ + unittest/TestIndexIterator.cc \ + unittest/TestInit.cc \ + unittest/TestInt32Metadata.cc \ + unittest/TestInt64Metadata.cc \ + unittest/TestInternalOrigin.cc \ + unittest/TestLaplacian.cc \ + unittest/TestLeaf.cc \ + unittest/TestLeafBool.cc \ + unittest/TestLeafManager.cc \ + unittest/TestLeafMask.cc \ + unittest/TestLeafIO.cc \ + unittest/TestLeafOrigin.cc \ + unittest/TestLevelSetRayIntersector.cc \ + unittest/TestLevelSetUtil.cc \ + unittest/TestLinearInterp.cc \ + unittest/TestMaps.cc \ + unittest/TestMat4Metadata.cc \ + unittest/TestMath.cc \ + unittest/TestMeanCurvature.cc \ + unittest/TestMeshToVolume.cc \ + unittest/TestMetadata.cc \ + unittest/TestMetadataIO.cc \ + unittest/TestMetaMap.cc \ + unittest/TestMultiResGrid.cc \ + unittest/TestName.cc \ + unittest/TestNodeIterator.cc \ + unittest/TestNodeManager.cc \ + unittest/TestNodeMask.cc \ + unittest/TestParticleAtlas.cc \ + unittest/TestParticlesToLevelSet.cc \ + unittest/TestPointAdvect.cc \ + unittest/TestPointAttribute.cc \ + unittest/TestPointConversion.cc \ + unittest/TestPointCount.cc \ + unittest/TestPointDataLeaf.cc \ + unittest/TestPointDelete.cc \ + unittest/TestPointGroup.cc \ + unittest/TestPointIndexGrid.cc \ + unittest/TestPointMask.cc \ + unittest/TestPointMove.cc \ + unittest/TestPointPartitioner.cc \ + unittest/TestPointSample.cc \ + unittest/TestPointScatter.cc \ + unittest/TestPointsToMask.cc \ + unittest/TestPoissonSolver.cc \ + unittest/TestPotentialFlow.cc \ + unittest/TestPrePostAPI.cc \ + unittest/TestQuadraticInterp.cc \ + unittest/TestQuantizedUnitVec.cc \ + unittest/TestQuat.cc \ + unittest/TestRay.cc \ + unittest/TestStats.cc \ + unittest/TestStream.cc \ + unittest/TestStreamCompression.cc \ + unittest/TestStringMetadata.cc \ + unittest/TestTools.cc \ + unittest/TestTopologyToLevelSet.cc \ + unittest/TestTransform.cc \ + unittest/TestTree.cc \ + unittest/TestTreeCombine.cc \ + unittest/TestTreeGetSetValues.cc \ + unittest/TestTreeIterators.cc \ + unittest/TestTreeVisitor.cc \ + unittest/TestTypes.cc \ + unittest/TestUtil.cc \ + unittest/TestValueAccessor.cc \ + unittest/TestVec2Metadata.cc \ + unittest/TestVec3Metadata.cc \ + unittest/TestVolumeRayIntersector.cc \ + unittest/TestVolumeToMesh.cc \ + unittest/TestVolumeToSpheres.cc \ +# + +DOC_FILES := \ + ../doc/changes.txt \ + ../doc/codingstyle.txt \ + ../doc/doc.txt \ + ../doc/examplecode.txt \ + ../doc/faq.txt \ + ../doc/math.txt \ + ../doc/points.txt \ + ../doc/python.txt +DOC_INDEX := ../doc/html/index.html +DOC_PDF := ../doc/latex/refman.pdf + +LIBVIEWER_INCLUDE_NAMES := \ + viewer/Camera.h \ + viewer/ClipBox.h \ + viewer/Font.h \ + viewer/RenderModules.h \ + viewer/Viewer.h \ +# +# Used for "install" target only +LIBVIEWER_PUBLIC_INCLUDE_NAMES := \ + viewer/Viewer.h \ +# +LIBVIEWER_SRC_NAMES := \ + viewer/Camera.cc \ + viewer/ClipBox.cc \ + viewer/Font.cc \ + viewer/RenderModules.cc \ + viewer/Viewer.cc \ +# +ifdef MBSD + LIBVIEWER_FLAGS := -framework Cocoa -framework OpenGL -framework IOKit +else + LIBVIEWER_FLAGS := -lGL -lGLU +endif + + +CMD_INCLUDE_NAMES := \ +# + +CMD_SRC_NAMES := \ + cmd/openvdb_lod.cc \ + cmd/openvdb_print.cc \ + cmd/openvdb_render.cc \ + cmd/openvdb_view.cc \ +# + + +PYTHON_INCLUDE_NAMES := \ + python/pyopenvdb.h \ + python/pyutil.h \ + python/pyAccessor.h \ + python/pyGrid.h \ +# +# Used for "install" target only +PYTHON_PUBLIC_INCLUDE_NAMES := \ + python/pyopenvdb.h \ +# +PYTHON_SRC_NAMES := \ + python/pyFloatGrid.cc \ + python/pyIntGrid.cc \ + python/pyMetadata.cc \ + python/pyOpenVDBModule.cc \ + python/pyPointGrid.cc \ + python/pyTransform.cc \ + python/pyVec3Grid.cc \ +# +PYCXXFLAGS := -fPIC -isystem python -isystem $(PYTHON_INCL_DIR) -isystem $(PYCONFIG_INCL_DIR) +ifneq (,$(strip $(NUMPY_INCL_DIR))) +PYCXXFLAGS += -isystem $(NUMPY_INCL_DIR) -DPY_OPENVDB_USE_NUMPY +endif +ifneq (no,$(strip $(PYTHON_WRAP_ALL_GRID_TYPES))) +PYCXXFLAGS += -DPY_OPENVDB_WRAP_ALL_GRID_TYPES +endif + + +HEADER_SUBDIRS := $(dir $(INCLUDE_NAMES)) + +ALL_INCLUDE_FILES := \ + $(INCLUDE_NAMES) \ + $(UNITTEST_INCLUDE_NAMES) \ + $(CMD_INCLUDE_NAMES) \ + $(LIBVIEWER_INCLUDE_NAMES) \ + $(PYTHON_INCLUDE_NAMES) \ +# +SRC_FILES := \ + $(SRC_NAMES) \ + $(UNITTEST_SRC_NAMES) \ + $(CMD_SRC_NAMES) \ + $(LIBVIEWER_SRC_NAMES) \ + $(PYTHON_SRC_NAMES) \ +# +ALL_SRC_FILES := $(SRC_FILES) + +OBJ_NAMES := $(SRC_NAMES:.cc=.o) +UNITTEST_OBJ_NAMES := $(UNITTEST_SRC_NAMES:.cc=.o) +LIBVIEWER_OBJ_NAMES := $(LIBVIEWER_SRC_NAMES:.cc=.o) +PYTHON_OBJ_NAMES := $(PYTHON_SRC_NAMES:.cc=.o) + +LIB_MAJOR_VERSION=$(shell grep 'define OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER ' \ + version.h | sed 's/[^0-9]*//g') +LIB_MINOR_VERSION=$(shell grep 'define OPENVDB_LIBRARY_MINOR_VERSION_NUMBER ' \ + version.h | sed 's/[^0-9]*//g') +LIB_PATCH_VERSION=$(shell grep 'define OPENVDB_LIBRARY_PATCH_VERSION_NUMBER ' \ + version.h | sed 's/[^0-9]*//g') + +LIB_VERSION=$(LIB_MAJOR_VERSION).$(LIB_MINOR_VERSION).$(LIB_PATCH_VERSION) +SO_VERSION=$(LIB_MAJOR_VERSION).$(LIB_MINOR_VERSION) + +LIBOPENVDB_NAME=libopenvdb +LIBOPENVDB_STATIC := $(LIBOPENVDB_NAME).a +ifndef MBSD +LIBOPENVDB_SHARED_NAME := $(LIBOPENVDB_NAME).so +LIBOPENVDB_SHARED := $(LIBOPENVDB_NAME).so.$(LIB_VERSION) +LIBOPENVDB_SONAME := $(LIBOPENVDB_NAME).so.$(SO_VERSION) +LIBOPENVDB_SONAME_FLAGS := -Wl,-soname,$(LIBOPENVDB_SONAME) +else +LIBOPENVDB_SHARED_NAME := $(LIBOPENVDB_NAME).dylib +LIBOPENVDB_SHARED := $(LIBOPENVDB_NAME).$(LIB_VERSION).dylib +LIBOPENVDB_SONAME := $(LIBOPENVDB_NAME).$(SO_VERSION).dylib +LIBOPENVDB_SONAME_FLAGS := -Wl,-install_name,$(DESTDIR_LIB_DIR)/$(LIBOPENVDB_SONAME) +endif + +# TODO: libopenvdb_viewer is currently built into vdb_view and is not installed separately. +LIBVIEWER_NAME=libopenvdb_viewer +LIBVIEWER_STATIC := $(LIBVIEWER_NAME).a +ifndef MBSD +LIBVIEWER_SHARED_NAME := $(LIBVIEWER_NAME).so +LIBVIEWER_SHARED := $(LIBVIEWER_NAME).so.$(LIB_VERSION) +LIBVIEWER_SONAME := $(LIBVIEWER_NAME).so.$(SO_VERSION) +LIBVIEWER_SONAME_FLAGS := -Wl,-soname,$(LIBVIEWER_SONAME) +else +LIBVIEWER_SHARED_NAME := $(LIBVIEWER_NAME).dylib +LIBVIEWER_SHARED := $(LIBVIEWER_NAME).$(LIB_VERSION).dylib +LIBVIEWER_SONAME := $(LIBVIEWER_NAME).$(SO_VERSION).dylib +LIBVIEWER_SONAME_FLAGS := -Wl,-install_name,$(DESTDIR_LIB_DIR)/$(LIBVIEWER_SONAME) +endif + +PYTHON_MODULE_NAME=pyopenvdb +PYTHON_MODULE := $(PYTHON_MODULE_NAME).so +PYTHON_SONAME := $(PYTHON_MODULE_NAME).so.$(SO_VERSION) +ifndef MBSD +PYTHON_SONAME_FLAGS := -Wl,-soname,$(PYTHON_SONAME) +endif + +ifeq (no,$(strip $(shared))) + LIBOPENVDB := $(LIBOPENVDB_STATIC) + LIBVIEWER := $(LIBVIEWER_STATIC) +else + LIBOPENVDB := $(LIBOPENVDB_SHARED) + LIBVIEWER := $(LIBVIEWER_SHARED) + LIBOPENVDB_RPATH := -Wl,-rpath,$(DESTDIR_LIB_DIR) +endif # shared + +DEPEND := dependencies + +# Get the list of dependencies that are newer than the current target, +# but limit the list to at most three entries. +list_deps = $(if $(wordlist 4,5,$(?F)),$(firstword $(?F)) and others,$(wordlist 1,3,$(?F))) + +ALL_PRODUCTS := \ + $(LIBOPENVDB) \ + vdb_test \ + vdb_lod \ + vdb_print \ + vdb_render \ + vdb_view \ + $(DEPEND) \ + $(LIBOPENVDB_SHARED_NAME) \ + $(LIBOPENVDB_SONAME) \ + $(PYTHON_MODULE) \ +# + +.SUFFIXES: .o .cc + +.PHONY: all clean depend doc header_test install lib pdfdoc pydoc pytest python test viewerlib + +.cc.o: + @echo "Building $@ because of $(call list_deps)" + $(CXX) -c $(CXXFLAGS) -fPIC -o $@ $< + +all: lib python vdb_lod vdb_print vdb_render vdb_test depend + +$(OBJ_NAMES): %.o: %.cc + @echo "Building $@ because of $(call list_deps)" + $(CXX) -c -DOPENVDB_PRIVATE $(CXXFLAGS) -fPIC -o $@ $< + +ifneq (no,$(strip $(shared))) + +# Build shared library +lib: $(LIBOPENVDB_SHARED_NAME) $(LIBOPENVDB_SONAME) + +$(LIBOPENVDB_SHARED_NAME): $(LIBOPENVDB_SHARED) + ln -f -s $< $@ + +$(LIBOPENVDB_SONAME): $(LIBOPENVDB_SHARED) + ln -f -s $< $@ + +$(LIBOPENVDB_SHARED): $(OBJ_NAMES) + @echo "Building $@ because of $(list_deps)" + $(CXX) $(CXXFLAGS) -shared -o $@ $^ $(LIBS_RPATH) $(LIBOPENVDB_SONAME_FLAGS) + +else + +# Build static library +lib: $(LIBOPENVDB) + +$(LIBOPENVDB_STATIC): $(OBJ_NAMES) + @echo "Building $@ because of $(list_deps)" + $(AR) cr $@ $^ + +endif # shared + + +$(DOC_INDEX): ../doc/doxygen-config $(INCLUDE_NAMES) $(SRC_NAMES) $(DOC_FILES) + @echo "Generating documentation because of $(list_deps)" + pushd ..; \ + echo 'OUTPUT_DIRECTORY=./doc' | cat ./doc/doxygen-config - | $(DOXYGEN) - $(QUIET); \ + popd > /dev/null + +$(DOC_PDF): ../doc/doxygen-config $(INCLUDE_NAMES) $(SRC_NAMES) $(DOC_FILES) + @echo "Generating documentation because of $(list_deps)" + pushd ..; \ + echo -e 'OUTPUT_DIRECTORY=./doc\nGENERATE_LATEX=YES\nGENERATE_HTML=NO' \ + | cat ./doc/doxygen-config - | $(DOXYGEN) - $(QUIET) \ + && cd ./doc/latex && make refman.pdf $(QUIET) \ + && echo 'Created doc/latex/refman.pdf'; \ + popd > /dev/null + +ifneq (,$(strip $(DOXYGEN))) +doc: $(DOC_INDEX) +pdfdoc: $(DOC_PDF) +else +doc: + @echo "$@"': $$DOXYGEN is undefined' +pdfdoc: + @echo "$@"': $$DOXYGEN is undefined' +endif + +vdb_lod: $(LIBOPENVDB) cmd/openvdb_lod.cc + @echo "Building $@ because of $(list_deps)" + $(CXX) $(CXXFLAGS) -o $@ cmd/openvdb_lod.cc -I . \ + $(LIBOPENVDB_RPATH) -L$(CURDIR) $(LIBOPENVDB) \ + $(LIBS_RPATH) $(CONCURRENT_MALLOC_LIB) + +vdb_print: $(LIBOPENVDB) cmd/openvdb_print.cc + @echo "Building $@ because of $(list_deps)" + $(CXX) $(CXXFLAGS) -o $@ cmd/openvdb_print.cc -I . \ + $(LIBOPENVDB_RPATH) -L$(CURDIR) $(LIBOPENVDB) \ + $(LIBS_RPATH) $(CONCURRENT_MALLOC_LIB) + +vdb_render: $(LIBOPENVDB) cmd/openvdb_render.cc + @echo "Building $@ because of $(list_deps)" + $(CXX) $(CXXFLAGS) -o $@ cmd/openvdb_render.cc -I . \ + -isystem $(EXR_INCL_DIR) -isystem $(ILMBASE_INCL_DIR) \ + -Wl,-rpath,$(EXR_LIB_DIR) -L$(EXR_LIB_DIR) $(EXR_LIB) \ + -Wl,-rpath,$(ILMBASE_LIB_DIR) -L$(ILMBASE_LIB_DIR) $(ILMBASE_LIB) \ + $(LIBOPENVDB_RPATH) -L$(CURDIR) $(LIBOPENVDB) \ + $(LIBS_RPATH) $(CONCURRENT_MALLOC_LIB) + +# Create an openvdb_viewer/ symlink to the viewer/ subdirectory, +# to mirror the DWA directory structure. +openvdb_viewer: + ln -f -s viewer openvdb_viewer + +ifneq (yes,$(has_glfw)) +vdb_view: + @echo "$@"': GLFW is unavailable' +else +$(LIBVIEWER_INCLUDE_NAMES): openvdb_viewer + +$(LIBVIEWER_OBJ_NAMES): $(LIBVIEWER_INCLUDE_NAMES) +$(LIBVIEWER_OBJ_NAMES): %.o: %.cc + @echo "Building $@ because of $(list_deps)" + $(CXX) -c $(CXXFLAGS) -I . -isystem $(GLFW_INCL_DIR) -DGL_GLEXT_PROTOTYPES=1 -fPIC -o $@ $< + +vdb_view: $(LIBOPENVDB) $(LIBVIEWER_OBJ_NAMES) cmd/openvdb_view.cc + @echo "Building $@ because of $(list_deps)" + $(CXX) $(CXXFLAGS) -o $@ cmd/openvdb_view.cc $(LIBVIEWER_OBJ_NAMES) \ + -I . $(LIBOPENVDB_RPATH) -L$(CURDIR) $(LIBOPENVDB) \ + $(LIBVIEWER_FLAGS) $(LIBS_RPATH) $(BOOST_THREAD_LIB) $(CONCURRENT_MALLOC_LIB) \ + -Wl,-rpath,$(GLFW_LIB_DIR) -L$(GLFW_LIB_DIR) $(GLFW_LIB) +endif + + +# Build the Python module +$(PYTHON_OBJ_NAMES): $(PYTHON_INCLUDE_NAMES) +$(PYTHON_OBJ_NAMES): %.o: %.cc + @echo "Building $@ because of $(list_deps)" + $(CXX) -c $(CXXFLAGS) -I . $(PYCXXFLAGS) -o $@ $< +$(PYTHON_MODULE): $(LIBOPENVDB) $(PYTHON_OBJ_NAMES) + @echo "Building $@ because of $(list_deps)" + $(CXX) $(CXXFLAGS) $(PYCXXFLAGS) -shared $(PYTHON_SONAME_FLAGS) -o $@ $(PYTHON_OBJ_NAMES) \ + -Wl,-rpath,$(PYTHON_LIB_DIR) -L$(PYTHON_LIB_DIR) $(PYTHON_LIB) \ + -Wl,-rpath,$(BOOST_PYTHON_LIB_DIR) -L$(BOOST_PYTHON_LIB_DIR) $(BOOST_PYTHON_LIB) \ + $(LIBOPENVDB_RPATH) -L$(CURDIR) $(LIBOPENVDB) \ + $(LIBS_RPATH) $(CONCURRENT_MALLOC_LIB) + +ifeq (yes,$(has_python)) +ifneq (,$(strip $(EPYDOC))) +pydoc: $(PYTHON_MODULE) $(LIBOPENVDB_SONAME) + @echo "Generating Python module documentation because of $(list_deps)" + pydocdir=../doc/html/python; \ + mkdir -p $${pydocdir}; \ + echo "Created $${pydocdir}"; \ + export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(CURDIR); \ + export PYTHONPATH=${PYTHONPATH}:$(CURDIR); \ + $(EPYDOC) --html -o $${pydocdir} $(PYTHON_MODULE_NAME) $(QUIET) +else +pydoc: + @echo "$@"': $$EPYDOC is undefined' +endif + +pytest: $(PYTHON_MODULE) $(LIBOPENVDB_SONAME) + @echo "Testing Python module $(PYTHON_MODULE)" + export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(CURDIR); \ + export PYTHONPATH=${PYTHONPATH}:$(CURDIR); \ + python$(PYTHON_VERSION) ./python/test/TestOpenVDB.py $(QUIET_TEST) + +python: $(PYTHON_MODULE) +else +python pytest pydoc: + @echo "$@"': Python is unavailable' +endif + + +$(UNITTEST_OBJ_NAMES): %.o: %.cc + @echo "Building $@ because of $(list_deps)" + $(CXX) -c $(CXXFLAGS) -isystem $(CPPUNIT_INCL_DIR) -fPIC -o $@ $< + +ifneq (,$(strip $(CPPUNIT_INCL_DIR))) +vdb_test: $(LIBOPENVDB) $(UNITTEST_OBJ_NAMES) + @echo "Building $@ because of $(list_deps)" + $(CXX) $(CXXFLAGS) -o $@ $(UNITTEST_OBJ_NAMES) \ + $(LIBOPENVDB_RPATH) -L$(CURDIR) $(LIBOPENVDB) \ + $(LIBS_RPATH) $(CONCURRENT_MALLOC_LIB) \ + -Wl,-rpath,$(CPPUNIT_LIB_DIR) -L$(CPPUNIT_LIB_DIR) $(CPPUNIT_LIB) + +test: lib vdb_test + @echo "Testing $(LIBOPENVDB_NAME)" + export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(CURDIR); ./vdb_test $(QUIET_TEST) +else +vdb_test: + @echo "$@"': $$(CPPUNIT_INCL_DIR) is undefined' +test: + @echo "$@"': $$(CPPUNIT_INCL_DIR) is undefined' +endif + +install_lib: lib + mkdir -p $(DESTDIR)/include/openvdb + @echo "Created $(DESTDIR)/include/openvdb" + pushd $(DESTDIR)/include/openvdb > /dev/null; \ + mkdir -p $(HEADER_SUBDIRS); popd > /dev/null + for f in $(INCLUDE_NAMES); \ + do cp -f $$f $(DESTDIR)/include/openvdb/$$f; done + @echo "Copied header files to $(DESTDIR)/include" + @# + mkdir -p $(DESTDIR_LIB_DIR) + @echo "Created $(DESTDIR_LIB_DIR)" + cp -f $(LIBOPENVDB) $(DESTDIR_LIB_DIR) + pushd $(DESTDIR_LIB_DIR) > /dev/null; \ + if [ -f $(LIBOPENVDB_SHARED) ]; then \ + ln -f -s $(LIBOPENVDB_SHARED) $(LIBOPENVDB_SHARED_NAME); \ + ln -f -s $(LIBOPENVDB_SHARED) $(LIBOPENVDB_SONAME); \ + fi; \ + popd > /dev/null + @echo "Copied libopenvdb to $(DESTDIR_LIB_DIR)" + +install: install_lib python vdb_lod vdb_print vdb_render vdb_view doc pydoc + if [ -f $(LIBVIEWER) ]; \ + then \ + mkdir -p $(DESTDIR)/include/openvdb_viewer; \ + echo "Created $(DESTDIR)/include/openvdb_viewer"; \ + cp -f $(LIBVIEWER_PUBLIC_INCLUDE_NAMES) $(DESTDIR)/include/openvdb_viewer/; \ + @echo "Copied vdb_view header files to $(DESTDIR)/include" \ + cp -f $(LIBVIEWER) $(DESTDIR_LIB_DIR); \ + pushd $(DESTDIR_LIB_DIR) > /dev/null; \ + if [ -f $(LIBVIEWER_SHARED) ]; then \ + ln -f -s $(LIBVIEWER_SHARED) $(LIBVIEWER_SHARED_NAME); fi; \ + popd > /dev/null; \ + @echo "Copied libopenvdb_viewer to $(DESTDIR_LIB_DIR)"; \ + fi + @# + if [ -f $(PYTHON_MODULE) ]; \ + then \ + installdir=$(DESTDIR)/python/include/python$(PYTHON_VERSION); \ + mkdir -p $${installdir}; \ + echo "Created $${installdir}"; \ + cp -f $(PYTHON_PUBLIC_INCLUDE_NAMES) $${installdir}/; \ + echo "Copied Python header files to $${installdir}"; \ + installdir=$(DESTDIR)/python/lib/python$(PYTHON_VERSION); \ + mkdir -p $${installdir}; \ + echo "Created $${installdir}"; \ + cp -f $(PYTHON_MODULE) $${installdir}/; \ + pushd $${installdir} > /dev/null; \ + ln -f -s $(PYTHON_MODULE) $(PYTHON_SONAME); \ + popd > /dev/null; \ + echo "Copied Python module to $${installdir}"; \ + fi + @# + mkdir -p $(DESTDIR)/bin + @echo "Created $(DESTDIR)/bin/" + cp -f vdb_lod $(DESTDIR)/bin + @echo "Copied vdb_lod to $(DESTDIR)/bin/" + cp -f vdb_print $(DESTDIR)/bin + @echo "Copied vdb_print to $(DESTDIR)/bin/" + cp -f vdb_render $(DESTDIR)/bin + @echo "Copied vdb_render to $(DESTDIR)/bin/" + if [ -f vdb_view ]; \ + then \ + cp -f vdb_view $(DESTDIR)/bin; \ + echo "Copied vdb_view to $(DESTDIR)/bin/"; \ + fi + @# + if [ -d ../doc/html ]; \ + then \ + mkdir -p $(DESTDIR)/share/doc/openvdb; \ + echo "Created $(DESTDIR)/share/doc/openvdb/"; \ + cp -r -f ../doc/html $(DESTDIR)/share/doc/openvdb; \ + echo "Copied documentation to $(DESTDIR)/share/doc/openvdb/"; \ + fi + +# TODO: This accumulates all source file dependencies into a single file +# containing a rule for each *.o file. Consider generating a separate +# dependency file for each *.o file instead. +$(DEPEND): $(ALL_INCLUDE_FILES) $(ALL_SRC_FILES) openvdb_viewer + @echo "Generating dependencies because of $(list_deps)" + $(RM) $(DEPEND) + for f in $(SRC_NAMES) $(CMD_SRC_NAMES); \ + do $(CXX) $(CXXFLAGS) -O0 \ + -MM $$f -MT `echo $$f | sed 's%\.[^.]*%.o%'` >> $(DEPEND); \ + done + if [ -d "$(CPPUNIT_INCL_DIR)" ]; \ + then \ + for f in $(UNITTEST_SRC_NAMES); \ + do $(CXX) $(CXXFLAGS) -O0 \ + -MM $$f -MT `echo $$f | sed 's%\.[^.]*%.o%'` \ + -isystem $(CPPUNIT_INCL_DIR) >> $(DEPEND); \ + done; \ + fi + +depend: $(DEPEND) + +# Compile an implicit translation unit for each header to identify any indirect includes +HEADER_TEST_CXXFLAGS := $(CXXFLAGS) +HEADER_TEST_FILES := $(addprefix header_test-,$(INCLUDE_NAMES) $(CMD_INCLUDE_NAMES)) +ifeq (yes,$(has_cppunit)) + HEADER_TEST_FILES += $(addprefix header_test-,$(UNITTEST_INCLUDE_NAMES)) + HEADER_TEST_CXXFLAGS += -isystem $(CPPUNIT_INCL_DIR) +endif +ifeq (yes,$(has_glfw)) + HEADER_TEST_FILES += $(addprefix header_test-,$(LIBVIEWER_INCLUDE_NAMES)) + HEADER_TEST_CXXFLAGS += -isystem $(GLFW_INCL_DIR) -DGL_GLEXT_PROTOTYPES=1 +endif +ifeq (yes,$(has_python)) + HEADER_TEST_FILES += $(addprefix header_test-,$(PYTHON_INCLUDE_NAMES)) + HEADER_TEST_CXXFLAGS += $(PYCXXFLAGS) +endif +$(HEADER_TEST_FILES): header_test-%: + echo "#include \"$*\"" | $(CXX) -c -x c++ $(HEADER_TEST_CXXFLAGS) -fPIC -o /dev/null - +echo_header_test: + @echo "Checking for missing or indirectly included headers" +header_test: echo_header_test $(HEADER_TEST_FILES) + +clean: + $(RM) $(OBJ_NAMES) $(ALL_PRODUCTS) $(DEPEND) + $(RM) $(LIBOPENVDB_STATIC) + $(RM) $(LIBOPENVDB_SHARED) + $(RM) $(LIBVIEWER_OBJ_NAMES) + $(RM) $(PYTHON_OBJ_NAMES) + $(RM) $(UNITTEST_OBJ_NAMES) + $(RM) -r ../doc/html ../doc/latex + +ifneq (,$(strip $(wildcard $(DEPEND)))) + include $(DEPEND) +endif diff --git a/openvdb/MetaMap.cc b/openvdb/MetaMap.cc new file mode 100644 index 00000000..d0396af7 --- /dev/null +++ b/openvdb/MetaMap.cc @@ -0,0 +1,209 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "MetaMap.h" + +#include "util/logging.h" +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +MetaMap::MetaMap(const MetaMap& other) +{ + this->insertMeta(other); +} + + +MetaMap::Ptr +MetaMap::copyMeta() const +{ + MetaMap::Ptr ret(new MetaMap); + ret->mMeta = this->mMeta; + return ret; +} + + +MetaMap::Ptr +MetaMap::deepCopyMeta() const +{ + return MetaMap::Ptr(new MetaMap(*this)); +} + + +MetaMap& +MetaMap::operator=(const MetaMap& other) +{ + if (&other != this) { + this->clearMetadata(); + // Insert all metadata into this map. + ConstMetaIterator iter = other.beginMeta(); + for ( ; iter != other.endMeta(); ++iter) { + this->insertMeta(iter->first, *(iter->second)); + } + } + return *this; +} + + +void +MetaMap::readMeta(std::istream &is) +{ + // Clear out the current metamap if need be. + this->clearMetadata(); + + // Read in the number of metadata items. + Index32 count = 0; + is.read(reinterpret_cast(&count), sizeof(Index32)); + + // Read in each metadata. + for (Index32 i = 0; i < count; ++i) { + // Read in the name. + Name name = readString(is); + + // Read in the metadata typename. + Name typeName = readString(is); + + // Read in the metadata value and add it to the map. + if (Metadata::isRegisteredType(typeName)) { + Metadata::Ptr metadata = Metadata::createMetadata(typeName); + metadata->read(is); + insertMeta(name, *metadata); + } else { +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + UnknownMetadata metadata(typeName); + metadata.read(is); // read raw bytes into an array + // only add unknown metadata to the grid if not temporary, + // denoted by a double underscore prefix (such as __metadata) + bool temporary = typeName.compare(0, 2, "__") == 0; + if (!temporary) { + insertMeta(name, metadata); + } +#else + OPENVDB_LOG_WARN("cannot read metadata \"" << name + << "\" of unregistered type \"" << typeName << "\""); + UnknownMetadata metadata; + metadata.read(is); +#endif + } + } +} + + +void +MetaMap::writeMeta(std::ostream &os) const +{ + // Write out the number of metadata items we have in the map. Note that we + // save as Index32 to save a 32-bit number. Using size_t would be platform + // dependent. + Index32 count = static_cast(metaCount()); + os.write(reinterpret_cast(&count), sizeof(Index32)); + + // Iterate through each metadata and write it out. + for (ConstMetaIterator iter = beginMeta(); iter != endMeta(); ++iter) { + // Write the name of the metadata. + writeString(os, iter->first); + + // Write the type name of the metadata. + writeString(os, iter->second->typeName()); + + // Write out the metadata value. + iter->second->write(os); + } +} + + +void +MetaMap::insertMeta(const Name &name, const Metadata &m) +{ + if (name.size() == 0) + OPENVDB_THROW(ValueError, "Metadata name cannot be an empty string"); + + // See if the value already exists, if so then replace the existing one. + MetaIterator iter = mMeta.find(name); + + if (iter == mMeta.end()) { + // Create a copy of the metadata and store it in the map + Metadata::Ptr tmp = m.copy(); + mMeta[name] = tmp; + } else { + if (iter->second->typeName() != m.typeName()) { + std::ostringstream ostr; + ostr << "Cannot assign value of type " + << m.typeName() << " to metadata attribute " << name + << " of " << "type " << iter->second->typeName(); + OPENVDB_THROW(TypeError, ostr.str()); + } + // else + Metadata::Ptr tmp = m.copy(); + iter->second = tmp; + } +} + + +void +MetaMap::insertMeta(const MetaMap& other) +{ + for (ConstMetaIterator it = other.beginMeta(), end = other.endMeta(); it != end; ++it) { + if (it->second) this->insertMeta(it->first, *it->second); + } +} + + +void +MetaMap::removeMeta(const Name &name) +{ + MetaIterator iter = mMeta.find(name); + if (iter != mMeta.end()) { + mMeta.erase(iter); + } +} + + +bool +MetaMap::operator==(const MetaMap& other) const +{ + // Check if the two maps have the same number of elements. + if (this->mMeta.size() != other.mMeta.size()) return false; + // Iterate over the two maps in sorted order. + for (ConstMetaIterator it = beginMeta(), otherIt = other.beginMeta(), end = endMeta(); + it != end; ++it, ++otherIt) + { + // Check if the two keys match. + if (it->first != otherIt->first) return false; + // Check if the two values are either both null or both non-null pointers. + if (bool(it->second) != bool(otherIt->second)) return false; + // If the two values are both non-null, compare their contents. + if (it->second && otherIt->second && *it->second != *otherIt->second) return false; + } + return true; +} + + +std::string +MetaMap::str(const std::string& indent) const +{ + std::ostringstream ostr; + char sep[2] = { 0, 0 }; + for (ConstMetaIterator iter = beginMeta(); iter != endMeta(); ++iter) { + ostr << sep << indent << iter->first; + if (iter->second) { + const std::string value = iter->second->str(); + if (!value.empty()) ostr << ": " << value; + } + sep[0] = '\n'; + } + return ostr.str(); +} + +std::ostream& +operator<<(std::ostream& ostr, const MetaMap& metamap) +{ + ostr << metamap.str(); + return ostr; +} + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/MetaMap.h b/openvdb/MetaMap.h new file mode 100644 index 00000000..8f01acd7 --- /dev/null +++ b/openvdb/MetaMap.h @@ -0,0 +1,217 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED +#define OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED + +#include "Metadata.h" +#include "Types.h" +#include "Exceptions.h" +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +/// Container that maps names (strings) to values of arbitrary types +class OPENVDB_API MetaMap +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + using MetadataMap = std::map; + using MetaIterator = MetadataMap::iterator; + using ConstMetaIterator = MetadataMap::const_iterator; + ///< @todo this should really iterate over a map of Metadata::ConstPtrs + + MetaMap() {} + MetaMap(const MetaMap& other); + virtual ~MetaMap() {} + + /// Return a copy of this map whose fields are shared with this map. + MetaMap::Ptr copyMeta() const; + /// Return a deep copy of this map that shares no data with this map. + MetaMap::Ptr deepCopyMeta() const; + + /// Assign a deep copy of another map to this map. + MetaMap& operator=(const MetaMap&); + + /// Unserialize metadata from the given stream. + void readMeta(std::istream&); + /// Serialize metadata to the given stream. + void writeMeta(std::ostream&) const; + + /// @brief Insert a new metadata field or overwrite the value of an existing field. + /// @details If a field with the given name doesn't already exist, add a new field. + /// Otherwise, if the new value's type is the same as the existing field's value type, + /// overwrite the existing value with new value. + /// @throw TypeError if a field with the given name already exists, but its value type + /// is not the same as the new value's + /// @throw ValueError if the given field name is empty. + void insertMeta(const Name&, const Metadata& value); + /// @brief Deep copy all of the metadata fields from the given map into this map. + /// @throw TypeError if any field in the given map has the same name as + /// but a different value type than one of this map's fields. + void insertMeta(const MetaMap&); + + /// Remove the given metadata field if it exists. + void removeMeta(const Name&); + + //@{ + /// @brief Return a pointer to the metadata with the given name. + /// If no such field exists, return a null pointer. + Metadata::Ptr operator[](const Name&); + Metadata::ConstPtr operator[](const Name&) const; + //@} + + //@{ + /// @brief Return a pointer to a TypedMetadata object of type @c T and with the given name. + /// If no such field exists or if there is a type mismatch, return a null pointer. + template typename T::Ptr getMetadata(const Name&); + template typename T::ConstPtr getMetadata(const Name&) const; + //@} + + /// @brief Return a reference to the value of type @c T stored in the given metadata field. + /// @throw LookupError if no field with the given name exists. + /// @throw TypeError if the given field is not of type @c T. + template T& metaValue(const Name&); + template const T& metaValue(const Name&) const; + + // Functions for iterating over the metadata + MetaIterator beginMeta() { return mMeta.begin(); } + MetaIterator endMeta() { return mMeta.end(); } + ConstMetaIterator beginMeta() const { return mMeta.begin(); } + ConstMetaIterator endMeta() const { return mMeta.end(); } + + void clearMetadata() { mMeta.clear(); } + + size_t metaCount() const { return mMeta.size(); } + + /// Return a string describing this metadata map. Prefix each line with @a indent. + std::string str(const std::string& indent = "") const; + + /// Return @c true if the given map is equivalent to this map. + bool operator==(const MetaMap& other) const; + /// Return @c true if the given map is different from this map. + bool operator!=(const MetaMap& other) const { return !(*this == other); } + +private: + /// @brief Return a pointer to TypedMetadata with the given template parameter. + /// @throw LookupError if no field with the given name is found. + /// @throw TypeError if the given field is not of type T. + template + typename TypedMetadata::Ptr getValidTypedMetadata(const Name&) const; + + MetadataMap mMeta; +}; + +/// Write a MetaMap to an output stream +std::ostream& operator<<(std::ostream&, const MetaMap&); + + +//////////////////////////////////////// + + +inline Metadata::Ptr +MetaMap::operator[](const Name& name) +{ + MetaIterator iter = mMeta.find(name); + return (iter == mMeta.end() ? Metadata::Ptr() : iter->second); +} + +inline Metadata::ConstPtr +MetaMap::operator[](const Name &name) const +{ + ConstMetaIterator iter = mMeta.find(name); + return (iter == mMeta.end() ? Metadata::Ptr() : iter->second); +} + + +//////////////////////////////////////// + + +template +inline typename T::Ptr +MetaMap::getMetadata(const Name &name) +{ + ConstMetaIterator iter = mMeta.find(name); + if (iter == mMeta.end()) return typename T::Ptr{}; + + // To ensure that we get valid conversion if the metadata pointers cross dso + // boundaries, we have to check the qualified typename and then do a static + // cast. This is slower than doing a dynamic_pointer_cast, but is safer when + // pointers cross dso boundaries. + if (iter->second->typeName() == T::staticTypeName()) { + return StaticPtrCast(iter->second); + } // else + return typename T::Ptr{}; +} + +template +inline typename T::ConstPtr +MetaMap::getMetadata(const Name &name) const +{ + ConstMetaIterator iter = mMeta.find(name); + if (iter == mMeta.end()) return typename T::ConstPtr{}; + + // To ensure that we get valid conversion if the metadata pointers cross dso + // boundaries, we have to check the qualified typename and then do a static + // cast. This is slower than doing a dynamic_pointer_cast, but is safer when + // pointers cross dso boundaries. + if (iter->second->typeName() == T::staticTypeName()) { + return StaticPtrCast(iter->second); + } // else + return typename T::ConstPtr{}; +} + + +//////////////////////////////////////// + + +template +inline typename TypedMetadata::Ptr +MetaMap::getValidTypedMetadata(const Name &name) const +{ + ConstMetaIterator iter = mMeta.find(name); + if (iter == mMeta.end()) OPENVDB_THROW(LookupError, "Cannot find metadata " << name); + + // To ensure that we get valid conversion if the metadata pointers cross dso + // boundaries, we have to check the qualified typename and then do a static + // cast. This is slower than doing a dynamic_pointer_cast, but is safer when + // pointers cross dso boundaries. + typename TypedMetadata::Ptr m; + if (iter->second->typeName() == TypedMetadata::staticTypeName()) { + m = StaticPtrCast, Metadata>(iter->second); + } + if (!m) OPENVDB_THROW(TypeError, "Invalid type for metadata " << name); + return m; +} + + +//////////////////////////////////////// + + +template +inline T& +MetaMap::metaValue(const Name &name) +{ + typename TypedMetadata::Ptr m = getValidTypedMetadata(name); + return m->value(); +} + + +template +inline const T& +MetaMap::metaValue(const Name &name) const +{ + typename TypedMetadata::Ptr m = getValidTypedMetadata(name); + return m->value(); +} + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED diff --git a/openvdb/Metadata.cc b/openvdb/Metadata.cc new file mode 100644 index 00000000..0634ccb6 --- /dev/null +++ b/openvdb/Metadata.cc @@ -0,0 +1,189 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Metadata.h" + +#include +#include // for std::min() +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +using Mutex = tbb::mutex; +using Lock = Mutex::scoped_lock; + +using createMetadata = Metadata::Ptr (*)(); +using MetadataFactoryMap = std::map; +using MetadataFactoryMapCIter = MetadataFactoryMap::const_iterator; + +struct LockedMetadataTypeRegistry { + LockedMetadataTypeRegistry() {} + ~LockedMetadataTypeRegistry() {} + Mutex mMutex; + MetadataFactoryMap mMap; +}; + +// Global function for accessing the regsitry +static LockedMetadataTypeRegistry* +getMetadataTypeRegistry() +{ + static LockedMetadataTypeRegistry registry; + return ®istry; +} + +bool +Metadata::isRegisteredType(const Name &typeName) +{ + LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry(); + Lock lock(registry->mMutex); + + return (registry->mMap.find(typeName) != registry->mMap.end()); +} + +void +Metadata::registerType(const Name &typeName, Metadata::Ptr (*createMetadata)()) +{ + LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry(); + Lock lock(registry->mMutex); + + if (registry->mMap.find(typeName) != registry->mMap.end()) { + OPENVDB_THROW(KeyError, + "Cannot register " << typeName << ". Type is already registered"); + } + + registry->mMap[typeName] = createMetadata; +} + +void +Metadata::unregisterType(const Name &typeName) +{ + LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry(); + Lock lock(registry->mMutex); + + registry->mMap.erase(typeName); +} + +Metadata::Ptr +Metadata::createMetadata(const Name &typeName) +{ + LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry(); + Lock lock(registry->mMutex); + + MetadataFactoryMapCIter iter = registry->mMap.find(typeName); + + if (iter == registry->mMap.end()) { + OPENVDB_THROW(LookupError, + "Cannot create metadata for unregistered type " << typeName); + } + + return (iter->second)(); +} + +void +Metadata::clearRegistry() +{ + LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry(); + Lock lock(registry->mMutex); + + registry->mMap.clear(); +} + + +//////////////////////////////////////// + + +bool +Metadata::operator==(const Metadata& other) const +{ + if (other.size() != this->size()) return false; + if (other.typeName() != this->typeName()) return false; + + std::ostringstream + bytes(std::ios_base::binary), + otherBytes(std::ios_base::binary); + try { + this->writeValue(bytes); + other.writeValue(otherBytes); + return (bytes.str() == otherBytes.str()); + } catch (Exception&) {} + return false; +} + + +//////////////////////////////////////// + + +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + +Metadata::Ptr +UnknownMetadata::copy() const +{ + Metadata::Ptr metadata{new UnknownMetadata{mTypeName}}; + static_cast(metadata.get())->setValue(mBytes); + return metadata; +} + + +void +UnknownMetadata::copy(const Metadata& other) +{ + std::ostringstream ostr(std::ios_base::binary); + other.write(ostr); + std::istringstream istr(ostr.str(), std::ios_base::binary); + const auto numBytes = readSize(istr); + readValue(istr, numBytes); +} + + +void +UnknownMetadata::readValue(std::istream& is, Index32 numBytes) +{ + mBytes.clear(); + if (numBytes > 0) { + ByteVec buffer(numBytes); + is.read(reinterpret_cast(&buffer[0]), numBytes); + mBytes.swap(buffer); + } +} + + +void +UnknownMetadata::writeValue(std::ostream& os) const +{ + if (!mBytes.empty()) { + os.write(reinterpret_cast(&mBytes[0]), mBytes.size()); + } +} + +#else // if OPENVDB_ABI_VERSION_NUMBER < 5 + +void +UnknownMetadata::readValue(std::istream& is, Index32 numBytes) +{ + // Read and discard the metadata (without seeking, because + // the stream might not be seekable). + const size_t BUFFER_SIZE = 1024; + std::vector buffer(BUFFER_SIZE); + for (Index32 bytesRemaining = numBytes; bytesRemaining > 0; ) { + const Index32 bytesToSkip = std::min(bytesRemaining, BUFFER_SIZE); + is.read(&buffer[0], bytesToSkip); + bytesRemaining -= bytesToSkip; + } +} + + +void +UnknownMetadata::writeValue(std::ostream&) const +{ + OPENVDB_THROW(TypeError, "Metadata has unknown type"); +} + +#endif + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/Metadata.h b/openvdb/Metadata.h new file mode 100644 index 00000000..bceaa873 --- /dev/null +++ b/openvdb/Metadata.h @@ -0,0 +1,437 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_METADATA_HAS_BEEN_INCLUDED +#define OPENVDB_METADATA_HAS_BEEN_INCLUDED + +#include "version.h" +#include "Exceptions.h" +#include "Types.h" +#include "math/Math.h" // for math::isZero() +#include "util/Name.h" +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +/// @brief Base class for storing metadata information in a grid. +class OPENVDB_API Metadata +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + Metadata() {} + virtual ~Metadata() {} + + // Disallow copying of instances of this class. + Metadata(const Metadata&) = delete; + Metadata& operator=(const Metadata&) = delete; + + /// Return the type name of the metadata. + virtual Name typeName() const = 0; + + /// Return a copy of the metadata. + virtual Metadata::Ptr copy() const = 0; + + /// Copy the given metadata into this metadata. + virtual void copy(const Metadata& other) = 0; + + /// Return a textual representation of this metadata. + virtual std::string str() const = 0; + + /// Return the boolean representation of this metadata (empty strings + /// and zeroVals evaluate to false; most other values evaluate to true). + virtual bool asBool() const = 0; + + /// Return @c true if the given metadata is equivalent to this metadata. + bool operator==(const Metadata& other) const; + /// Return @c true if the given metadata is different from this metadata. + bool operator!=(const Metadata& other) const { return !(*this == other); } + + /// Return the size of this metadata in bytes. + virtual Index32 size() const = 0; + + /// Unserialize this metadata from a stream. + void read(std::istream&); + /// Serialize this metadata to a stream. + void write(std::ostream&) const; + + /// Create new metadata of the given type. + static Metadata::Ptr createMetadata(const Name& typeName); + + /// Return @c true if the given type is known by the metadata type registry. + static bool isRegisteredType(const Name& typeName); + + /// Clear out the metadata registry. + static void clearRegistry(); + + /// Register the given metadata type along with a factory function. + static void registerType(const Name& typeName, Metadata::Ptr (*createMetadata)()); + static void unregisterType(const Name& typeName); + +protected: + /// Read the size of the metadata from a stream. + static Index32 readSize(std::istream&); + /// Write the size of the metadata to a stream. + void writeSize(std::ostream&) const; + + /// Read the metadata from a stream. + virtual void readValue(std::istream&, Index32 numBytes) = 0; + /// Write the metadata to a stream. + virtual void writeValue(std::ostream&) const = 0; +}; + + +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + +/// @brief Subclass to hold raw data of an unregistered type +class OPENVDB_API UnknownMetadata: public Metadata +{ +public: + using ByteVec = std::vector; + + explicit UnknownMetadata(const Name& typ = ""): mTypeName(typ) {} + + Name typeName() const override { return mTypeName; } + Metadata::Ptr copy() const override; + void copy(const Metadata&) override; + std::string str() const override { return (mBytes.empty() ? "" : ""); } + bool asBool() const override { return !mBytes.empty(); } + Index32 size() const override { return static_cast(mBytes.size()); } + + void setValue(const ByteVec& bytes) { mBytes = bytes; } + const ByteVec& value() const { return mBytes; } + +protected: + void readValue(std::istream&, Index32 numBytes) override; + void writeValue(std::ostream&) const override; + +private: + Name mTypeName; + ByteVec mBytes; +}; + +#else // if OPENVDB_ABI_VERSION_NUMBER < 5 + +/// @brief Subclass to read (and ignore) data of an unregistered type +class OPENVDB_API UnknownMetadata: public Metadata +{ +public: + UnknownMetadata() {} + Name typeName() const override { return ""; } + Metadata::Ptr copy() const override { OPENVDB_THROW(TypeError, "Metadata has unknown type"); } + void copy(const Metadata&) override {OPENVDB_THROW(TypeError, "Destination has unknown type");} + std::string str() const override { return ""; } + bool asBool() const override { return false; } + Index32 size() const override { return 0; } + +protected: + void readValue(std::istream&, Index32 numBytes) override; + void writeValue(std::ostream&) const override; +}; + +#endif + + +/// @brief Templated metadata class to hold specific types. +template +class TypedMetadata: public Metadata +{ +public: + using Ptr = SharedPtr>; + using ConstPtr = SharedPtr>; + + TypedMetadata(); + TypedMetadata(const T& value); + TypedMetadata(const TypedMetadata& other); + ~TypedMetadata() override; + + Name typeName() const override; + Metadata::Ptr copy() const override; + void copy(const Metadata& other) override; + std::string str() const override; + bool asBool() const override; + Index32 size() const override { return static_cast(sizeof(T)); } + + /// Set this metadata's value. + void setValue(const T&); + /// Return this metadata's value. + T& value(); + const T& value() const; + + // Static specialized function for the type name. This function must be + // template specialized for each type T. + static Name staticTypeName() { return typeNameAsString(); } + + /// Create new metadata of this type. + static Metadata::Ptr createMetadata(); + + static void registerType(); + static void unregisterType(); + static bool isRegisteredType(); + +protected: + void readValue(std::istream&, Index32 numBytes) override; + void writeValue(std::ostream&) const override; + +private: + T mValue; +}; + +/// Write a Metadata to an output stream +std::ostream& operator<<(std::ostream& ostr, const Metadata& metadata); + + +//////////////////////////////////////// + + +inline void +Metadata::writeSize(std::ostream& os) const +{ + const Index32 n = this->size(); + os.write(reinterpret_cast(&n), sizeof(Index32)); +} + + +inline Index32 +Metadata::readSize(std::istream& is) +{ + Index32 n = 0; + is.read(reinterpret_cast(&n), sizeof(Index32)); + return n; +} + + +inline void +Metadata::read(std::istream& is) +{ + const Index32 numBytes = this->readSize(is); + this->readValue(is, numBytes); +} + + +inline void +Metadata::write(std::ostream& os) const +{ + this->writeSize(os); + this->writeValue(os); +} + + +//////////////////////////////////////// + + +template +inline +TypedMetadata::TypedMetadata() : mValue(T()) +{ +} + +template +inline +TypedMetadata::TypedMetadata(const T &value) : mValue(value) +{ +} + +template +inline +TypedMetadata::TypedMetadata(const TypedMetadata &other) : + Metadata(), + mValue(other.mValue) +{ +} + +template +inline +TypedMetadata::~TypedMetadata() +{ +} + +template +inline Name +TypedMetadata::typeName() const +{ + return TypedMetadata::staticTypeName(); +} + +template +inline void +TypedMetadata::setValue(const T& val) +{ + mValue = val; +} + +template +inline T& +TypedMetadata::value() +{ + return mValue; +} + +template +inline const T& +TypedMetadata::value() const +{ + return mValue; +} + +template +inline Metadata::Ptr +TypedMetadata::copy() const +{ + Metadata::Ptr metadata(new TypedMetadata()); + metadata->copy(*this); + return metadata; +} + +template +inline void +TypedMetadata::copy(const Metadata &other) +{ + const TypedMetadata* t = dynamic_cast*>(&other); + if (t == nullptr) OPENVDB_THROW(TypeError, "Incompatible type during copy"); + mValue = t->mValue; +} + + +template +inline void +TypedMetadata::readValue(std::istream& is, Index32 /*numBytes*/) +{ + //assert(this->size() == numBytes); + is.read(reinterpret_cast(&mValue), this->size()); +} + +template +inline void +TypedMetadata::writeValue(std::ostream& os) const +{ + os.write(reinterpret_cast(&mValue), this->size()); +} + +template +inline std::string +TypedMetadata::str() const +{ + std::ostringstream ostr; + ostr << mValue; + return ostr.str(); +} + +template +inline bool +TypedMetadata::asBool() const +{ + return !math::isZero(mValue); +} + +template +inline Metadata::Ptr +TypedMetadata::createMetadata() +{ + Metadata::Ptr ret(new TypedMetadata()); + return ret; +} + +template +inline void +TypedMetadata::registerType() +{ + Metadata::registerType(TypedMetadata::staticTypeName(), + TypedMetadata::createMetadata); +} + +template +inline void +TypedMetadata::unregisterType() +{ + Metadata::unregisterType(TypedMetadata::staticTypeName()); +} + +template +inline bool +TypedMetadata::isRegisteredType() +{ + return Metadata::isRegisteredType(TypedMetadata::staticTypeName()); +} + + +template<> +inline std::string +TypedMetadata::str() const +{ + return (mValue ? "true" : "false"); +} + + +inline std::ostream& +operator<<(std::ostream& ostr, const Metadata& metadata) +{ + ostr << metadata.str(); + return ostr; +} + + +using BoolMetadata = TypedMetadata; +using DoubleMetadata = TypedMetadata; +using FloatMetadata = TypedMetadata; +using Int32Metadata = TypedMetadata; +using Int64Metadata = TypedMetadata; +using StringMetadata = TypedMetadata; +using Vec2DMetadata = TypedMetadata; +using Vec2IMetadata = TypedMetadata; +using Vec2SMetadata = TypedMetadata; +using Vec3DMetadata = TypedMetadata; +using Vec3IMetadata = TypedMetadata; +using Vec3SMetadata = TypedMetadata; +using Vec4DMetadata = TypedMetadata; +using Vec4IMetadata = TypedMetadata; +using Vec4SMetadata = TypedMetadata; +using Mat4SMetadata = TypedMetadata; +using Mat4DMetadata = TypedMetadata; + + +//////////////////////////////////////// + + +template<> +inline Index32 +StringMetadata::size() const +{ + return static_cast(mValue.size()); +} + + +template<> +inline std::string +StringMetadata::str() const +{ + return mValue; +} + + +template<> +inline void +StringMetadata::readValue(std::istream& is, Index32 size) +{ + mValue.resize(size, '\0'); + is.read(&mValue[0], size); +} + +template<> +inline void +StringMetadata::writeValue(std::ostream& os) const +{ + os.write(reinterpret_cast(&mValue[0]), this->size()); +} + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_METADATA_HAS_BEEN_INCLUDED diff --git a/openvdb/Platform.cc b/openvdb/Platform.cc new file mode 100644 index 00000000..4a7f6115 --- /dev/null +++ b/openvdb/Platform.cc @@ -0,0 +1,7 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +// For Windows, we need these includes to ensure all OPENVDB_API +// functions/classes are compiled into the shared library. +#include "openvdb.h" +#include "Exceptions.h" diff --git a/openvdb/Platform.h b/openvdb/Platform.h new file mode 100644 index 00000000..2acf5b79 --- /dev/null +++ b/openvdb/Platform.h @@ -0,0 +1,241 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +/// +/// @file Platform.h + +#ifndef OPENVDB_PLATFORM_HAS_BEEN_INCLUDED +#define OPENVDB_PLATFORM_HAS_BEEN_INCLUDED + +#include "PlatformConfig.h" + +#define PRAGMA(x) _Pragma(#x) + +/// @name Utilities +/// @{ +/// @cond OPENVDB_VERSION_INTERNAL +#define OPENVDB_PREPROC_STRINGIFY_(x) #x +/// @endcond +/// @brief Return @a x as a string literal. If @a x is a macro, +/// return its value as a string literal. +/// @hideinitializer +#define OPENVDB_PREPROC_STRINGIFY(x) OPENVDB_PREPROC_STRINGIFY_(x) + +/// @cond OPENVDB_VERSION_INTERNAL +#define OPENVDB_PREPROC_CONCAT_(x, y) x ## y +/// @endcond +/// @brief Form a new token by concatenating two existing tokens. +/// If either token is a macro, concatenate its value. +/// @hideinitializer +#define OPENVDB_PREPROC_CONCAT(x, y) OPENVDB_PREPROC_CONCAT_(x, y) +/// @} + + +/// Use OPENVDB_DEPRECATED to mark functions as deprecated. +/// It should be placed right before the signature of the function, +/// e.g., "OPENVDB_DEPRECATED void functionName();". +#ifdef OPENVDB_DEPRECATED +#undef OPENVDB_DEPRECATED +#endif +#ifdef _MSC_VER + #define OPENVDB_DEPRECATED __declspec(deprecated) +#else + #define OPENVDB_DEPRECATED __attribute__ ((deprecated)) +#endif + +/// Macro for determining if GCC version is >= than X.Y +#if defined(__GNUC__) + #define OPENVDB_CHECK_GCC(MAJOR, MINOR) \ + (__GNUC__ > MAJOR || (__GNUC__ == MAJOR && __GNUC_MINOR__ >= MINOR)) +#else + #define OPENVDB_CHECK_GCC(MAJOR, MINOR) 0 +#endif + +/// OpenVDB now requires C++11 +#define OPENVDB_HAS_CXX11 1 + +/// For compilers that need templated function specializations to have +/// storage qualifiers, we need to declare the specializations as static inline. +/// Otherwise, we'll get linker errors about multiply defined symbols. +#if defined(__GNUC__) && OPENVDB_CHECK_GCC(4, 4) + #define OPENVDB_STATIC_SPECIALIZATION +#else + #define OPENVDB_STATIC_SPECIALIZATION static +#endif + + +/// SIMD Intrinsic Headers +#if defined(OPENVDB_USE_SSE42) || defined(OPENVDB_USE_AVX) + #if defined(_WIN32) + #include + #elif defined(__GNUC__) + #if defined(__x86_64__) || defined(__i386__) + #include + #elif defined(__ARM_NEON__) + #include + #endif + #endif +#endif + + +/// Bracket code with OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN/_END, +/// as in the following example, to inhibit ICC remarks about unreachable code: +/// @code +/// template +/// void processNode(NodeType& node) +/// { +/// OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN +/// if (NodeType::LEVEL == 0) return; // ignore leaf nodes +/// int i = 0; +/// ... +/// OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +/// } +/// @endcode +/// In the above, NodeType::LEVEL == 0 is a compile-time constant expression, +/// so for some template instantiations, the line below it is unreachable. +#if defined(__INTEL_COMPILER) + // Disable ICC remarks 111 ("statement is unreachable"), 128 ("loop is not reachable"), + // 185 ("dynamic initialization in unreachable code"), and 280 ("selector expression + // is constant"). + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN \ + _Pragma("warning (push)") \ + _Pragma("warning (disable:111)") \ + _Pragma("warning (disable:128)") \ + _Pragma("warning (disable:185)") \ + _Pragma("warning (disable:280)") + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END \ + _Pragma("warning (pop)") +#elif defined(__clang__) + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN \ + PRAGMA(clang diagnostic push) \ + PRAGMA(clang diagnostic ignored "-Wunreachable-code") + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END \ + PRAGMA(clang diagnostic pop) +#else + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +#endif + + +/// @brief Bracket code with OPENVDB_NO_DEPRECATION_WARNING_BEGIN/_END, +/// to inhibit warnings about deprecated code. +/// @note Use this sparingly. Remove references to deprecated code if at all possible. +/// @details Example: +/// @code +/// OPENVDB_DEPRECATED void myDeprecatedFunction() {} +/// +/// { +/// OPENVDB_NO_DEPRECATION_WARNING_BEGIN +/// myDeprecatedFunction(); +/// OPENVDB_NO_DEPRECATION_WARNING_END +/// } +/// @endcode +#if defined __INTEL_COMPILER + #define OPENVDB_NO_DEPRECATION_WARNING_BEGIN \ + _Pragma("warning (push)") \ + _Pragma("warning (disable:1478)") \ + PRAGMA(message("NOTE: ignoring deprecation warning at " __FILE__ \ + ":" OPENVDB_PREPROC_STRINGIFY(__LINE__))) + #define OPENVDB_NO_DEPRECATION_WARNING_END \ + _Pragma("warning (pop)") +#elif defined __clang__ + #define OPENVDB_NO_DEPRECATION_WARNING_BEGIN \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") + // note: no #pragma message, since Clang treats them as warnings + #define OPENVDB_NO_DEPRECATION_WARNING_END \ + _Pragma("clang diagnostic pop") +#elif defined __GNUC__ + #define OPENVDB_NO_DEPRECATION_WARNING_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ + _Pragma("message(\"NOTE: ignoring deprecation warning\")") + #define OPENVDB_NO_DEPRECATION_WARNING_END \ + _Pragma("GCC diagnostic pop") +#elif defined _MSC_VER + #define OPENVDB_NO_DEPRECATION_WARNING_BEGIN \ + __pragma(warning(push)) \ + __pragma(warning(disable : 4996)) \ + __pragma(message("NOTE: ignoring deprecation warning at " __FILE__ \ + ":" OPENVDB_PREPROC_STRINGIFY(__LINE__))) + #define OPENVDB_NO_DEPRECATION_WARNING_END \ + __pragma(warning(pop)) +#else + #define OPENVDB_NO_DEPRECATION_WARNING_BEGIN + #define OPENVDB_NO_DEPRECATION_WARNING_END +#endif + + +/// @brief Bracket code with OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN/_END, +/// to inhibit warnings about type conversion. +/// @note Use this sparingly. Use static casts and explicit type conversion if at all possible. +/// @details Example: +/// @code +/// float value = 0.1f; +/// OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN +/// int valueAsInt = value; +/// OPENVDB_NO_TYPE_CONVERSION_WARNING_END +/// @endcode +#if defined __INTEL_COMPILER + #define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + #define OPENVDB_NO_TYPE_CONVERSION_WARNING_END +#elif defined __GNUC__ + // -Wfloat-conversion was only introduced in GCC 4.9 + #if OPENVDB_CHECK_GCC(4, 9) + #define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wconversion\"") \ + _Pragma("GCC diagnostic ignored \"-Wfloat-conversion\"") + #else + #define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wconversion\"") + #endif + #define OPENVDB_NO_TYPE_CONVERSION_WARNING_END \ + _Pragma("GCC diagnostic pop") +#else + #define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + #define OPENVDB_NO_TYPE_CONVERSION_WARNING_END +#endif + +/// Helper macros for defining library symbol visibility +#ifdef OPENVDB_EXPORT +#undef OPENVDB_EXPORT +#endif +#ifdef OPENVDB_IMPORT +#undef OPENVDB_IMPORT +#endif +#ifdef __GNUC__ + #define OPENVDB_EXPORT __attribute__((visibility("default"))) + #define OPENVDB_IMPORT __attribute__((visibility("default"))) +#endif +#ifdef _WIN32 + #ifdef OPENVDB_DLL + #define OPENVDB_EXPORT __declspec(dllexport) + #define OPENVDB_IMPORT __declspec(dllimport) + #else + #define OPENVDB_EXPORT + #define OPENVDB_IMPORT + #endif +#endif + +/// All classes and public free standing functions must be explicitly marked +/// as \_API to be exported. The \_PRIVATE macros are defined when +/// building that particular library. +#ifdef OPENVDB_API +#undef OPENVDB_API +#endif +#ifdef OPENVDB_PRIVATE + #define OPENVDB_API OPENVDB_EXPORT +#else + #define OPENVDB_API OPENVDB_IMPORT +#endif +#ifdef OPENVDB_HOUDINI_API +#undef OPENVDB_HOUDINI_API +#endif +#ifdef OPENVDB_HOUDINI_PRIVATE + #define OPENVDB_HOUDINI_API OPENVDB_EXPORT +#else + #define OPENVDB_HOUDINI_API OPENVDB_IMPORT +#endif + +#endif // OPENVDB_PLATFORM_HAS_BEEN_INCLUDED diff --git a/openvdb/PlatformConfig.h b/openvdb/PlatformConfig.h new file mode 100644 index 00000000..8aab3546 --- /dev/null +++ b/openvdb/PlatformConfig.h @@ -0,0 +1,26 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +/// +/// @file PlatformConfig.h + +#ifndef OPENVDB_PLATFORMCONFIG_HAS_BEEN_INCLUDED +#define OPENVDB_PLATFORMCONFIG_HAS_BEEN_INCLUDED + +// Windows specific configuration +#ifdef _WIN32 + + // By default, assume we're building OpenVDB as a DLL if we're dynamically + // linking in the CRT, unless OPENVDB_STATICLIB is defined. + #if defined(_DLL) && !defined(OPENVDB_STATICLIB) && !defined(OPENVDB_DLL) + #define OPENVDB_DLL + #endif + + // By default, assume that we're dynamically linking OpenEXR, unless + // OPENVDB_OPENEXR_STATICLIB is defined. + #if !defined(OPENVDB_OPENEXR_STATICLIB) && !defined(OPENEXR_DLL) + #define OPENEXR_DLL + #endif + +#endif // _WIN32 + +#endif // OPENVDB_PLATFORMCONFIG_HAS_BEEN_INCLUDED diff --git a/openvdb/README b/openvdb/README new file mode 100644 index 00000000..664fc703 --- /dev/null +++ b/openvdb/README @@ -0,0 +1,16 @@ +======================================================================== + OpenVDB +======================================================================== + +The OpenVDB library comprises a hierarchical data structure and a suite +of tools for the efficient manipulation of sparse, possibly time-varying, +volumetric data discretized on a three-dimensional grid. + +For instructions on library installation and dependencies see INSTALL + +For documentation of the library and code examples see: +www.openvdb.org/documentation/doxygen + +For more details visit the project's home page: +www.openvdb.org + diff --git a/openvdb/Types.h b/openvdb/Types.h new file mode 100644 index 00000000..70269cf0 --- /dev/null +++ b/openvdb/Types.h @@ -0,0 +1,739 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_TYPES_HAS_BEEN_INCLUDED +#define OPENVDB_TYPES_HAS_BEEN_INCLUDED + +#include "version.h" +#include "Platform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +// One-dimensional scalar types +using Index32 = uint32_t; +using Index64 = uint64_t; +using Index = Index32; +using Int16 = int16_t; +using Int32 = int32_t; +using Int64 = int64_t; +using Int = Int32; +using Byte = unsigned char; +using Real = double; + +// Two-dimensional vector types +using Vec2R = math::Vec2; +using Vec2I = math::Vec2; +using Vec2f = math::Vec2; +using Vec2H = math::Vec2; +using math::Vec2i; +using math::Vec2s; +using math::Vec2d; + +// Three-dimensional vector types +using Vec3R = math::Vec3; +using Vec3I = math::Vec3; +using Vec3f = math::Vec3; +using Vec3H = math::Vec3; +using Vec3U8 = math::Vec3; +using Vec3U16 = math::Vec3; +using math::Vec3i; +using math::Vec3s; +using math::Vec3d; + +using math::Coord; +using math::CoordBBox; +using BBoxd = math::BBox; + +// Four-dimensional vector types +using Vec4R = math::Vec4; +using Vec4I = math::Vec4; +using Vec4f = math::Vec4; +using Vec4H = math::Vec4; +using math::Vec4i; +using math::Vec4s; +using math::Vec4d; + +// Three-dimensional matrix types +using Mat3R = math::Mat3; +using math::Mat3s; +using math::Mat3d; + +// Four-dimensional matrix types +using Mat4R = math::Mat4; +using math::Mat4s; +using math::Mat4d; + +// Quaternions +using QuatR = math::Quat; +using math::Quats; +using math::Quatd; + +// Dummy type for a voxel with a binary mask value, e.g. the active state +class ValueMask {}; + +// Use STL shared pointers from OpenVDB 4 on. +template using SharedPtr = std::shared_ptr; +template using WeakPtr = std::weak_ptr; + +/// @brief Return a new shared pointer that points to the same object +/// as the given pointer but with possibly different const-ness. +/// @par Example: +/// @code +/// FloatGrid::ConstPtr grid = ...; +/// FloatGrid::Ptr nonConstGrid = ConstPtrCast(grid); +/// FloatGrid::ConstPtr constGrid = ConstPtrCast(nonConstGrid); +/// @endcode +template inline SharedPtr +ConstPtrCast(const SharedPtr& ptr) { return std::const_pointer_cast(ptr); } + +/// @brief Return a new shared pointer that is either null or points to +/// the same object as the given pointer after a @c dynamic_cast. +/// @par Example: +/// @code +/// GridBase::ConstPtr grid = ...; +/// FloatGrid::ConstPtr floatGrid = DynamicPtrCast(grid); +/// @endcode +template inline SharedPtr +DynamicPtrCast(const SharedPtr& ptr) { return std::dynamic_pointer_cast(ptr); } + +/// @brief Return a new shared pointer that points to the same object +/// as the given pointer after a @c static_cast. +/// @par Example: +/// @code +/// FloatGrid::Ptr floatGrid = ...; +/// GridBase::Ptr grid = StaticPtrCast(floatGrid); +/// @endcode +template inline SharedPtr +StaticPtrCast(const SharedPtr& ptr) { return std::static_pointer_cast(ptr); } + + +//////////////////////////////////////// + + +/// @brief Integer wrapper, required to distinguish PointIndexGrid and +/// PointDataGrid from Int32Grid and Int64Grid +/// @note @c Kind is a dummy parameter used to create distinct types. +template +struct PointIndex +{ + static_assert(std::is_integral::value, "PointIndex requires an integer value type"); + + using IntType = IntType_; + + PointIndex(IntType i = IntType(0)): mIndex(i) {} + + /// Explicit type conversion constructor + template explicit PointIndex(T i): mIndex(static_cast(i)) {} + + operator IntType() const { return mIndex; } + + /// Needed to support the (zeroVal() + val) idiom. + template + PointIndex operator+(T x) { return PointIndex(mIndex + IntType(x)); } + +private: + IntType mIndex; +}; + + +using PointIndex32 = PointIndex; +using PointIndex64 = PointIndex; + +using PointDataIndex32 = PointIndex; +using PointDataIndex64 = PointIndex; + + +//////////////////////////////////////// + + +/// @brief Helper metafunction used to determine if the first template +/// parameter is a specialization of the class template given in the second +/// template parameter +template class Template> +struct IsSpecializationOf: public std::false_type {}; + +template class Template> +struct IsSpecializationOf, Template>: public std::true_type {}; + + +//////////////////////////////////////// + + +template::value || + IsSpecializationOf::value || + IsSpecializationOf::value> +struct VecTraits +{ + static const bool IsVec = true; + static const int Size = T::size; + using ElementType = typename T::ValueType; +}; + +template +struct VecTraits +{ + static const bool IsVec = false; + static const int Size = 1; + using ElementType = T; +}; + +template::value> +struct QuatTraits +{ + static const bool IsQuat = true; + static const int Size = T::size; + using ElementType = typename T::ValueType; +}; + +template +struct QuatTraits +{ + static const bool IsQuat = false; + static const int Size = 1; + using ElementType = T; +}; + +template::value || + IsSpecializationOf::value> +struct MatTraits +{ + static const bool IsMat = true; + static const int Size = T::size; + using ElementType = typename T::ValueType; +}; + +template +struct MatTraits +{ + static const bool IsMat = false; + static const int Size = 1; + using ElementType = T; +}; + +template::IsVec || + QuatTraits::IsQuat || + MatTraits::IsMat> +struct ValueTraits +{ + static const bool IsVec = VecTraits::IsVec; + static const bool IsQuat = QuatTraits::IsQuat; + static const bool IsMat = MatTraits::IsMat; + static const bool IsScalar = false; + static const int Size = T::size; + static const int Elements = IsMat ? Size*Size : Size; + using ElementType = typename T::ValueType; +}; + +template +struct ValueTraits +{ + static const bool IsVec = false; + static const bool IsQuat = false; + static const bool IsMat = false; + static const bool IsScalar = true; + static const int Size = 1; + static const int Elements = 1; + using ElementType = T; +}; + + +//////////////////////////////////////// + + +/// @brief CanConvertType::value is @c true if a value +/// of type @a ToType can be constructed from a value of type @a FromType. +template +struct CanConvertType { enum { value = std::is_constructible::value }; }; + +// Specializations for vector types, which can be constructed from values +// of their own ValueTypes (or values that can be converted to their ValueTypes), +// but only explicitly +template struct CanConvertType > { enum { value = true }; }; +template struct CanConvertType > { enum { value = true }; }; +template struct CanConvertType > { enum { value = true }; }; +template struct CanConvertType, math::Vec2 > { enum {value = true}; }; +template struct CanConvertType, math::Vec3 > { enum {value = true}; }; +template struct CanConvertType, math::Vec4 > { enum {value = true}; }; +template +struct CanConvertType > { enum { value = CanConvertType::value }; }; +template +struct CanConvertType > { enum { value = CanConvertType::value }; }; +template +struct CanConvertType > { enum { value = CanConvertType::value }; }; +template<> struct CanConvertType { enum {value = true}; }; +template<> struct CanConvertType { enum {value = true}; }; +template +struct CanConvertType { enum {value = CanConvertType::value}; }; +template +struct CanConvertType { enum {value = CanConvertType::value}; }; + + +//////////////////////////////////////// + + +/// @brief CopyConstness::Type is either const T2 +/// or @c T2 with no @c const qualifier, depending on whether @c T1 is @c const. +/// @details For example, +/// - CopyConstness::Type is @c int +/// - CopyConstness::Type is @c int +/// - CopyConstness::Type is const int +/// - CopyConstness::Type is const int +template struct CopyConstness { + using Type = typename std::remove_const::type; +}; + +/// @cond OPENVDB_TYPES_INTERNAL +template struct CopyConstness { + using Type = const ToType; +}; +/// @endcond + + +//////////////////////////////////////// + + +/// @cond OPENVDB_TYPES_INTERNAL + +template struct TypeList; // forward declaration + +namespace internal { + +// Implementation details of TypeList + +template struct TSAppendImpl; + +// Append zero or more types. +template +struct TSAppendImpl, OtherTs...> { + using type = TypeList; +}; + +// Append another TypeList's members. +template +struct TSAppendImpl, TypeList> { + using type = TypeList; +}; + + +// Remove all occurrences of type T. +template struct TSEraseImpl; + +// TypeList<>::Erase = TypeList<> +template +struct TSEraseImpl, T> { using type = TypeList<>; }; + +// TypeList::Erase = TypeList::Erase +template +struct TSEraseImpl, T> { + using type = typename TSEraseImpl, T>::type; +}; + +// TypeList::Erase = +// TypeList::Append::Erase> +template +struct TSEraseImpl, T> { + using type = typename TSAppendImpl, + typename TSEraseImpl, T>::type>::type; +}; + + +template struct TSRemoveImpl; + +template +struct TSRemoveImpl { using type = ListT; }; + +// Remove one or more types. +template +struct TSRemoveImpl { + using type = typename TSRemoveImpl::type, Ts...>::type; +}; + +// Remove the members of another TypeList. +template +struct TSRemoveImpl> { + using type = typename TSRemoveImpl::type; +}; + + +template inline void TSForEachImpl(OpT) {} +template +inline void TSForEachImpl(OpT op) { op(T()); TSForEachImpl(op); } + +} // namespace internal + +/// @endcond + + +/// @brief A list of types (not necessarily unique) +/// @details Example: +/// @code +/// using MyTypes = openvdb::TypeList; +/// @endcode +template +struct TypeList +{ + /// The type of this list + using Self = TypeList; + + /// @brief Append types, or the members of another TypeList, to this list. + /// @details Example: + /// @code + /// { + /// using IntTypes = openvdb::TypeList; + /// using RealTypes = openvdb::TypeList; + /// using NumericTypes = IntTypes::Append; + /// } + /// { + /// using IntTypes = openvdb::TypeList::Append; + /// using NumericTypes = IntTypes::Append::Append; + /// } + /// @endcode + template + using Append = typename internal::TSAppendImpl::type; + + /// @brief Remove all occurrences of one or more types, or the members of + /// another TypeList, from this list. + /// @details Example: + /// @code + /// { + /// using NumericTypes = openvdb::TypeList; + /// using LongTypes = openvdb::TypeList; + /// using ShortTypes = NumericTypes::Remove; // float, Int16, Int32 + /// } + /// @endcode + template + using Remove = typename internal::TSRemoveImpl::type; + + /// @brief Invoke a templated, unary functor on a value of each type in this list. + /// @details Example: + /// @code + /// #include + /// + /// template + /// void printTypeList() + /// { + /// std::string sep; + /// auto op = [&](auto x) { // C++14 + /// std::cout << sep << typeid(decltype(x)).name(); sep = ", "; }; + /// ListT::foreach(op); + /// } + /// + /// using MyTypes = openvdb::TypeList; + /// printTypeList(); // "i, f, d" (exact output is compiler-dependent) + /// @endcode + /// + /// @note The functor object is passed by value. Wrap it with @c std::ref + /// to use the same object for each type. + template + static void foreach(OpT op) { internal::TSForEachImpl(op); } +}; + + +//////////////////////////////////////// + + +// Add new items to the *end* of this list, and update NUM_GRID_CLASSES. +enum GridClass { + GRID_UNKNOWN = 0, + GRID_LEVEL_SET, + GRID_FOG_VOLUME, + GRID_STAGGERED +}; +enum { NUM_GRID_CLASSES = GRID_STAGGERED + 1 }; + +static const Real LEVEL_SET_HALF_WIDTH = 3; + +/// The type of a vector determines how transforms are applied to it: +///
+///
Invariant +///
Does not transform (e.g., tuple, uvw, color) +/// +///
Covariant +///
Apply inverse-transpose transformation: @e w = 0, ignores translation +/// (e.g., gradient/normal) +/// +///
Covariant Normalize +///
Apply inverse-transpose transformation: @e w = 0, ignores translation, +/// vectors are renormalized (e.g., unit normal) +/// +///
Contravariant Relative +///
Apply "regular" transformation: @e w = 0, ignores translation +/// (e.g., displacement, velocity, acceleration) +/// +///
Contravariant Absolute +///
Apply "regular" transformation: @e w = 1, vector translates (e.g., position) +///
+enum VecType { + VEC_INVARIANT = 0, + VEC_COVARIANT, + VEC_COVARIANT_NORMALIZE, + VEC_CONTRAVARIANT_RELATIVE, + VEC_CONTRAVARIANT_ABSOLUTE +}; +enum { NUM_VEC_TYPES = VEC_CONTRAVARIANT_ABSOLUTE + 1 }; + + +/// Specify how grids should be merged during certain (typically multithreaded) operations. +///
+///
MERGE_ACTIVE_STATES +///
The output grid is active wherever any of the input grids is active. +/// +///
MERGE_NODES +///
The output grid's tree has a node wherever any of the input grids' trees +/// has a node, regardless of any active states. +/// +///
MERGE_ACTIVE_STATES_AND_NODES +///
The output grid is active wherever any of the input grids is active, +/// and its tree has a node wherever any of the input grids' trees has a node. +///
+enum MergePolicy { + MERGE_ACTIVE_STATES = 0, + MERGE_NODES, + MERGE_ACTIVE_STATES_AND_NODES +}; + + +//////////////////////////////////////// + + +template const char* typeNameAsString() { return typeid(T).name(); } +template<> inline const char* typeNameAsString() { return "bool"; } +template<> inline const char* typeNameAsString() { return "mask"; } +template<> inline const char* typeNameAsString() { return "half"; } +template<> inline const char* typeNameAsString() { return "float"; } +template<> inline const char* typeNameAsString() { return "double"; } +template<> inline const char* typeNameAsString() { return "int8"; } +template<> inline const char* typeNameAsString() { return "uint8"; } +template<> inline const char* typeNameAsString() { return "int16"; } +template<> inline const char* typeNameAsString() { return "uint16"; } +template<> inline const char* typeNameAsString() { return "int32"; } +template<> inline const char* typeNameAsString() { return "uint32"; } +template<> inline const char* typeNameAsString() { return "int64"; } +template<> inline const char* typeNameAsString() { return "vec2i"; } +template<> inline const char* typeNameAsString() { return "vec2s"; } +template<> inline const char* typeNameAsString() { return "vec2d"; } +template<> inline const char* typeNameAsString() { return "vec3u8"; } +template<> inline const char* typeNameAsString() { return "vec3u16"; } +template<> inline const char* typeNameAsString() { return "vec3i"; } +template<> inline const char* typeNameAsString() { return "vec3s"; } +template<> inline const char* typeNameAsString() { return "vec3d"; } +template<> inline const char* typeNameAsString() { return "vec4i"; } +template<> inline const char* typeNameAsString() { return "vec4s"; } +template<> inline const char* typeNameAsString() { return "vec4d"; } +template<> inline const char* typeNameAsString() { return "string"; } +template<> inline const char* typeNameAsString() { return "mat3s"; } +template<> inline const char* typeNameAsString() { return "mat3d"; } +template<> inline const char* typeNameAsString() { return "mat4s"; } +template<> inline const char* typeNameAsString() { return "mat4d"; } +template<> inline const char* typeNameAsString() { return "quats"; } +template<> inline const char* typeNameAsString() { return "quatd"; } +template<> inline const char* typeNameAsString() { return "ptidx32"; } +template<> inline const char* typeNameAsString() { return "ptidx64"; } +template<> inline const char* typeNameAsString() { return "ptdataidx32"; } +template<> inline const char* typeNameAsString() { return "ptdataidx64"; } + + +//////////////////////////////////////// + + +/// @brief This struct collects both input and output arguments to "grid combiner" functors +/// used with the tree::TypedGrid::combineExtended() and combine2Extended() methods. +/// AValueType and BValueType are the value types of the two grids being combined. +/// +/// @see openvdb/tree/Tree.h for usage information. +/// +/// Setter methods return references to this object, to facilitate the following usage: +/// @code +/// CombineArgs args; +/// myCombineOp(args.setARef(aVal).setBRef(bVal).setAIsActive(true).setBIsActive(false)); +/// @endcode +template +class CombineArgs +{ +public: + using AValueT = AValueType; + using BValueT = BValueType; + + CombineArgs() + : mAValPtr(nullptr) + , mBValPtr(nullptr) + , mResultValPtr(&mResultVal) + , mAIsActive(false) + , mBIsActive(false) + , mResultIsActive(false) + { + } + + /// Use this constructor when the result value is stored externally. + CombineArgs(const AValueType& a, const BValueType& b, AValueType& result, + bool aOn = false, bool bOn = false) + : mAValPtr(&a) + , mBValPtr(&b) + , mResultValPtr(&result) + , mAIsActive(aOn) + , mBIsActive(bOn) + { + this->updateResultActive(); + } + + /// Use this constructor when the result value should be stored in this struct. + CombineArgs(const AValueType& a, const BValueType& b, bool aOn = false, bool bOn = false) + : mAValPtr(&a) + , mBValPtr(&b) + , mResultValPtr(&mResultVal) + , mAIsActive(aOn) + , mBIsActive(bOn) + { + this->updateResultActive(); + } + + /// Get the A input value. + const AValueType& a() const { return *mAValPtr; } + /// Get the B input value. + const BValueType& b() const { return *mBValPtr; } + //@{ + /// Get the output value. + const AValueType& result() const { return *mResultValPtr; } + AValueType& result() { return *mResultValPtr; } + //@} + + /// Set the output value. + CombineArgs& setResult(const AValueType& val) { *mResultValPtr = val; return *this; } + + /// Redirect the A value to a new external source. + CombineArgs& setARef(const AValueType& a) { mAValPtr = &a; return *this; } + /// Redirect the B value to a new external source. + CombineArgs& setBRef(const BValueType& b) { mBValPtr = &b; return *this; } + /// Redirect the result value to a new external destination. + CombineArgs& setResultRef(AValueType& val) { mResultValPtr = &val; return *this; } + + /// @return true if the A value is active + bool aIsActive() const { return mAIsActive; } + /// @return true if the B value is active + bool bIsActive() const { return mBIsActive; } + /// @return true if the output value is active + bool resultIsActive() const { return mResultIsActive; } + + /// Set the active state of the A value. + CombineArgs& setAIsActive(bool b) { mAIsActive = b; updateResultActive(); return *this; } + /// Set the active state of the B value. + CombineArgs& setBIsActive(bool b) { mBIsActive = b; updateResultActive(); return *this; } + /// Set the active state of the output value. + CombineArgs& setResultIsActive(bool b) { mResultIsActive = b; return *this; } + +protected: + /// By default, the result value is active if either of the input values is active, + /// but this behavior can be overridden by calling setResultIsActive(). + void updateResultActive() { mResultIsActive = mAIsActive || mBIsActive; } + + const AValueType* mAValPtr; // pointer to input value from A grid + const BValueType* mBValPtr; // pointer to input value from B grid + AValueType mResultVal; // computed output value (unused if stored externally) + AValueType* mResultValPtr; // pointer to either mResultVal or an external value + bool mAIsActive, mBIsActive; // active states of A and B values + bool mResultIsActive; // computed active state (default: A active || B active) +}; + + +/// This struct adapts a "grid combiner" functor to swap the A and B grid values +/// (e.g., so that if the original functor computes a + 2 * b, the adapted functor +/// will compute b + 2 * a). +template +struct SwappedCombineOp +{ + SwappedCombineOp(CombineOp& _op): op(_op) {} + + void operator()(CombineArgs& args) + { + CombineArgs swappedArgs(args.b(), args.a(), args.result(), + args.bIsActive(), args.aIsActive()); + op(swappedArgs); + } + + CombineOp& op; +}; + + +//////////////////////////////////////// + + +/// @brief Tag dispatch class that distinguishes shallow copy constructors +/// from deep copy constructors +class ShallowCopy {}; +/// @brief Tag dispatch class that distinguishes topology copy constructors +/// from deep copy constructors +class TopologyCopy {}; +/// @brief Tag dispatch class that distinguishes constructors during file input +class PartialCreate {}; + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#if defined(__ICC) + +// Use these defines to bracket a region of code that has safe static accesses. +// Keep the region as small as possible. +#define OPENVDB_START_THREADSAFE_STATIC_REFERENCE __pragma(warning(disable:1710)) +#define OPENVDB_FINISH_THREADSAFE_STATIC_REFERENCE __pragma(warning(default:1710)) +#define OPENVDB_START_THREADSAFE_STATIC_WRITE __pragma(warning(disable:1711)) +#define OPENVDB_FINISH_THREADSAFE_STATIC_WRITE __pragma(warning(default:1711)) +#define OPENVDB_START_THREADSAFE_STATIC_ADDRESS __pragma(warning(disable:1712)) +#define OPENVDB_FINISH_THREADSAFE_STATIC_ADDRESS __pragma(warning(default:1712)) + +// Use these defines to bracket a region of code that has unsafe static accesses. +// Keep the region as small as possible. +#define OPENVDB_START_NON_THREADSAFE_STATIC_REFERENCE __pragma(warning(disable:1710)) +#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_REFERENCE __pragma(warning(default:1710)) +#define OPENVDB_START_NON_THREADSAFE_STATIC_WRITE __pragma(warning(disable:1711)) +#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_WRITE __pragma(warning(default:1711)) +#define OPENVDB_START_NON_THREADSAFE_STATIC_ADDRESS __pragma(warning(disable:1712)) +#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_ADDRESS __pragma(warning(default:1712)) + +// Simpler version for one-line cases +#define OPENVDB_THREADSAFE_STATIC_REFERENCE(CODE) \ + __pragma(warning(disable:1710)); CODE; __pragma(warning(default:1710)) +#define OPENVDB_THREADSAFE_STATIC_WRITE(CODE) \ + __pragma(warning(disable:1711)); CODE; __pragma(warning(default:1711)) +#define OPENVDB_THREADSAFE_STATIC_ADDRESS(CODE) \ + __pragma(warning(disable:1712)); CODE; __pragma(warning(default:1712)) + +#else // GCC does not support these compiler warnings + +#define OPENVDB_START_THREADSAFE_STATIC_REFERENCE +#define OPENVDB_FINISH_THREADSAFE_STATIC_REFERENCE +#define OPENVDB_START_THREADSAFE_STATIC_WRITE +#define OPENVDB_FINISH_THREADSAFE_STATIC_WRITE +#define OPENVDB_START_THREADSAFE_STATIC_ADDRESS +#define OPENVDB_FINISH_THREADSAFE_STATIC_ADDRESS + +#define OPENVDB_START_NON_THREADSAFE_STATIC_REFERENCE +#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_REFERENCE +#define OPENVDB_START_NON_THREADSAFE_STATIC_WRITE +#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_WRITE +#define OPENVDB_START_NON_THREADSAFE_STATIC_ADDRESS +#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_ADDRESS + +#define OPENVDB_THREADSAFE_STATIC_REFERENCE(CODE) CODE +#define OPENVDB_THREADSAFE_STATIC_WRITE(CODE) CODE +#define OPENVDB_THREADSAFE_STATIC_ADDRESS(CODE) CODE + +#endif // defined(__ICC) + +#endif // OPENVDB_TYPES_HAS_BEEN_INCLUDED diff --git a/openvdb/cmd/CMakeLists.txt b/openvdb/cmd/CMakeLists.txt new file mode 100644 index 00000000..1e5ef0db --- /dev/null +++ b/openvdb/cmd/CMakeLists.txt @@ -0,0 +1,220 @@ +## Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: MPL-2.0 +# +#[=======================================================================[ + + CMake Configuration for OpenVDB Binaries + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.3) +project(OpenVDBBinaries) + +# Monitoring _ROOT variables +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif() + +###### OpenVDB Binary Component Options + +option(OPENVDB_BUILD_VDB_PRINT "Build vdb_print" ON) +option(OPENVDB_BUILD_VDB_LOD "Build vdb_lod" OFF) +option(OPENVDB_BUILD_VDB_RENDER "Build vdb_render" OFF) +option(OPENVDB_BUILD_VDB_VIEW "Build vdb_view" OFF) + +######################################################################### + +message(STATUS "----------------------------------------------------") +message(STATUS "----------- Configuring OpenVDBBinaries ------------") +message(STATUS "----------------------------------------------------") + +########################################################################## + +# Collect lib dependencies shared by all binaries + +if(NOT OPENVDB_BUILD_CORE) + set(OPENVDB_LIB OpenVDB::openvdb) +else() + set(OPENVDB_LIB openvdb) +endif() + +set(OPENVDB_BINARIES_DEPENDENT_LIBS + ${OPENVDB_LIB} +) + +########################################################################## + +if(WIN32) + # Because of implicit linking! + link_directories(${Boost_LIBRARY_DIR}) + if(OPENVDB_DISABLE_BOOST_IMPLICIT_LINKING) + add_definitions(-DBOOST_ALL_NO_LIB) + endif() +endif() + +if(WIN32) + add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_DLL) +endif() + +# rpath handling + +set(RPATHS "") +if(OPENVDB_ENABLE_RPATH) + # @todo There is probably a better way to do this for imported targets + list(APPEND RPATHS + ${Boost_LIBRARY_DIRS} + ${IlmBase_LIBRARY_DIRS} + ${Log4cplus_LIBRARY_DIRS} + ${Blosc_LIBRARY_DIRS} + ${Tbb_LIBRARY_DIRS} + ) + if(OPENVDB_BUILD_CORE) + list(APPEND RPATHS ${CMAKE_INSTALL_PREFIX}/lib) + else() + list(APPEND RPATHS ${OpenVDB_LIBRARY_DIRS}) + endif() + + list(REMOVE_DUPLICATES RPATHS) +endif() + +########################################################################## + +##### VDB binaries + +#### vdb_print + +if(OPENVDB_BUILD_VDB_PRINT) + set(VDB_PRINT_SOURCE_FILES openvdb_print.cc) + add_executable(vdb_print ${VDB_PRINT_SOURCE_FILES}) + target_link_libraries(vdb_print ${OPENVDB_BINARIES_DEPENDENT_LIBS}) + + if(OPENVDB_ENABLE_RPATH) + set_target_properties(vdb_print + PROPERTIES INSTALL_RPATH "${RPATHS}" + ) + endif() + + install(TARGETS vdb_print DESTINATION bin) +endif() + +########################################################################## + +#### vdb_lod + +if(OPENVDB_BUILD_VDB_LOD) + set(VDB_LOD_SOURCE_FILES openvdb_lod.cc) + add_executable(vdb_lod ${VDB_LOD_SOURCE_FILES}) + target_link_libraries(vdb_lod ${OPENVDB_BINARIES_DEPENDENT_LIBS}) + + if(OPENVDB_ENABLE_RPATH) + set_target_properties(vdb_lod + PROPERTIES INSTALL_RPATH "${RPATHS}" + ) + endif() + + install(TARGETS vdb_lod DESTINATION bin) +endif() + +########################################################################## + +#### vdb_render + +if(OPENVDB_BUILD_VDB_RENDER) + find_package(IlmBase ${MINIMUM_ILMBASE_VERSION} REQUIRED COMPONENTS Half Iex IlmThread) + find_package(OpenEXR ${MINIMUM_OPENEXR_VERSION} REQUIRED COMPONENTS IlmImf) + + set(VDB_RENDER_SOURCE_FILES openvdb_render.cc) + add_executable(vdb_render ${VDB_RENDER_SOURCE_FILES}) + + # Set deps. Note that the order here is important. If we're building against + # Houdini 17.5 we must include OpenEXR and IlmBase deps first to ensure the + # users chosen namespaced headers are correctly prioritized. Otherwise other + # include paths from shared installs (including houdini) may pull in the wrong + # headers + + target_link_libraries(vdb_render + OpenEXR::IlmImf + IlmBase::IlmThread + IlmBase::Iex + ${OPENVDB_BINARIES_DEPENDENT_LIBS} + ) + + if(OPENVDB_ENABLE_RPATH) + set(OPENVDB_RENDER_RPATHS) + list(APPEND OPENVDB_RENDER_RPATHS + ${OpenEXR_LIBRARY_DIRS} + ${RPATHS} + ) + list(REMOVE_DUPLICATES OPENVDB_RENDER_RPATHS) + + set_target_properties(vdb_render + PROPERTIES INSTALL_RPATH "${OPENVDB_RENDER_RPATHS}" + ) + unset(OPENVDB_RENDER_RPATHS) + endif() + + install(TARGETS vdb_render DESTINATION bin) +endif() + +########################################################################## + +#### vdb_view + +if(OPENVDB_BUILD_VDB_VIEW) + + # @todo improve the viewer header system + + file(GLOB VIEWER_SOURCE_FILES ../viewer/*.h) + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/dwa/openvdb_viewer) + file(COPY ${VIEWER_SOURCE_FILES} DESTINATION ${CMAKE_BINARY_DIR}/dwa/openvdb_viewer) + + if(WIN32) + message(WARNING "Currently no CMake support for building vdb_view binary on Windows.") + # @todo + # find_package(GLEW REQUIRED) + # INCLUDE_DIRECTORIES ( SYSTEM ${GLEW_INCLUDE_DIR}) + else() + find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS thread) + find_package(OpenGL REQUIRED) + + # wraps find_package(glfw3) and sets the glfw target + include(OpenVDBGLFW3Setup) + + set(VDB_VIEW_SOURCE_FILES + openvdb_view.cc + ../viewer/Camera.cc + ../viewer/ClipBox.cc + ../viewer/Font.cc + ../viewer/RenderModules.cc + ../viewer/Viewer.cc + ) + + add_executable(vdb_view ${VDB_VIEW_SOURCE_FILES}) + + target_include_directories(vdb_view + PRIVATE ${CMAKE_BINARY_DIR}/dwa + ) + + target_link_libraries(vdb_view + ${OPENVDB_BINARIES_DEPENDENT_LIBS} + Boost::thread + OpenGL::GL + OpenGL::GLU + glfw + ) + + target_compile_definitions(vdb_view PRIVATE + "-DOPENVDB_USE_GLFW_3" "-DGL_GLEXT_PROTOTYPES=1" + ) + + if(OPENVDB_ENABLE_RPATH) + set_target_properties(vdb_view + PROPERTIES INSTALL_RPATH "${RPATHS}" + ) + endif() + + install(TARGETS vdb_view DESTINATION bin) + endif() +endif() + +unset(RPATHS) diff --git a/openvdb/cmd/openvdb_lod.cc b/openvdb/cmd/openvdb_lod.cc new file mode 100644 index 00000000..ff174da7 --- /dev/null +++ b/openvdb/cmd/openvdb_lod.cc @@ -0,0 +1,358 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include // for boost::is_any_of() +#include +#include // for std::atof() +#include // for std::setprecision() +#include +#include +#include +#include // for std::runtime_error +#include + + +namespace { + +const char* gProgName = ""; + +inline void +usage [[noreturn]] (int exitStatus = EXIT_FAILURE) +{ + std::cerr << +"Usage: " << gProgName << " in.vdb out.vdb -range FROM[-TO[:STEP]] [options]\n" << +"Which: generates a volume mipmap from an OpenVDB grid\n" << +"Where:\n" << +" FROM is the highest-resolution mip level to be generated\n" << +" TO is the lowest-resolution mip level to be generated (default: FROM)\n" << +" STEP is the mip level step size (default: 1)\n" << +"Options:\n" << +" -name S[,S,S,...] name(s) of the grid(s) to be processed\n" << +" (default: process all grids of supported types)\n" << +" -keep pass through grids that were not processed\n" << +" (default: discard grids that were not processed)\n" << +" -nokeep cancel an earlier -keep option\n" << +" -p, -preserve if only one mip level is generated, give it the same\n" << +" name as the original grid (default: name each level\n" << +" \"NAME_level_N\", where NAME is the original grid name\n" << +" and N is the level number, e.g., \"density_level_0\")\n" << +" -nopreserve cancel an earlier -p or -preserve option\n" << +" -version print version information\n" << +"\n" << +"Mip level 0 is the input grid. Each successive integer level is half\n" << +"the resolution of the previous level. Fractional levels are supported.\n" << +"\n" << +"Examples:\n" << +" Generate levels 0, 1, and 2 (full resolution, half resolution,\n" << +" and quarter resolution, respectively) for all grids of supported types\n" << +" and ignore all other grids:\n" << +"\n" << +" " << gProgName << " in.vdb out.vdb -range 0-2\n" << +"\n" << +" Generate levels 0, 0.5, and 1 for all grids of supported types\n" << +" and pass through all other grids:\n" << +"\n" << +" " << gProgName << " in.vdb out.vdb -range 0-1:0.5 -keep\n" << +"\n" << +" Generate level 3 for the first of multiple grids named \"density\":\n" << +"\n" << +" " << gProgName << " in.vdb out.vdb -range 3 -name 'density[0]'\n" << +"\n" << +" Generate level 1.5 for the second of multiple unnamed grids and for\n" << +" the grid named \"velocity\" and give the resulting grids the same names\n"<< +" as the original grids:\n" << +"\n" << +" " << gProgName << " in.vdb out.vdb -range 1.5 -name '[1],velocity' -p\n" << +"\n"; + exit(exitStatus); +} + + +struct Options +{ + Options(): from(0.0), to(0.0), step(1.0), keep(false), preserve(false) {} + + double from, to, step; + bool keep, preserve; +}; + + +/// @brief Parse a string of the form "from-to:step" and populate the given @a opts +/// with the resulting values. +/// @throw std::runtime_error if parsing fails for any reason +inline void +parseRangeSpec(const std::string& rangeSpec, Options& opts) +{ + // Split on the "-" character, of which there should be at most one. + std::vector rangeItems; + boost::split(rangeItems, rangeSpec, boost::is_any_of("-")); + if (rangeItems.empty() || rangeItems.size() > 2) throw std::runtime_error(""); + + // Extract the "from" value, and default "to" to "from" and "step" to 1. + opts.from = opts.to = std::atof(rangeItems[0].c_str()); + opts.step = 1.0; + + if (rangeItems.size() > 1) { + // Split on the ":" character, of which there should be at most one. + const std::string item = rangeItems[1]; + boost::split(rangeItems, item, boost::is_any_of(":")); + if (rangeItems.empty() || rangeItems.size() > 2) throw std::runtime_error(""); + + // Extract the "to" value. + opts.to = std::atof(rangeItems[0].c_str()); + if (rangeItems.size() > 1) { + // Extract the "step" value. + opts.step = std::atof(rangeItems[1].c_str()); + } + } + + if (opts.from < 0.0 || opts.to < opts.from || opts.step <= 0.0) throw std::runtime_error(""); +} + + +/// @brief Mipmap a single grid of a fully-resolved type. +/// @return a vector of pointers to the member grids of the mipmap +template +inline openvdb::GridPtrVec +mip(const GridType& inGrid, const Options& opts) +{ + OPENVDB_LOG_INFO("processing grid \"" << inGrid.getName() << "\""); + + // MultiResGrid requires at least two mipmap levels, starting from level 0. + const int levels = std::max(2, openvdb::math::Ceil(opts.to) + 1); + + openvdb::util::CpuTimer timer; + timer.start(); + + // Initialize the mipmap. + typedef typename GridType::TreeType TreeT; + openvdb::tools::MultiResGrid mrg(levels, inGrid); + + openvdb::GridPtrVec outGrids; + for (double level = opts.from; level <= opts.to; level += opts.step) { + // Request a level from the mipmap. + if (openvdb::GridBase::Ptr levelGrid = + mrg.template createGrid(static_cast(level))) + { + outGrids.push_back(levelGrid); + } + } + + if (outGrids.size() == 1 && opts.preserve) { + // If -preserve is in effect and there is only one output grid, + // give it the same name as the input grid. + outGrids[0]->setName(inGrid.getName()); + } + + OPENVDB_LOG_INFO("processed grid \"" << inGrid.getName() << "\" in " + << std::setprecision(3) << timer.seconds() << " sec"); + + return outGrids; +} + + +/// @brief Mipmap a single grid and append the resulting grids to @a outGrids. +inline void +process(const openvdb::GridBase::Ptr& baseGrid, openvdb::GridPtrVec& outGrids, const Options& opts) +{ + using namespace openvdb; + + if (!baseGrid) return; + + GridPtrVec mipmap; + if (FloatGrid::Ptr g0 = GridBase::grid(baseGrid)) { mipmap = mip(*g0, opts); } + else if (DoubleGrid::Ptr g1 = GridBase::grid(baseGrid)) { mipmap = mip(*g1, opts); } + else if (Vec3SGrid::Ptr g2 = GridBase::grid(baseGrid)) { mipmap = mip(*g2, opts); } + else if (Vec3DGrid::Ptr g3 = GridBase::grid(baseGrid)) { mipmap = mip(*g3, opts); } + else if (Vec3IGrid::Ptr g4 = GridBase::grid(baseGrid)) { mipmap = mip(*g4, opts); } + else if (Int32Grid::Ptr g5 = GridBase::grid(baseGrid)) { mipmap = mip(*g5, opts); } + else if (Int64Grid::Ptr g6 = GridBase::grid(baseGrid)) { mipmap = mip(*g6, opts); } + else if (BoolGrid::Ptr g7 = GridBase::grid(baseGrid)) { mipmap = mip(*g7, opts); } + else if (MaskGrid::Ptr g8 = GridBase::grid(baseGrid)) { mipmap = mip(*g8, opts); } + else { + std::string operation = "skipped"; + if (opts.keep) { + operation = "passed through"; + outGrids.push_back(baseGrid); + }; + OPENVDB_LOG_WARN(operation << " grid \"" << baseGrid->getName() + << "\" of unsupported type " << baseGrid->type()); + } + outGrids.insert(outGrids.end(), mipmap.begin(), mipmap.end()); +} + +} // unnamed namespace + + +int +main(int argc, char *argv[]) +{ + OPENVDB_START_THREADSAFE_STATIC_WRITE + gProgName = argv[0]; + if (const char* ptr = ::strrchr(gProgName, '/')) gProgName = ptr + 1; + OPENVDB_FINISH_THREADSAFE_STATIC_WRITE + + int exitStatus = EXIT_SUCCESS; + + if (argc == 1) usage(); + + openvdb::logging::initialize(argc, argv); + openvdb::initialize(); + + // Parse command-line arguments. + Options opts; + bool version = false; + std::string inFilename, outFilename, gridNameStr, rangeSpec; + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg[0] == '-') { + if (arg == "-name") { + if (i + 1 < argc && argv[i + 1]) { + gridNameStr = argv[i + 1]; + ++i; + } else { + OPENVDB_LOG_FATAL("missing grid name(s) after -name"); + usage(); + } + } else if (arg == "-keep") { + opts.keep = true; + } else if (arg == "-nokeep") { + opts.keep = false; + } else if (arg == "-p" || arg == "-preserve") { + opts.preserve = true; + } else if (arg == "-nopreserve") { + opts.preserve = false; + } else if (arg == "-range") { + if (i + 1 < argc && argv[i + 1]) { + rangeSpec = argv[i + 1]; + ++i; + } else { + OPENVDB_LOG_FATAL("missing level range specification after -range"); + usage(); + } + } else if (arg == "-h" || arg == "-help" || arg == "--help") { + usage(EXIT_SUCCESS); + } else if (arg == "-version" || arg == "--version") { + version = true; + } else { + OPENVDB_LOG_FATAL("\"" << arg << "\" is not a valid option"); + usage(); + } + } else if (!arg.empty()) { + if (inFilename.empty()) { + inFilename = arg; + } else if (outFilename.empty()) { + outFilename = arg; + } else { + OPENVDB_LOG_FATAL("unrecognized argument \"" << arg << "\""); + usage(); + } + } + } + + if (version) { + std::cout << "OpenVDB library version: " + << openvdb::getLibraryAbiVersionString() << "\n"; + std::cout << "OpenVDB file format version: " + << openvdb::OPENVDB_FILE_VERSION << std::endl; + if (outFilename.empty()) return EXIT_SUCCESS; + } + + if (inFilename.empty()) { + OPENVDB_LOG_FATAL("missing input OpenVDB filename"); + usage(); + } + if (outFilename.empty()) { + OPENVDB_LOG_FATAL("missing output OpenVDB filename"); + usage(); + } + if (rangeSpec.empty()) { + OPENVDB_LOG_FATAL("missing level range specification"); + usage(); + } + + try { + parseRangeSpec(rangeSpec, opts); + } catch (...) { + OPENVDB_LOG_FATAL("invalid level range specification \"" << rangeSpec << "\""); + usage(); + } + + // If -name was specified, generate a white list of names of grids to be processed. + // Otherwise (if the white list is empty), process all grids of supported types. + std::set whitelist; + if (!gridNameStr.empty()) { + boost::split(whitelist, gridNameStr, boost::is_any_of(",")); + } + + // Process the input file. + try { + openvdb::io::File file(inFilename); + file.open(); + + const openvdb::MetaMap::ConstPtr fileMetadata = file.getMetadata(); + + openvdb::GridPtrVec outGrids; + + // For each input grid... + for (openvdb::io::File::NameIterator nameIter = file.beginName(); + nameIter != file.endName(); ++nameIter) + { + const std::string& name = nameIter.gridName(); + // If there is a white list, check if the grid is on the list. + const bool skip = (!whitelist.empty() && (whitelist.find(name) == whitelist.end())); + + if (skip && !opts.keep) { + OPENVDB_LOG_INFO("skipped grid \"" << name << "\""); + } else { + // If the grid's name is on the white list or if -keep is in effect, read the grid. + openvdb::GridBase::Ptr baseGrid = file.readGrid(name); + if (!baseGrid) { + OPENVDB_LOG_WARN("failed to read grid \"" << name << "\""); + } else { + if (skip) { + OPENVDB_LOG_INFO("passed through grid \"" << name << "\""); + outGrids.push_back(baseGrid); + } else { + process(baseGrid, outGrids, opts); + } + } + } + } + file.close(); + + openvdb::util::CpuTimer timer; + timer.start(); + + openvdb::io::File outFile(outFilename); + if (fileMetadata) { + outFile.write(outGrids, *fileMetadata); + } else { + outFile.write(outGrids); + } + + const double msec = timer.milliseconds(); // elapsed time + + if (outGrids.empty()) { + OPENVDB_LOG_WARN("wrote empty file " << outFilename << " in " + << std::setprecision(3) << (msec / 1000.0) << " sec"); + } else { + OPENVDB_LOG_INFO("wrote file " << outFilename << " in " + << std::setprecision(3) << (msec / 1000.0) << " sec"); + } + } + catch (const std::exception& e) { + OPENVDB_LOG_FATAL(e.what()); + exitStatus = EXIT_FAILURE; + } + catch (...) { + OPENVDB_LOG_FATAL("Exception caught (unexpected type)"); + std::unexpected(); + } + + return exitStatus; +} diff --git a/openvdb/cmd/openvdb_print.cc b/openvdb/cmd/openvdb_print.cc new file mode 100644 index 00000000..d75e3c49 --- /dev/null +++ b/openvdb/cmd/openvdb_print.cc @@ -0,0 +1,315 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace { + +using StringVec = std::vector; + +const char* INDENT = " "; +const char* gProgName = ""; + +void +usage [[noreturn]] (int exitStatus = EXIT_FAILURE) +{ + std::cerr << +"Usage: " << gProgName << " in.vdb [in.vdb ...] [options]\n" << +"Which: prints information about OpenVDB grids\n" << +"Options:\n" << +" -l, -stats long printout, including grid statistics\n" << +" -m, -metadata print per-file and per-grid metadata\n" << +" -version print version information\n"; + exit(exitStatus); +} + + +std::string +sizeAsString(openvdb::Index64 n, const std::string& units) +{ + std::ostringstream ostr; + ostr << std::setprecision(3); + if (n < 1000) { + ostr << n; + } else if (n < 1000000) { + ostr << (double(n) / 1.0e3) << "K"; + } else if (n < 1000000000) { + ostr << (double(n) / 1.0e6) << "M"; + } else { + ostr << (double(n) / 1.0e9) << "G"; + } + ostr << units; + return ostr.str(); +} + + +std::string +bytesAsString(openvdb::Index64 n) +{ + std::ostringstream ostr; + ostr << std::setprecision(3); + if (n >> 30) { + ostr << (double(n) / double(uint64_t(1) << 30)) << "GB"; + } else if (n >> 20) { + ostr << (double(n) / double(uint64_t(1) << 20)) << "MB"; + } else if (n >> 10) { + ostr << (double(n) / double(uint64_t(1) << 10)) << "KB"; + } else { + ostr << n << "B"; + } + return ostr.str(); +} + + +std::string +coordAsString(const openvdb::Coord ijk, const std::string& sep, + const std::string& start, const std::string& stop) +{ + std::ostringstream ostr; + ostr << start << ijk[0] << sep << ijk[1] << sep << ijk[2] << stop; + return ostr.str(); +} + + +std::string +bkgdValueAsString(const openvdb::GridBase::ConstPtr& grid) +{ + std::ostringstream ostr; + if (grid) { + const openvdb::TreeBase& tree = grid->baseTree(); + ostr << "background: "; + openvdb::Metadata::Ptr background = tree.getBackgroundValue(); + if (background) ostr << background->str(); + } + return ostr.str(); +} + + +/// Print detailed information about the given VDB files. +/// If @a metadata is true, include file-level metadata key, value pairs. +void +printLongListing(const StringVec& filenames) +{ + bool oneFile = (filenames.size() == 1), firstFile = true; + + for (size_t i = 0, N = filenames.size(); i < N; ++i, firstFile = false) { + openvdb::io::File file(filenames[i]); + std::string version; + openvdb::GridPtrVecPtr grids; + openvdb::MetaMap::Ptr meta; + try { + file.open(); + grids = file.getGrids(); + meta = file.getMetadata(); + version = file.version(); + file.close(); + } catch (openvdb::Exception& e) { + OPENVDB_LOG_ERROR(e.what() << " (" << filenames[i] << ")"); + } + if (!grids) continue; + + if (!oneFile) { + if (!firstFile) { + std::cout << "\n" << std::string(40, '-') << "\n\n"; + } + std::cout << filenames[i] << "\n\n"; + } + + // Print file-level metadata. + std::cout << "VDB version: " << version << "\n"; + if (meta) { + std::string str = meta->str(); + if (!str.empty()) std::cout << str << "\n"; + } + std::cout << "\n"; + + // For each grid in the file... + bool firstGrid = true; + for (openvdb::GridPtrVec::const_iterator it = grids->begin(); it != grids->end(); ++it) { + if (openvdb::GridBase::ConstPtr grid = *it) { + if (!firstGrid) std::cout << "\n\n"; + std::cout << "Name: " << grid->getName() << std::endl; + grid->print(std::cout, /*verboseLevel=*/11); + firstGrid = false; + } + } + } +} + + +/// Print condensed information about the given VDB files. +/// If @a metadata is true, include file- and grid-level metadata. +void +printShortListing(const StringVec& filenames, bool metadata) +{ + bool oneFile = (filenames.size() == 1), firstFile = true; + + for (size_t i = 0, N = filenames.size(); i < N; ++i, firstFile = false) { + const std::string + indent(oneFile ? "": INDENT), + indent2(indent + INDENT); + + if (!oneFile) { + if (metadata && !firstFile) std::cout << "\n"; + std::cout << filenames[i] << ":\n"; + } + + openvdb::GridPtrVecPtr grids; + openvdb::MetaMap::Ptr meta; + + openvdb::io::File file(filenames[i]); + try { + file.open(); + grids = file.getGrids(); + meta = file.getMetadata(); + file.close(); + } catch (openvdb::Exception& e) { + OPENVDB_LOG_ERROR(e.what() << " (" << filenames[i] << ")"); + } + if (!grids) continue; + + if (metadata) { + // Print file-level metadata. + std::string str = meta->str(indent); + if (!str.empty()) std::cout << str << "\n"; + } + + // For each grid in the file... + for (openvdb::GridPtrVec::const_iterator it = grids->begin(); it != grids->end(); ++it) { + const openvdb::GridBase::ConstPtr grid = *it; + if (!grid) continue; + + // Print the grid name and its voxel value datatype. + std::cout << indent << std::left << std::setw(11) << grid->getName() + << " " << std::right << std::setw(6) << grid->valueType(); + + // Print the grid's bounding box and dimensions. + openvdb::CoordBBox bbox = grid->evalActiveVoxelBoundingBox(); + std::string + boxStr = coordAsString(bbox.min(), ",", "(", ")") + "->" + + coordAsString(bbox.max(), ",", "(", ")"), + dimStr = coordAsString(bbox.extents(), "x", "", ""); + boxStr += std::string( + std::max(1, int(40 - boxStr.size() - dimStr.size())), ' ') + dimStr; + std::cout << " " << std::left << std::setw(40) << boxStr; + + // Print the number of active voxels. + std::cout << " " << std::right << std::setw(8) + << sizeAsString(grid->activeVoxelCount(), "Vox"); + + // Print the grid's in-core size, in bytes. + std::cout << " " << std::right << std::setw(6) << bytesAsString(grid->memUsage()); + + std::cout << std::endl; + + // Print grid-specific metadata. + if (metadata) { + // Print background value. + std::string str = bkgdValueAsString(grid); + if (!str.empty()) { + std::cout << indent2 << str << "\n"; + } + // Print local and world transforms. + grid->transform().print(std::cout, indent2); + // Print custom metadata. + str = grid->str(indent2); + if (!str.empty()) std::cout << str << "\n"; + std::cout << std::flush; + } + } + } +} + +} // unnamed namespace + + +int +main(int argc, char *argv[]) +{ + OPENVDB_START_THREADSAFE_STATIC_WRITE + gProgName = argv[0]; + if (const char* ptr = ::strrchr(gProgName, '/')) gProgName = ptr + 1; + OPENVDB_FINISH_THREADSAFE_STATIC_WRITE + + int exitStatus = EXIT_SUCCESS; + + if (argc == 1) usage(); + + openvdb::logging::initialize(argc, argv); + + bool stats = false, metadata = false, version = false; + StringVec filenames; + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg[0] == '-') { + if (arg == "-m" || arg == "-metadata") { + metadata = true; + } else if (arg == "-l" || arg == "-stats") { + stats = true; + } else if (arg == "-h" || arg == "-help" || arg == "--help") { + usage(EXIT_SUCCESS); + } else if (arg == "-version" || arg == "--version") { + version = true; + } else { + OPENVDB_LOG_FATAL("\"" << arg << "\" is not a valid option"); + usage(); + } + } else if (!arg.empty()) { + filenames.push_back(arg); + } + } + + if (version) { + std::cout << "OpenVDB library version: " + << openvdb::getLibraryAbiVersionString() << "\n"; + std::cout << "OpenVDB file format version: " + << openvdb::OPENVDB_FILE_VERSION << std::endl; + if (filenames.empty()) return EXIT_SUCCESS; + } + + if (filenames.empty()) { + OPENVDB_LOG_FATAL("expected one or more OpenVDB files"); + usage(); + } + + try { + openvdb::initialize(); + + /// @todo Remove the following at some point: + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + openvdb::Grid::Type>::registerGrid(); + + if (stats) { + printLongListing(filenames); + } else { + printShortListing(filenames, metadata); + } + } + catch (const std::exception& e) { + OPENVDB_LOG_FATAL(e.what()); + exitStatus = EXIT_FAILURE; + } + catch (...) { + OPENVDB_LOG_FATAL("Exception caught (unexpected type)"); + } + + return exitStatus; +} diff --git a/openvdb/cmd/openvdb_render.cc b/openvdb/cmd/openvdb_render.cc new file mode 100644 index 00000000..012fdea4 --- /dev/null +++ b/openvdb/cmd/openvdb_render.cc @@ -0,0 +1,691 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file main.cc +/// +/// @brief Simple ray tracer for OpenVDB volumes +/// +/// @note This is intended mainly as an example of how to ray-trace +/// OpenVDB volumes. It is not a production-quality renderer. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // defines OPENEXR_DLL if required, must come before OpenEXR includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace { + +const char* gProgName = ""; + +const double LIGHT_DEFAULTS[] = { 0.3, 0.3, 0.0, 0.7, 0.7, 0.7 }; + + +struct RenderOpts +{ + std::string shader; + std::string color; + openvdb::Vec3SGrid::Ptr colorgrid; + std::string camera; + float aperture, focal, frame, znear, zfar; + double isovalue; + openvdb::Vec3d rotate; + openvdb::Vec3d translate; + openvdb::Vec3d target; + openvdb::Vec3d up; + bool lookat; + size_t samples; + openvdb::Vec3d absorb; + std::vector light; + openvdb::Vec3d scatter; + double cutoff, gain; + openvdb::Vec2d step; + size_t width, height; + std::string compression; + int threads; + bool verbose; + + RenderOpts(): + shader("diffuse"), + camera("perspective"), + aperture(41.2136f), + focal(50.0f), + frame(1.0f), + znear(1.0e-3f), + zfar(std::numeric_limits::max()), + isovalue(0.0), + rotate(0.0), + translate(0.0), + target(0.0), + up(0.0, 1.0, 0.0), + lookat(false), + samples(1), + absorb(0.1), + light(LIGHT_DEFAULTS, LIGHT_DEFAULTS + 6), + scatter(1.5), + cutoff(0.005), + gain(0.2), + step(1.0, 3.0), + width(1920), + height(1080), + compression("zip"), + threads(0), + verbose(false) + {} + + std::string validate() const + { + if (shader != "diffuse" && shader != "matte" && shader != "normal" && shader != "position"){ + return "expected diffuse, matte, normal or position shader, got \"" + shader + "\""; + } + if (!boost::starts_with(camera, "ortho") && !boost::starts_with(camera, "persp")) { + return "expected perspective or orthographic camera, got \"" + camera + "\""; + } + if (compression != "none" && compression != "rle" && compression != "zip") { + return "expected none, rle or zip compression, got \"" + compression + "\""; + } + if (width < 1 || height < 1) { + std::ostringstream ostr; + ostr << "expected width > 0 and height > 0, got " << width << "x" << height; + return ostr.str(); + } + return ""; + } + + std::ostream& put(std::ostream& os) const + { + os << " -absorb " << absorb[0] << "," << absorb[1] << "," << absorb[2] + << " -aperture " << aperture + << " -camera " << camera; + if (!color.empty()) os << " -color '" << color << "'"; + os << " -compression " << compression + << " -cpus " << threads + << " -cutoff " << cutoff + << " -far " << zfar + << " -focal " << focal + << " -frame " << frame + << " -gain " << gain + << " -isovalue " << isovalue + << " -light " << light[0] << "," << light[1] << "," << light[2] + << "," << light[3] << "," << light[4] << "," << light[5]; + if (lookat) os << " -lookat " << target[0] << "," << target[1] << "," << target[2]; + os << " -near " << znear + << " -res " << width << "x" << height; + if (!lookat) os << " -rotate " << rotate[0] << "," << rotate[1] << "," << rotate[2]; + os << " -shader " << shader + << " -samples " << samples + << " -scatter " << scatter[0] << "," << scatter[1] << "," << scatter[2] + << " -shadowstep " << step[1] + << " -step " << step[0] + << " -translate " << translate[0] << "," << translate[1] << "," << translate[2]; + if (lookat) os << " -up " << up[0] << "," << up[1] << "," << up[2]; + if (verbose) os << " -v"; + return os; + } +}; + +std::ostream& operator<<(std::ostream& os, const RenderOpts& opts) { return opts.put(os); } + + +void +usage [[noreturn]] (int exitStatus = EXIT_FAILURE) +{ + RenderOpts opts; // default options + const double fov = openvdb::tools::PerspectiveCamera::focalLengthToFieldOfView( + opts.focal, opts.aperture); + + std::ostringstream ostr; + ostr << std::setprecision(3) << +"Usage: " << gProgName << " in.vdb out.{exr,ppm} [options]\n" << +"Which: ray-traces OpenVDB volumes\n" << +"Options:\n" << +" -aperture F perspective camera aperture in mm (default: " << opts.aperture << ")\n" << +" -camera S camera type; either \"persp[ective]\" or \"ortho[graphic]\"\n" << +" (default: " << opts.camera << ")\n" << +" -compression S EXR compression scheme; either \"none\" (uncompressed),\n" << +" \"rle\" or \"zip\" (default: " << opts.compression << ")\n" << +" -cpus N number of rendering threads, or 1 to disable threading,\n" << +" or 0 to use all available CPUs (default: " << opts.threads << ")\n" << +" -far F camera far plane depth (default: " << opts.zfar << ")\n" << +" -focal F perspective camera focal length in mm (default: " << opts.focal << ")\n" << +" -fov F perspective camera field of view in degrees\n" << +" (default: " << fov << ")\n" << +" -frame F ortho camera frame width in world units (default: " << + opts.frame << ")\n" << +" -lookat X,Y,Z rotate the camera to point to (X, Y, Z)\n" << +" -name S name of the volume to be rendered (default: render\n" << +" the first floating-point volume found in in.vdb)\n" << +" -near F camera near plane depth (default: " << opts.znear << ")\n" << +" -res WxH image dimensions in pixels (default: " << + opts.width << "x" << opts.height << ")\n" << +" -r X,Y,Z \n" << +" -rotate X,Y,Z camera rotation in degrees\n" << +" (default: look at the center of the volume)\n" << +" -t X,Y,Z \n" << +" -translate X,Y,Z camera translation\n" << +" -up X,Y,Z vector that should point up after rotation with -lookat\n" << +" (default: " << opts.up << ")\n" << +"\n" << +" -v verbose (print timing and diagnostics)\n" << +" -version print version information and exit\n" << +" -h, -help print this usage message and exit\n" << +"\n" << +"Level set options:\n" << +" -color S name of a vec3s volume to be used to set material colors\n" << +" -isovalue F isovalue in world units for level set ray intersection\n" << +" (default: " << opts.isovalue << ")\n" << +" -samples N number of samples (rays) per pixel (default: " << opts.samples << ")\n" << +" -shader S shader name; either \"diffuse\", \"matte\", \"normal\"\n" << +" or \"position\" (default: " << opts.shader << ")\n" << +"\n" << +"Dense volume options:\n" << +" -absorb R,G,B absorption coefficients (default: " << opts.absorb << ")\n" << +" -cutoff F density and transmittance cutoff value (default: " << opts.cutoff << ")\n" << +" -gain F amount of scatter along the shadow ray (default: " << opts.gain << ")\n" << +" -light X,Y,Z[,R,G,B] light source direction and optional color\n" << +" (default: [" << opts.light[0] << ", " << opts.light[1] + << ", " << opts.light[2] << ", " << opts.light[3] << ", " << opts.light[4] + << ", " << opts.light[5] << "])\n" << +" -scatter R,G,B scattering coefficients (default: " << opts.scatter << ")\n" << +" -shadowstep F step size in voxels for integration along the shadow ray\n" << +" (default: " << opts.step[1] << ")\n" << +" -step F step size in voxels for integration along the primary ray\n" << +" (default: " << opts.step[0] << ")\n" << +"\n" << +"Examples:\n" << +" " << gProgName << " crawler.vdb crawler.exr -shader diffuse -res 1920x1080 \\\n" << +" -focal 35 -samples 4 -translate 0,210.5,400 -compression rle -v\n" << +"\n" << +" " << gProgName << " bunny_cloud.vdb bunny_cloud.exr -res 1920x1080 \\\n" << +" -translate 0,0,110 -absorb 0.4,0.2,0.1 -gain 0.2 -v\n" << +"\n" << +"Warning:\n" << +" This is not (and is not intended to be) a production-quality renderer.\n" << +" Use it for fast previewing or simply as a reference implementation\n" << +" for integration into existing ray tracers.\n"; + + std::cerr << ostr.str(); + exit(exitStatus); +} + + +void +saveEXR(const std::string& fname, const openvdb::tools::Film& film, const RenderOpts& opts) +{ + using RGBA = openvdb::tools::Film::RGBA; + + std::string filename = fname; + if (!boost::iends_with(filename, ".exr")) filename += ".exr"; + + if (opts.verbose) { + std::cout << gProgName << ": writing " << filename << "..." << std::endl; + } + + const tbb::tick_count start = tbb::tick_count::now(); + + int threads = (opts.threads == 0 ? 8 : opts.threads); + Imf::setGlobalThreadCount(threads); + + Imf::Header header(int(film.width()), int(film.height())); + if (opts.compression == "none") { + header.compression() = Imf::NO_COMPRESSION; + } else if (opts.compression == "rle") { + header.compression() = Imf::RLE_COMPRESSION; + } else if (opts.compression == "zip") { + header.compression() = Imf::ZIP_COMPRESSION; + } else { + OPENVDB_THROW(openvdb::ValueError, + "expected none, rle or zip compression, got \"" << opts.compression << "\""); + } + header.channels().insert("R", Imf::Channel(Imf::FLOAT)); + header.channels().insert("G", Imf::Channel(Imf::FLOAT)); + header.channels().insert("B", Imf::Channel(Imf::FLOAT)); + header.channels().insert("A", Imf::Channel(Imf::FLOAT)); + + const size_t pixelBytes = sizeof(RGBA), rowBytes = pixelBytes * film.width(); + RGBA& pixel0 = const_cast(film.pixels())[0]; + Imf::FrameBuffer framebuffer; + framebuffer.insert("R", + Imf::Slice(Imf::FLOAT, reinterpret_cast(&pixel0.r), pixelBytes, rowBytes)); + framebuffer.insert("G", + Imf::Slice(Imf::FLOAT, reinterpret_cast(&pixel0.g), pixelBytes, rowBytes)); + framebuffer.insert("B", + Imf::Slice(Imf::FLOAT, reinterpret_cast(&pixel0.b), pixelBytes, rowBytes)); + framebuffer.insert("A", + Imf::Slice(Imf::FLOAT, reinterpret_cast(&pixel0.a), pixelBytes, rowBytes)); + + Imf::OutputFile imgFile(filename.c_str(), header); + imgFile.setFrameBuffer(framebuffer); + imgFile.writePixels(int(film.height())); + + if (opts.verbose) { + std::ostringstream ostr; + ostr << gProgName << ": ...completed in " << std::setprecision(3) + << (tbb::tick_count::now() - start).seconds() << " sec"; + std::cout << ostr.str() << std::endl; + } +} + + +template +void +render(const GridType& grid, const std::string& imgFilename, const RenderOpts& opts) +{ + using namespace openvdb; + + const bool isLevelSet = (grid.getGridClass() == GRID_LEVEL_SET); + + tools::Film film(opts.width, opts.height); + + std::unique_ptr camera; + if (boost::starts_with(opts.camera, "persp")) { + camera.reset(new tools::PerspectiveCamera(film, opts.rotate, opts.translate, + opts.focal, opts.aperture, opts.znear, opts.zfar)); + } else if (boost::starts_with(opts.camera, "ortho")) { + camera.reset(new tools::OrthographicCamera(film, opts.rotate, opts.translate, + opts.frame, opts.znear, opts.zfar)); + } else { + OPENVDB_THROW(ValueError, + "expected perspective or orthographic camera, got \"" << opts.camera << "\""); + } + if (opts.lookat) camera->lookAt(opts.target, opts.up); + + // Define the shader for level set rendering. The default shader is a diffuse shader. + std::unique_ptr shader; + if (opts.shader == "matte") { + if (opts.colorgrid) { + shader.reset(new tools::MatteShader(*opts.colorgrid)); + } else { + shader.reset(new tools::MatteShader<>()); + } + } else if (opts.shader == "normal") { + if (opts.colorgrid) { + shader.reset(new tools::NormalShader(*opts.colorgrid)); + } else { + shader.reset(new tools::NormalShader<>()); + } + } else if (opts.shader == "position") { + const CoordBBox bbox = grid.evalActiveVoxelBoundingBox(); + const math::BBox bboxIndex(bbox.min().asVec3d(), bbox.max().asVec3d()); + const math::BBox bboxWorld = bboxIndex.applyMap(*(grid.transform().baseMap())); + if (opts.colorgrid) { + shader.reset(new tools::PositionShader(bboxWorld, *opts.colorgrid)); + } else { + shader.reset(new tools::PositionShader<>(bboxWorld)); + } + } else /* if (opts.shader == "diffuse") */ { // default + if (opts.colorgrid) { + shader.reset(new tools::DiffuseShader(*opts.colorgrid)); + } else { + shader.reset(new tools::DiffuseShader<>()); + } + } + + if (opts.verbose) { + std::cout << gProgName << ": ray-tracing"; + const std::string gridName = grid.getName(); + if (!gridName.empty()) std::cout << " " << gridName; + std::cout << "..." << std::endl; + } + const tbb::tick_count start = tbb::tick_count::now(); + + if (isLevelSet) { + tools::LevelSetRayIntersector intersector( + grid, static_cast(opts.isovalue)); + tools::rayTrace(grid, intersector, *shader, *camera, opts.samples, + /*seed=*/0, (opts.threads != 1)); + } else { + using IntersectorType = tools::VolumeRayIntersector; + IntersectorType intersector(grid); + + tools::VolumeRender renderer(intersector, *camera); + renderer.setLightDir(opts.light[0], opts.light[1], opts.light[2]); + renderer.setLightColor(opts.light[3], opts.light[4], opts.light[5]); + renderer.setPrimaryStep(opts.step[0]); + renderer.setShadowStep(opts.step[1]); + renderer.setScattering(opts.scatter[0], opts.scatter[1], opts.scatter[2]); + renderer.setAbsorption(opts.absorb[0], opts.absorb[1], opts.absorb[2]); + renderer.setLightGain(opts.gain); + renderer.setCutOff(opts.cutoff); + + renderer.render(opts.threads != 1); + } + + if (opts.verbose) { + std::ostringstream ostr; + ostr << gProgName << ": ...completed in " << std::setprecision(3) + << (tbb::tick_count::now() - start).seconds() << " sec"; + std::cout << ostr.str() << std::endl; + } + + if (boost::iends_with(imgFilename, ".ppm")) { + // Save as PPM (fast, but large file size). + std::string filename = imgFilename; + filename.erase(filename.size() - 4); // strip .ppm extension + film.savePPM(filename); + } else if (boost::iends_with(imgFilename, ".exr")) { + // Save as EXR (slow, but small file size). + saveEXR(imgFilename, film, opts); + } else { + OPENVDB_THROW(ValueError, "unsupported image file format (" + imgFilename + ")"); + } +} + + +void +strToSize(const std::string& s, size_t& x, size_t& y) +{ + std::vector elems; + boost::split(elems, s, boost::algorithm::is_any_of(",x")); + const size_t numElems = elems.size(); + if (numElems > 0) x = size_t(std::max(0, atoi(elems[0].c_str()))); + if (numElems > 1) y = size_t(std::max(0, atoi(elems[1].c_str()))); +} + + +std::vector +strToVec(const std::string& s) +{ + std::vector result; + std::vector elems; + boost::split(elems, s, boost::algorithm::is_any_of(",")); + for (size_t i = 0, N = elems.size(); i < N; ++i) { + result.push_back(atof(elems[i].c_str())); + } + return result; +} + + +openvdb::Vec3d +strToVec3d(const std::string& s) +{ + openvdb::Vec3d result(0.0, 0.0, 0.0); + std::vector elems = strToVec(s); + if (!elems.empty()) { + result = openvdb::Vec3d(elems[0]); + for (int i = 1, N = std::min(3, int(elems.size())); i < N; ++i) { + result[i] = elems[i]; + } + } + return result; +} + + +struct OptParse +{ + int argc; + char** argv; + + OptParse(int argc_, char* argv_[]): argc(argc_), argv(argv_) {} + + bool check(int idx, const std::string& name, int numArgs = 1) const + { + if (argv[idx] == name) { + if (idx + numArgs >= argc) { + OPENVDB_LOG_FATAL("option " << name << " requires " + << numArgs << " argument" << (numArgs == 1 ? "" : "s")); + usage(); + } + return true; + } + return false; + } +}; + +} // unnamed namespace + + +int +main(int argc, char *argv[]) +{ + OPENVDB_START_THREADSAFE_STATIC_WRITE + gProgName = argv[0]; + if (const char* ptr = ::strrchr(gProgName, '/')) gProgName = ptr + 1; + OPENVDB_FINISH_THREADSAFE_STATIC_WRITE + + int retcode = EXIT_SUCCESS; + + if (argc == 1) usage(); + + openvdb::logging::initialize(argc, argv); + + std::string vdbFilename, imgFilename, gridName; + RenderOpts opts; + + bool hasFocal = false, hasFov = false, hasRotate = false, hasLookAt = false; + float fov = 0.0; + + OptParse parser(argc, argv); + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg[0] == '-') { + if (parser.check(i, "-absorb")) { + ++i; + opts.absorb = strToVec3d(argv[i]); + } else if (parser.check(i, "-aperture")) { + ++i; + opts.aperture = float(atof(argv[i])); + } else if (parser.check(i, "-camera")) { + ++i; + opts.camera = argv[i]; + } else if (parser.check(i, "-color")) { + ++i; + opts.color = argv[i]; + } else if (parser.check(i, "-compression")) { + ++i; + opts.compression = argv[i]; + } else if (parser.check(i, "-cpus")) { + ++i; + opts.threads = std::max(0, atoi(argv[i])); + } else if (parser.check(i, "-cutoff")) { + ++i; + opts.cutoff = atof(argv[i]); + } else if (parser.check(i, "-isovalue")) { + ++i; + opts.isovalue = atof(argv[i]); + } else if (parser.check(i, "-far")) { + ++i; + opts.zfar = float(atof(argv[i])); + } else if (parser.check(i, "-focal")) { + ++i; + opts.focal = float(atof(argv[i])); + hasFocal = true; + } else if (parser.check(i, "-fov")) { + ++i; + fov = float(atof(argv[i])); + hasFov = true; + } else if (parser.check(i, "-frame")) { + ++i; + opts.frame = float(atof(argv[i])); + } else if (parser.check(i, "-gain")) { + ++i; + opts.gain = atof(argv[i]); + } else if (parser.check(i, "-light")) { + ++i; + opts.light = strToVec(argv[i]); + } else if (parser.check(i, "-lookat")) { + ++i; + opts.lookat = true; + opts.target = strToVec3d(argv[i]); + hasLookAt = true; + } else if (parser.check(i, "-name")) { + ++i; + gridName = argv[i]; + } else if (parser.check(i, "-near")) { + ++i; + opts.znear = float(atof(argv[i])); + } else if (parser.check(i, "-r") || parser.check(i, "-rotate")) { + ++i; + opts.rotate = strToVec3d(argv[i]); + hasRotate = true; + } else if (parser.check(i, "-res")) { + ++i; + strToSize(argv[i], opts.width, opts.height); + } else if (parser.check(i, "-scatter")) { + ++i; + opts.scatter = strToVec3d(argv[i]); + } else if (parser.check(i, "-shader")) { + ++i; + opts.shader = argv[i]; + } else if (parser.check(i, "-shadowstep")) { + ++i; + opts.step[1] = atof(argv[i]); + } else if (parser.check(i, "-samples")) { + ++i; + opts.samples = size_t(std::max(0, atoi(argv[i]))); + } else if (parser.check(i, "-step")) { + ++i; + opts.step[0] = atof(argv[i]); + } else if (parser.check(i, "-t") || parser.check(i, "-translate")) { + ++i; + opts.translate = strToVec3d(argv[i]); + } else if (parser.check(i, "-up")) { + ++i; + opts.up = strToVec3d(argv[i]); + } else if (arg == "-v") { + opts.verbose = true; + } else if (arg == "-version" || arg == "--version") { + std::cout << "OpenVDB library version: " + << openvdb::getLibraryAbiVersionString() << "\n"; + std::cout << "OpenVDB file format version: " + << openvdb::OPENVDB_FILE_VERSION << std::endl; + return EXIT_SUCCESS; + } else if (arg == "-h" || arg == "-help" || arg == "--help") { + usage(EXIT_SUCCESS); + } else { + OPENVDB_LOG_FATAL("\"" << arg << "\" is not a valid option"); + usage(); + } + } else if (vdbFilename.empty()) { + vdbFilename = arg; + } else if (imgFilename.empty()) { + imgFilename = arg; + } else { + usage(); + } + } + if (vdbFilename.empty() || imgFilename.empty()) { + usage(); + } + if (hasFov) { + if (hasFocal) { + OPENVDB_LOG_FATAL("specify -focal or -fov, but not both"); + usage(); + } + opts.focal = float( + openvdb::tools::PerspectiveCamera::fieldOfViewToFocalLength(fov, opts.aperture)); + } + if (hasLookAt && hasRotate) { + OPENVDB_LOG_FATAL("specify -lookat or -r[otate], but not both"); + usage(); + } + { + const std::string err = opts.validate(); + if (!err.empty()) { + OPENVDB_LOG_FATAL(err); + usage(); + } + } + + try { + tbb::task_scheduler_init schedulerInit( + (opts.threads == 0) ? tbb::task_scheduler_init::automatic : opts.threads); + + openvdb::initialize(); + + const tbb::tick_count start = tbb::tick_count::now(); + if (opts.verbose) { + std::cout << gProgName << ": reading "; + if (!gridName.empty()) std::cout << gridName << " from "; + std::cout << vdbFilename << "..." << std::endl; + } + + openvdb::FloatGrid::Ptr grid; + { + openvdb::io::File file(vdbFilename); + + if (!gridName.empty()) { + file.open(); + grid = openvdb::gridPtrCast(file.readGrid(gridName)); + if (!grid) { + OPENVDB_THROW(openvdb::ValueError, + gridName + " is not a scalar, floating-point volume"); + } + } else { + // If no grid was specified by name, retrieve the first float grid from the file. + file.open(/*delayLoad=*/false); + openvdb::io::File::NameIterator it = file.beginName(); + openvdb::GridPtrVecPtr grids = file.readAllGridMetadata(); + for (size_t i = 0; i < grids->size(); ++i, ++it) { + grid = openvdb::gridPtrCast(grids->at(i)); + if (grid) { + gridName = *it; + file.close(); + file.open(); + grid = openvdb::gridPtrCast(file.readGrid(gridName)); + break; + } + } + if (!grid) { + OPENVDB_THROW(openvdb::ValueError, + "no scalar, floating-point volumes in file " + vdbFilename); + } + } + + if (!opts.color.empty()) { + opts.colorgrid = + openvdb::gridPtrCast(file.readGrid(opts.color)); + if (!opts.colorgrid) { + OPENVDB_THROW(openvdb::ValueError, + opts.color + " is not a vec3s color volume"); + } + } + } + + if (opts.verbose) { + std::ostringstream ostr; + ostr << gProgName << ": ...completed in " << std::setprecision(3) + << (tbb::tick_count::now() - start).seconds() << " sec"; + std::cout << ostr.str() << std::endl; + } + + if (grid) { + if (!hasLookAt && !hasRotate) { + // If the user specified neither the camera rotation nor a target + // to look at, orient the camera to point to the center of the grid. + opts.target = grid->evalActiveVoxelBoundingBox().getCenter(); + opts.target = grid->constTransform().indexToWorld(opts.target); + opts.lookat = true; + } + + if (opts.verbose) std::cout << opts << std::endl; + + render(*grid, imgFilename, opts); + } + } catch (std::exception& e) { + OPENVDB_LOG_FATAL(e.what()); + retcode = EXIT_FAILURE; + } catch (...) { + OPENVDB_LOG_FATAL("Exception caught (unexpected type)"); + } + + return retcode; +} diff --git a/openvdb/cmd/openvdb_view.cc b/openvdb/cmd/openvdb_view.cc new file mode 100644 index 00000000..7e837559 --- /dev/null +++ b/openvdb/cmd/openvdb_view.cc @@ -0,0 +1,169 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include // for boost::is_any_of() +#include // for boost::starts_with() +#include +#include +#include +#include +#include +#include +#include + + +inline void +usage [[noreturn]] (const char* progName, int status) +{ + (status == EXIT_SUCCESS ? std::cout : std::cerr) << +"Usage: " << progName << " file.vdb [file.vdb ...] [options]\n" << +"Which: displays OpenVDB grids\n" << +"Options:\n" << +" -i print grid information\n" << +" -h, -help print this usage message and exit\n" << +" -version print version information\n" << +"\n" << +"Controls:\n" << +" Esc exit\n" << +" -> (Right) show next grid\n" << +" <- (Left) show previous grid\n" << +" 1 toggle tree topology view on/off\n" << +" 2 toggle surface view on/off\n" << +" 3 toggle data view on/off\n" << +" G (\"geometry\") look at center of geometry\n" << +" H (\"home\") look at origin\n" << +" I toggle on-screen grid info on/off\n" << +" left mouse tumble\n" << +" right mouse pan\n" << +" mouse wheel zoom\n" << +"\n" << +" X + wheel move right cut plane\n" << +" Shift + X + wheel move left cut plane\n" << +" Y + wheel move top cut plane\n" << +" Shift + Y + wheel move bottom cut plane\n" << +" Z + wheel move front cut plane\n" << +" Shift + Z + wheel move back cut plane\n" << +" Ctrl + X + wheel move both X cut planes\n" << +" Ctrl + Y + wheel move both Y cut planes\n" << +" Ctrl + Z + wheel move both Z cut planes\n"; + exit(status); +} + + +//////////////////////////////////////// + + +int +main(int argc, char *argv[]) +{ + const char* progName = argv[0]; + if (const char* ptr = ::strrchr(progName, '/')) progName = ptr + 1; + + int status = EXIT_SUCCESS; + + try { + openvdb::initialize(); + openvdb::logging::initialize(argc, argv); + + bool printInfo = false, printGLInfo = false, printVersionInfo = false; + + // Parse the command line. + std::vector filenames; + for (int n = 1; n < argc; ++n) { + std::string str(argv[n]); + if (str[0] != '-') { + filenames.push_back(str); + } else if (str == "-i") { + printInfo = true; + } else if (str == "-d") { // deprecated + printGLInfo = true; + } else if (str == "-h" || str == "-help" || str == "--help") { + usage(progName, EXIT_SUCCESS); + } else if (str == "-version" || str == "--version") { + printVersionInfo = true; + printGLInfo = true; + } else { + usage(progName, EXIT_FAILURE); + } + } + + const size_t numFiles = filenames.size(); + + if (printVersionInfo) { + std::cout << "OpenVDB library version: " + << openvdb::getLibraryAbiVersionString() << "\n"; + std::cout << "OpenVDB file format version: " + << openvdb::OPENVDB_FILE_VERSION << std::endl; + // If there are no files to view, don't print the OpenGL version, + // since that would require opening a viewer window. + if (numFiles == 0) return EXIT_SUCCESS; + } + if (numFiles == 0 && !printGLInfo) usage(progName, EXIT_FAILURE); + + openvdb_viewer::Viewer viewer = openvdb_viewer::init(progName, /*bg=*/false); + + if (printGLInfo) { + // Now that the viewer window is open, we can get the OpenGL version, if requested. + if (!printVersionInfo) { + // Preserve the behavior of the deprecated -d option. + std::cout << viewer.getVersionString() << std::endl; + } else { + // Print OpenGL and GLFW versions. + std::ostringstream ostr; + ostr << viewer.getVersionString(); // returns comma-separated list of versions + const std::string s = ostr.str(); + std::vector elems; + boost::split(elems, s, boost::algorithm::is_any_of(",")); + for (size_t i = 0; i < elems.size(); ++i) { + boost::trim(elems[i]); + // Don't print the OpenVDB library version again. + if (!boost::starts_with(elems[i], "OpenVDB:")) { + std::cout << elems[i] << std::endl; + } + } + } + if (numFiles == 0) return EXIT_SUCCESS; + } + + openvdb::GridCPtrVec allGrids; + + // Load VDB files. + std::string indent(numFiles == 1 ? "" : " "); + for (size_t n = 0; n < numFiles; ++n) { + openvdb::io::File file(filenames[n]); + file.open(); + + openvdb::GridPtrVecPtr grids = file.getGrids(); + if (grids->empty()) { + OPENVDB_LOG_WARN(filenames[n] << " is empty"); + continue; + } + allGrids.insert(allGrids.end(), grids->begin(), grids->end()); + + if (printInfo) { + if (numFiles > 1) std::cout << filenames[n] << ":\n"; + for (size_t i = 0; i < grids->size(); ++i) { + const std::string name = (*grids)[i]->getName(); + openvdb::Coord dim = (*grids)[i]->evalActiveVoxelDim(); + std::cout << indent << (name.empty() ? "" : name) + << " (" << dim[0] << " x " << dim[1] << " x " << dim[2] + << " voxels)" << std::endl; + } + } + } + + viewer.open(); + viewer.view(allGrids); + + openvdb_viewer::exit(); + + } catch (const char* s) { + OPENVDB_LOG_ERROR(s); + status = EXIT_FAILURE; + } catch (std::exception& e) { + OPENVDB_LOG_ERROR(e.what()); + status = EXIT_FAILURE; + } + return status; +} diff --git a/openvdb/io/Archive.cc b/openvdb/io/Archive.cc new file mode 100644 index 00000000..652cc563 --- /dev/null +++ b/openvdb/io/Archive.cc @@ -0,0 +1,1459 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Archive.h" + +#include "GridDescriptor.h" +#include "DelayedLoadMetadata.h" +#include "io.h" + +#include +#include +#include +#include +#include + +// Boost.Interprocess uses a header-only portion of Boost.DateTime +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-macros" +#endif +#define BOOST_DATE_TIME_NO_LIB +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#include +#include +#include +#include +#include +#include + +#include + +#ifdef _MSC_VER +#include // open_existing_file(), close_file() +extern "C" __declspec(dllimport) bool __stdcall GetFileTime( + void* fh, void* ctime, void* atime, void* mtime); +// boost::interprocess::detail was renamed to boost::interprocess::ipcdetail in Boost 1.48. +// Ensure that both namespaces exist. +namespace boost { namespace interprocess { namespace detail {} namespace ipcdetail {} } } +#else +#include // for struct stat +#include // for stat() +#include // for unlink() +#endif +#include // for std::find_if() +#include // for errno +#include // for getenv() +#include // for std::memcpy() +#include // for std::time() +#include +#include +#include +#include +#include +#include // for std::error_code() + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +#ifdef OPENVDB_USE_BLOSC +const uint32_t Archive::DEFAULT_COMPRESSION_FLAGS = (COMPRESS_BLOSC | COMPRESS_ACTIVE_MASK); +#else +const uint32_t Archive::DEFAULT_COMPRESSION_FLAGS = (COMPRESS_ZIP | COMPRESS_ACTIVE_MASK); +#endif + + +namespace { + +// Indices into a stream's internal extensible array of values used by readers and writers +struct StreamState +{ + static const long MAGIC_NUMBER; + + StreamState(); + ~StreamState(); + + // Important: The size and order of these member variables must *only* change when + // OpenVDB ABI changes to avoid potential segfaults when performing I/O + // across two different versions of the library. Adding new member + // variables to the end of the struct is allowed provided that they + // are only accessed from within an appropriate ABI guard. + int magicNumber; + int fileVersion; + int libraryMajorVersion; + int libraryMinorVersion; + int dataCompression; + int writeGridStatsMetadata; + int gridBackground; + int gridClass; + int halfFloat; + int mappedFile; + int metadata; +} +sStreamState; + +const long StreamState::MAGIC_NUMBER = + long((uint64_t(OPENVDB_MAGIC) << 32) | (uint64_t(OPENVDB_MAGIC))); + + +//////////////////////////////////////// + + +StreamState::StreamState(): magicNumber(std::ios_base::xalloc()) +{ + // Having reserved an entry (the one at index magicNumber) in the extensible array + // associated with every stream, store a magic number at that location in the + // array belonging to the cout stream. + std::cout.iword(magicNumber) = MAGIC_NUMBER; + std::cout.pword(magicNumber) = this; + + // Search for a lower-numbered entry in cout's array that already contains the magic number. + /// @todo This assumes that the indices returned by xalloc() increase monotonically. + int existingArray = -1; + for (int i = 0; i < magicNumber; ++i) { + if (std::cout.iword(i) == MAGIC_NUMBER) { + existingArray = i; + break; + } + } + + if (existingArray >= 0 && std::cout.pword(existingArray) != nullptr) { + // If a lower-numbered entry was found to contain the magic number, + // a coexisting version of this library must have registered it. + // In that case, the corresponding pointer should point to an existing + // StreamState struct. Copy the other array indices from that StreamState + // into this one, so as to share state with the other library. + const StreamState& other = + *static_cast(std::cout.pword(existingArray)); + fileVersion = other.fileVersion; + libraryMajorVersion = other.libraryMajorVersion; + libraryMinorVersion = other.libraryMinorVersion; + dataCompression = other.dataCompression; + writeGridStatsMetadata = other.writeGridStatsMetadata; + gridBackground = other.gridBackground; + gridClass = other.gridClass; + if (other.mappedFile != 0) { // memory-mapped file support was added in OpenVDB 3.0.0 + mappedFile = other.mappedFile; + metadata = other.metadata; + halfFloat = other.halfFloat; + } else { + mappedFile = std::ios_base::xalloc(); + metadata = std::ios_base::xalloc(); + halfFloat = std::ios_base::xalloc(); + } + } else { + // Reserve storage for per-stream file format and library version numbers + // and other values of use to readers and writers. Each of the following + // values is an index into the extensible arrays associated with all streams. + // The indices are common to all streams, but the values stored at those indices + // are unique to each stream. + fileVersion = std::ios_base::xalloc(); + libraryMajorVersion = std::ios_base::xalloc(); + libraryMinorVersion = std::ios_base::xalloc(); + dataCompression = std::ios_base::xalloc(); + writeGridStatsMetadata = std::ios_base::xalloc(); + gridBackground = std::ios_base::xalloc(); + gridClass = std::ios_base::xalloc(); + mappedFile = std::ios_base::xalloc(); + metadata = std::ios_base::xalloc(); + halfFloat = std::ios_base::xalloc(); + } +} + + +StreamState::~StreamState() +{ + // Ensure that this StreamState struct can no longer be accessed. + std::cout.iword(magicNumber) = 0; + std::cout.pword(magicNumber) = nullptr; +} + +} // unnamed namespace + + +//////////////////////////////////////// + + +struct StreamMetadata::Impl +{ + // Important: The size and order of these member variables must *only* change when + // OpenVDB ABI changes to avoid potential segfaults when performing I/O + // across two different versions of the library. Adding new member + // variables to the end of the struct is allowed provided that they + // are only accessed from within an appropriate ABI guard. + + uint32_t mFileVersion = OPENVDB_FILE_VERSION; + VersionId mLibraryVersion = { OPENVDB_LIBRARY_MAJOR_VERSION, OPENVDB_LIBRARY_MINOR_VERSION }; + uint32_t mCompression = COMPRESS_NONE; + uint32_t mGridClass = GRID_UNKNOWN; + const void* mBackgroundPtr = nullptr; ///< @todo use Metadata::Ptr? + bool mHalfFloat = false; + bool mWriteGridStats = false; + bool mSeekable = false; + bool mCountingPasses = false; + uint32_t mPass = 0; + MetaMap mGridMetadata; + AuxDataMap mAuxData; + bool mDelayedLoadMeta = DelayedLoadMetadata::isRegisteredType(); + uint64_t mLeaf = 0; + uint32_t mTest = 0; // for testing only +}; // struct StreamMetadata + + +StreamMetadata::StreamMetadata(): mImpl(new Impl) +{ +} + + +StreamMetadata::StreamMetadata(const StreamMetadata& other): mImpl(new Impl(*other.mImpl)) +{ +} + + +StreamMetadata::StreamMetadata(std::ios_base& strm): mImpl(new Impl) +{ + mImpl->mFileVersion = getFormatVersion(strm); + mImpl->mLibraryVersion = getLibraryVersion(strm); + mImpl->mCompression = getDataCompression(strm); + mImpl->mGridClass = getGridClass(strm); + mImpl->mHalfFloat = getHalfFloat(strm); + mImpl->mWriteGridStats = getWriteGridStatsMetadata(strm); +} + + +StreamMetadata::~StreamMetadata() +{ +} + + +StreamMetadata& +StreamMetadata::operator=(const StreamMetadata& other) +{ + if (&other != this) { + mImpl.reset(new Impl(*other.mImpl)); + } + return *this; +} + + +void +StreamMetadata::transferTo(std::ios_base& strm) const +{ + io::setVersion(strm, mImpl->mLibraryVersion, mImpl->mFileVersion); + io::setDataCompression(strm, mImpl->mCompression); + io::setGridBackgroundValuePtr(strm, mImpl->mBackgroundPtr); + io::setGridClass(strm, mImpl->mGridClass); + io::setHalfFloat(strm, mImpl->mHalfFloat); + io::setWriteGridStatsMetadata(strm, mImpl->mWriteGridStats); +} + + +uint32_t StreamMetadata::fileVersion() const { return mImpl->mFileVersion; } +VersionId StreamMetadata::libraryVersion() const { return mImpl->mLibraryVersion; } +uint32_t StreamMetadata::compression() const { return mImpl->mCompression; } +uint32_t StreamMetadata::gridClass() const { return mImpl->mGridClass; } +const void* StreamMetadata::backgroundPtr() const { return mImpl->mBackgroundPtr; } +bool StreamMetadata::halfFloat() const { return mImpl->mHalfFloat; } +bool StreamMetadata::writeGridStats() const { return mImpl->mWriteGridStats; } +bool StreamMetadata::seekable() const { return mImpl->mSeekable; } +bool StreamMetadata::delayedLoadMeta() const { return mImpl->mDelayedLoadMeta; } +bool StreamMetadata::countingPasses() const { return mImpl->mCountingPasses; } +uint32_t StreamMetadata::pass() const { return mImpl->mPass; } +uint64_t StreamMetadata::leaf() const { return mImpl->mLeaf; } +MetaMap& StreamMetadata::gridMetadata() { return mImpl->mGridMetadata; } +const MetaMap& StreamMetadata::gridMetadata() const { return mImpl->mGridMetadata; } +uint32_t StreamMetadata::__test() const { return mImpl->mTest; } + +StreamMetadata::AuxDataMap& StreamMetadata::auxData() { return mImpl->mAuxData; } +const StreamMetadata::AuxDataMap& StreamMetadata::auxData() const { return mImpl->mAuxData; } + +void StreamMetadata::setFileVersion(uint32_t v) { mImpl->mFileVersion = v; } +void StreamMetadata::setLibraryVersion(VersionId v) { mImpl->mLibraryVersion = v; } +void StreamMetadata::setCompression(uint32_t c) { mImpl->mCompression = c; } +void StreamMetadata::setGridClass(uint32_t c) { mImpl->mGridClass = c; } +void StreamMetadata::setBackgroundPtr(const void* ptr) { mImpl->mBackgroundPtr = ptr; } +void StreamMetadata::setHalfFloat(bool b) { mImpl->mHalfFloat = b; } +void StreamMetadata::setWriteGridStats(bool b) { mImpl->mWriteGridStats = b; } +void StreamMetadata::setSeekable(bool b) { mImpl->mSeekable = b; } +void StreamMetadata::setCountingPasses(bool b) { mImpl->mCountingPasses = b; } +void StreamMetadata::setPass(uint32_t i) { mImpl->mPass = i; } +void StreamMetadata::setLeaf(uint64_t i) { mImpl->mLeaf = i; } +void StreamMetadata::__setTest(uint32_t t) { mImpl->mTest = t; } + +std::string +StreamMetadata::str() const +{ + std::ostringstream ostr; + ostr << std::boolalpha; + ostr << "version: " << libraryVersion().first << "." << libraryVersion().second + << "/" << fileVersion() << "\n"; + ostr << "class: " << GridBase::gridClassToString(static_cast(gridClass())) << "\n"; + ostr << "compression: " << compressionToString(compression()) << "\n"; + ostr << "half_float: " << halfFloat() << "\n"; + ostr << "seekable: " << seekable() << "\n"; + ostr << "delayed_load_meta: " << delayedLoadMeta() << "\n"; + ostr << "pass: " << pass() << "\n"; + ostr << "counting_passes: " << countingPasses() << "\n"; + ostr << "write_grid_stats_metadata: " << writeGridStats() << "\n"; + if (!auxData().empty()) ostr << auxData(); + if (gridMetadata().metaCount() != 0) { + ostr << "grid_metadata:\n" << gridMetadata().str(/*indent=*/" "); + } + return ostr.str(); +} + + +std::ostream& +operator<<(std::ostream& os, const StreamMetadata& meta) +{ + os << meta.str(); + return os; +} + + +namespace { + +template +inline bool +writeAsType(std::ostream& os, const boost::any& val) +{ + if (val.type() == typeid(T)) { + os << boost::any_cast(val); + return true; + } + return false; +} + +struct PopulateDelayedLoadMetadataOp +{ + DelayedLoadMetadata& metadata; + uint32_t compression; + + PopulateDelayedLoadMetadataOp(DelayedLoadMetadata& _metadata, uint32_t _compression) + : metadata(_metadata) + , compression(_compression) { } + + template + void operator()(const GridT& grid) const + { + using TreeT = typename GridT::TreeType; + using ValueT = typename TreeT::ValueType; + using LeafT = typename TreeT::LeafNodeType; + using MaskT = typename LeafT::NodeMaskType; + + const TreeT& tree = grid.constTree(); + const Index32 leafCount = tree.leafCount(); + + // early exit if not leaf nodes + if (leafCount == Index32(0)) return; + + metadata.resizeMask(leafCount); + + if (compression & (COMPRESS_BLOSC | COMPRESS_ZIP)) { + metadata.resizeCompressedSize(leafCount); + } + + const auto background = tree.background(); + const bool saveFloatAsHalf = grid.saveFloatAsHalf(); + + tree::LeafManager leafManager(tree); + + leafManager.foreach( + [&](const LeafT& leaf, size_t idx) { + // set mask value + MaskCompress maskCompressData( + leaf.valueMask(), /*childMask=*/MaskT(), leaf.buffer().data(), background); + metadata.setMask(idx, maskCompressData.metadata); + + if (compression & (COMPRESS_BLOSC | COMPRESS_ZIP)) { + // set compressed size value + size_t sizeBytes(8); + size_t compressedSize = io::writeCompressedValuesSize( + leaf.buffer().data(), LeafT::SIZE, + leaf.valueMask(), maskCompressData.metadata, saveFloatAsHalf, compression); + metadata.setCompressedSize(idx, compressedSize+sizeBytes); + } + } + ); + } +}; + +bool populateDelayedLoadMetadata(DelayedLoadMetadata& metadata, + const GridBase& gridBase, uint32_t compression) +{ + PopulateDelayedLoadMetadataOp op(metadata, compression); + + using AllowedTypes = TypeList< + Int32Grid, Int64Grid, + FloatGrid, DoubleGrid, + Vec3IGrid, Vec3SGrid, Vec3DGrid>; + + return gridBase.apply(op); +} + +} // unnamed namespace + +std::ostream& +operator<<(std::ostream& os, const StreamMetadata::AuxDataMap& auxData) +{ + for (StreamMetadata::AuxDataMap::const_iterator it = auxData.begin(), end = auxData.end(); + it != end; ++it) + { + os << it->first << ": "; + // Note: boost::any doesn't support serialization. + const boost::any& val = it->second; + if (!writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val) + && !writeAsType(os, val)) + { + os << val.type().name() << "(...)"; + } + os << "\n"; + } + return os; +} + + +//////////////////////////////////////// + + +// Memory-mapping a VDB file permits threaded input (and output, potentially, +// though that might not be practical for compressed files or files containing +// multiple grids). In particular, a memory-mapped file can be loaded lazily, +// meaning that the voxel buffers of the leaf nodes of a grid's tree are not allocated +// until they are actually accessed. When access to its buffer is requested, +// a leaf node allocates memory for the buffer and then streams in (and decompresses) +// its contents from the memory map, starting from a stream offset that was recorded +// at the time the node was constructed. The memory map must persist as long as +// there are unloaded leaf nodes; this is ensured by storing a shared pointer +// to the map in each unloaded node. + +class MappedFile::Impl +{ +public: + Impl(const std::string& filename, bool autoDelete) + : mMap(filename.c_str(), boost::interprocess::read_only) + , mRegion(mMap, boost::interprocess::read_only) + , mAutoDelete(autoDelete) + { + mLastWriteTime = this->getLastWriteTime(); + + if (mAutoDelete) { +#ifndef _MSC_VER + // On Unix systems, unlink the file so that it gets deleted once it is closed. + ::unlink(mMap.get_name()); +#endif + } + } + + ~Impl() + { + std::string filename; + if (const char* s = mMap.get_name()) filename = s; + OPENVDB_LOG_DEBUG_RUNTIME("closing memory-mapped file " << filename); + if (mNotifier) mNotifier(filename); + if (mAutoDelete) { + if (!boost::interprocess::file_mapping::remove(filename.c_str())) { + if (errno != ENOENT) { + // Warn if the file exists but couldn't be removed. + std::string mesg = getErrorString(); + if (!mesg.empty()) mesg = " (" + mesg + ")"; + OPENVDB_LOG_WARN("failed to remove temporary file " << filename << mesg); + } + } + } + } + + Index64 getLastWriteTime() const + { + Index64 result = 0; + const char* filename = mMap.get_name(); + +#ifdef _MSC_VER + // boost::interprocess::detail was renamed to boost::interprocess::ipcdetail in Boost 1.48. + using namespace boost::interprocess::detail; + using namespace boost::interprocess::ipcdetail; + + if (void* fh = open_existing_file(filename, boost::interprocess::read_only)) { + struct { unsigned long lo, hi; } mtime; // Windows FILETIME struct + if (GetFileTime(fh, nullptr, nullptr, &mtime)) { + result = (Index64(mtime.hi) << 32) | mtime.lo; + } + close_file(fh); + } +#else + struct stat info; + if (0 == ::stat(filename, &info)) { + result = Index64(info.st_mtime); + } +#endif + return result; + } + + boost::interprocess::file_mapping mMap; + boost::interprocess::mapped_region mRegion; + bool mAutoDelete; + Notifier mNotifier; + mutable tbb::atomic mLastWriteTime; + +private: + Impl(const Impl&); // not copyable + Impl& operator=(const Impl&); // not copyable +}; + + +MappedFile::MappedFile(const std::string& filename, bool autoDelete): + mImpl(new Impl(filename, autoDelete)) +{ +} + + +MappedFile::~MappedFile() +{ +} + + +std::string +MappedFile::filename() const +{ + std::string result; + if (const char* s = mImpl->mMap.get_name()) result = s; + return result; +} + + +SharedPtr +MappedFile::createBuffer() const +{ + if (!mImpl->mAutoDelete && mImpl->mLastWriteTime > 0) { + // Warn if the file has been modified since it was opened + // (but don't bother checking if it is a private, temporary file). + if (mImpl->getLastWriteTime() > mImpl->mLastWriteTime) { + OPENVDB_LOG_WARN("file " << this->filename() << " might have changed on disk" + << " since it was opened"); + mImpl->mLastWriteTime = 0; // suppress further warnings + } + } + + return SharedPtr{ + new boost::iostreams::stream_buffer{ + static_cast(mImpl->mRegion.get_address()), mImpl->mRegion.get_size()}}; +} + + +void +MappedFile::setNotifier(const Notifier& notifier) +{ + mImpl->mNotifier = notifier; +} + + +void +MappedFile::clearNotifier() +{ + mImpl->mNotifier = nullptr; +} + + +//////////////////////////////////////// + + +std::string +getErrorString(int errorNum) +{ + return std::error_code(errorNum, std::generic_category()).message(); +} + + +std::string +getErrorString() +{ + return getErrorString(errno); +} + + +//////////////////////////////////////// + + +Archive::Archive() + : mFileVersion(OPENVDB_FILE_VERSION) + , mLibraryVersion(OPENVDB_LIBRARY_MAJOR_VERSION, OPENVDB_LIBRARY_MINOR_VERSION) + , mUuid(boost::uuids::nil_uuid()) + , mInputHasGridOffsets(false) + , mEnableInstancing(true) + , mCompression(DEFAULT_COMPRESSION_FLAGS) + , mEnableGridStats(true) +{ +} + + +Archive::~Archive() +{ +} + + +Archive::Ptr +Archive::copy() const +{ + return Archive::Ptr(new Archive(*this)); +} + + +//////////////////////////////////////// + + +std::string +Archive::getUniqueTag() const +{ + return boost::uuids::to_string(mUuid); +} + + +bool +Archive::isIdentical(const std::string& uuidStr) const +{ + return uuidStr == getUniqueTag(); +} + + +//////////////////////////////////////// + + +uint32_t +getFormatVersion(std::ios_base& is) +{ + /// @todo get from StreamMetadata + return static_cast(is.iword(sStreamState.fileVersion)); +} + + +void +Archive::setFormatVersion(std::istream& is) +{ + is.iword(sStreamState.fileVersion) = mFileVersion; ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(is)) { + meta->setFileVersion(mFileVersion); + } +} + + +VersionId +getLibraryVersion(std::ios_base& is) +{ + /// @todo get from StreamMetadata + VersionId version; + version.first = static_cast(is.iword(sStreamState.libraryMajorVersion)); + version.second = static_cast(is.iword(sStreamState.libraryMinorVersion)); + return version; +} + + +void +Archive::setLibraryVersion(std::istream& is) +{ + is.iword(sStreamState.libraryMajorVersion) = mLibraryVersion.first; ///< @todo remove + is.iword(sStreamState.libraryMinorVersion) = mLibraryVersion.second; ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(is)) { + meta->setLibraryVersion(mLibraryVersion); + } +} + + +std::string +getVersion(std::ios_base& is) +{ + VersionId version = getLibraryVersion(is); + std::ostringstream ostr; + ostr << version.first << "." << version.second << "/" << getFormatVersion(is); + return ostr.str(); +} + + +void +setCurrentVersion(std::istream& is) +{ + is.iword(sStreamState.fileVersion) = OPENVDB_FILE_VERSION; ///< @todo remove + is.iword(sStreamState.libraryMajorVersion) = OPENVDB_LIBRARY_MAJOR_VERSION; ///< @todo remove + is.iword(sStreamState.libraryMinorVersion) = OPENVDB_LIBRARY_MINOR_VERSION; ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(is)) { + meta->setFileVersion(OPENVDB_FILE_VERSION); + meta->setLibraryVersion(VersionId( + OPENVDB_LIBRARY_MAJOR_VERSION, OPENVDB_LIBRARY_MINOR_VERSION)); + } +} + + +void +setVersion(std::ios_base& strm, const VersionId& libraryVersion, uint32_t fileVersion) +{ + strm.iword(sStreamState.fileVersion) = fileVersion; ///< @todo remove + strm.iword(sStreamState.libraryMajorVersion) = libraryVersion.first; ///< @todo remove + strm.iword(sStreamState.libraryMinorVersion) = libraryVersion.second; ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { + meta->setFileVersion(fileVersion); + meta->setLibraryVersion(libraryVersion); + } +} + + +std::string +Archive::version() const +{ + std::ostringstream ostr; + ostr << mLibraryVersion.first << "." << mLibraryVersion.second << "/" << mFileVersion; + return ostr.str(); +} + + +//////////////////////////////////////// + + +uint32_t +getDataCompression(std::ios_base& strm) +{ + /// @todo get from StreamMetadata + return uint32_t(strm.iword(sStreamState.dataCompression)); +} + + +void +setDataCompression(std::ios_base& strm, uint32_t c) +{ + strm.iword(sStreamState.dataCompression) = c; ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { + meta->setCompression(c); + } +} + + +void +Archive::setDataCompression(std::istream& is) +{ + io::setDataCompression(is, mCompression); ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(is)) { + meta->setCompression(mCompression); + } +} + + +//static +bool +Archive::hasBloscCompression() +{ +#ifdef OPENVDB_USE_BLOSC + return true; +#else + return false; +#endif +} + + +void +Archive::setGridCompression(std::ostream& os, const GridBase& grid) const +{ + // Start with the options that are enabled globally for this archive. + uint32_t c = compression(); + + // Disable options that are inappropriate for the given grid. + switch (grid.getGridClass()) { + case GRID_LEVEL_SET: + case GRID_FOG_VOLUME: + // ZLIB compression is not used on level sets or fog volumes. + c = c & ~COMPRESS_ZIP; + break; + case GRID_STAGGERED: + case GRID_UNKNOWN: + break; + } + io::setDataCompression(os, c); + + os.write(reinterpret_cast(&c), sizeof(uint32_t)); +} + + +void +Archive::readGridCompression(std::istream& is) +{ + if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) { + uint32_t c = COMPRESS_NONE; + is.read(reinterpret_cast(&c), sizeof(uint32_t)); + io::setDataCompression(is, c); + } +} + + +//////////////////////////////////////// + + +bool +getWriteGridStatsMetadata(std::ios_base& strm) +{ + /// @todo get from StreamMetadata + return strm.iword(sStreamState.writeGridStatsMetadata) != 0; +} + + +void +setWriteGridStatsMetadata(std::ios_base& strm, bool writeGridStats) +{ + strm.iword(sStreamState.writeGridStatsMetadata) = writeGridStats; ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { + meta->setWriteGridStats(writeGridStats); + } +} + + +//////////////////////////////////////// + + +uint32_t +getGridClass(std::ios_base& strm) +{ + /// @todo get from StreamMetadata + const uint32_t val = static_cast(strm.iword(sStreamState.gridClass)); + if (val >= NUM_GRID_CLASSES) return GRID_UNKNOWN; + return val; +} + + +void +setGridClass(std::ios_base& strm, uint32_t cls) +{ + strm.iword(sStreamState.gridClass) = long(cls); ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { + meta->setGridClass(cls); + } +} + + +bool +getHalfFloat(std::ios_base& strm) +{ + /// @todo get from StreamMetadata + return strm.iword(sStreamState.halfFloat) != 0; +} + + +void +setHalfFloat(std::ios_base& strm, bool halfFloat) +{ + strm.iword(sStreamState.halfFloat) = halfFloat; ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { + meta->setHalfFloat(halfFloat); + } +} + + +const void* +getGridBackgroundValuePtr(std::ios_base& strm) +{ + /// @todo get from StreamMetadata + return strm.pword(sStreamState.gridBackground); +} + + +void +setGridBackgroundValuePtr(std::ios_base& strm, const void* background) +{ + strm.pword(sStreamState.gridBackground) = const_cast(background); ///< @todo remove + if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { + meta->setBackgroundPtr(background); + } +} + + +MappedFile::Ptr +getMappedFilePtr(std::ios_base& strm) +{ + if (const void* ptr = strm.pword(sStreamState.mappedFile)) { + return *static_cast(ptr); + } + return MappedFile::Ptr(); +} + + +void +setMappedFilePtr(std::ios_base& strm, io::MappedFile::Ptr& mappedFile) +{ + strm.pword(sStreamState.mappedFile) = &mappedFile; +} + + +StreamMetadata::Ptr +getStreamMetadataPtr(std::ios_base& strm) +{ + if (const void* ptr = strm.pword(sStreamState.metadata)) { + return *static_cast(ptr); + } + return StreamMetadata::Ptr(); +} + + +void +setStreamMetadataPtr(std::ios_base& strm, StreamMetadata::Ptr& meta, bool transfer) +{ + strm.pword(sStreamState.metadata) = &meta; + if (transfer && meta) meta->transferTo(strm); +} + + +StreamMetadata::Ptr +clearStreamMetadataPtr(std::ios_base& strm) +{ + StreamMetadata::Ptr result = getStreamMetadataPtr(strm); + strm.pword(sStreamState.metadata) = nullptr; + return result; +} + + +//////////////////////////////////////// + + +bool +Archive::readHeader(std::istream& is) +{ + // 1) Read the magic number for VDB. + int64_t magic; + is.read(reinterpret_cast(&magic), sizeof(int64_t)); + + if (magic != OPENVDB_MAGIC) { + OPENVDB_THROW(IoError, "not a VDB file"); + } + + // 2) Read the file format version number. + is.read(reinterpret_cast(&mFileVersion), sizeof(uint32_t)); + if (mFileVersion > OPENVDB_FILE_VERSION) { + OPENVDB_LOG_WARN("unsupported VDB file format (expected version " + << OPENVDB_FILE_VERSION << " or earlier, got version " << mFileVersion << ")"); + } else if (mFileVersion < 211) { + // Versions prior to 211 stored separate major, minor and patch numbers. + uint32_t version; + is.read(reinterpret_cast(&version), sizeof(uint32_t)); + mFileVersion = 100 * mFileVersion + 10 * version; + is.read(reinterpret_cast(&version), sizeof(uint32_t)); + mFileVersion += version; + } + + // 3) Read the library version numbers (not stored prior to file format version 211). + mLibraryVersion.first = mLibraryVersion.second = 0; + if (mFileVersion >= 211) { + uint32_t version; + is.read(reinterpret_cast(&version), sizeof(uint32_t)); + mLibraryVersion.first = version; // major version + is.read(reinterpret_cast(&version), sizeof(uint32_t)); + mLibraryVersion.second = version; // minor version + } + + // 4) Read the flag indicating whether the stream supports partial reading. + // (Versions prior to 212 have no flag because they always supported partial reading.) + mInputHasGridOffsets = true; + if (mFileVersion >= 212) { + char hasGridOffsets; + is.read(&hasGridOffsets, sizeof(char)); + mInputHasGridOffsets = hasGridOffsets; + } + + // 5) Read the flag that indicates whether data is compressed. + // (From version 222 on, compression information is stored per grid.) + mCompression = DEFAULT_COMPRESSION_FLAGS; + if (mFileVersion < OPENVDB_FILE_VERSION_BLOSC_COMPRESSION) { + // Prior to the introduction of Blosc, ZLIB was the default compression scheme. + mCompression = (COMPRESS_ZIP | COMPRESS_ACTIVE_MASK); + } + if (mFileVersion >= OPENVDB_FILE_VERSION_SELECTIVE_COMPRESSION && + mFileVersion < OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) + { + char isCompressed; + is.read(&isCompressed, sizeof(char)); + mCompression = (isCompressed != 0 ? COMPRESS_ZIP : COMPRESS_NONE); + } + + // 6) Read the 16-byte (128-bit) uuid. + boost::uuids::uuid oldUuid = mUuid; + if (mFileVersion >= OPENVDB_FILE_VERSION_BOOST_UUID) { + // UUID is stored as an ASCII string. + is >> mUuid; + } else { + // Older versions stored the UUID as a byte string. + char uuidBytes[16]; + is.read(uuidBytes, 16); + std::memcpy(&mUuid.data[0], uuidBytes, std::min(16, mUuid.size())); + } + return oldUuid != mUuid; // true if UUID in input stream differs from old UUID +} + + +void +Archive::writeHeader(std::ostream& os, bool seekable) const +{ + using boost::uint32_t; + using boost::int64_t; + + // 1) Write the magic number for VDB. + int64_t magic = OPENVDB_MAGIC; + os.write(reinterpret_cast(&magic), sizeof(int64_t)); + + // 2) Write the file format version number. + uint32_t version = OPENVDB_FILE_VERSION; + os.write(reinterpret_cast(&version), sizeof(uint32_t)); + + // 3) Write the library version numbers. + version = OPENVDB_LIBRARY_MAJOR_VERSION; + os.write(reinterpret_cast(&version), sizeof(uint32_t)); + version = OPENVDB_LIBRARY_MINOR_VERSION; + os.write(reinterpret_cast(&version), sizeof(uint32_t)); + + // 4) Write a flag indicating that this stream contains no grid offsets. + char hasGridOffsets = seekable; + os.write(&hasGridOffsets, sizeof(char)); + + // 5) Write a flag indicating that this stream contains compressed leaf data. + // (Omitted as of version 222) + + // 6) Generate a new random 16-byte (128-bit) uuid and write it to the stream. + std::mt19937 ran; + ran.seed(std::mt19937::result_type(std::random_device()() + std::time(nullptr))); + boost::uuids::basic_random_generator gen(&ran); + mUuid = gen(); // mUuid is mutable + os << mUuid; +} + + +//////////////////////////////////////// + + +int32_t +Archive::readGridCount(std::istream& is) +{ + int32_t gridCount = 0; + is.read(reinterpret_cast(&gridCount), sizeof(int32_t)); + return gridCount; +} + + +//////////////////////////////////////// + + +void +Archive::connectInstance(const GridDescriptor& gd, const NamedGridMap& grids) const +{ + if (!gd.isInstance() || grids.empty()) return; + + NamedGridMap::const_iterator it = grids.find(gd.uniqueName()); + if (it == grids.end()) return; + GridBase::Ptr grid = it->second; + if (!grid) return; + + it = grids.find(gd.instanceParentName()); + if (it != grids.end()) { + GridBase::Ptr parent = it->second; + if (mEnableInstancing) { + // Share the instance parent's tree. + grid->setTree(parent->baseTreePtr()); + } else { + // Copy the instance parent's tree. + grid->setTree(parent->baseTree().copy()); + } + } else { + OPENVDB_THROW(KeyError, "missing instance parent \"" + << GridDescriptor::nameAsString(gd.instanceParentName()) + << "\" for grid " << GridDescriptor::nameAsString(gd.uniqueName())); + } +} + + +//////////////////////////////////////// + + +//static +bool +Archive::isDelayedLoadingEnabled() +{ + return (nullptr == std::getenv("OPENVDB_DISABLE_DELAYED_LOAD")); +} + + +namespace { + +struct NoBBox {}; + +template +void +doReadGrid(GridBase::Ptr grid, const GridDescriptor& gd, std::istream& is, const BoxType& bbox) +{ + struct Local { + static void readBuffers(GridBase& g, std::istream& istrm, NoBBox) { g.readBuffers(istrm); } + static void readBuffers(GridBase& g, std::istream& istrm, const CoordBBox& indexBBox) { + g.readBuffers(istrm, indexBBox); + } + static void readBuffers(GridBase& g, std::istream& istrm, const BBoxd& worldBBox) { + g.readBuffers(istrm, g.constTransform().worldToIndexNodeCentered(worldBBox)); + } + }; + + // Restore the file-level stream metadata on exit. + struct OnExit { + OnExit(std::ios_base& strm_): strm(&strm_), ptr(strm_.pword(sStreamState.metadata)) {} + ~OnExit() { strm->pword(sStreamState.metadata) = ptr; } + std::ios_base* strm; + void* ptr; + }; + OnExit restore(is); + + // Stream metadata varies per grid, and it needs to persist + // in case delayed load is in effect. + io::StreamMetadata::Ptr streamMetadata; + if (io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(is)) { + // Make a grid-level copy of the file-level stream metadata. + streamMetadata.reset(new StreamMetadata(*meta)); + } else { + streamMetadata.reset(new StreamMetadata); + } + streamMetadata->setHalfFloat(grid->saveFloatAsHalf()); + io::setStreamMetadataPtr(is, streamMetadata, /*transfer=*/false); + + io::setGridClass(is, GRID_UNKNOWN); + io::setGridBackgroundValuePtr(is, nullptr); + + grid->readMeta(is); + + // Add a description of the compression settings to the grid as metadata. + /// @todo Would this be useful? + //const uint32_t c = getDataCompression(is); + //grid->insertMeta(GridBase::META_FILE_COMPRESSION, + // StringMetadata(compressionToString(c))); + + const VersionId version = getLibraryVersion(is); + if (version.first < 6 || (version.first == 6 && version.second <= 1)) { + // If delay load metadata exists, but the file format version does not support + // delay load metadata, this likely means the original grid was read and then + // written using a prior version of OpenVDB and ABI>=5 where unknown metadata + // can be blindly copied. This means that it is possible for the metadata to + // no longer be in sync with the grid, so we remove it to ensure correctness. + + if ((*grid)[GridBase::META_FILE_DELAYED_LOAD]) { + grid->removeMeta(GridBase::META_FILE_DELAYED_LOAD); + } + } + + streamMetadata->gridMetadata() = static_cast(*grid); + const GridClass gridClass = grid->getGridClass(); + io::setGridClass(is, gridClass); + + // reset leaf value to zero + streamMetadata->setLeaf(0); + + // drop DelayedLoadMetadata from the grid as it is only useful for IO + // a stream metadata non-zero value disables this behaviour for testing + + if (streamMetadata->__test() == uint32_t(0)) { + if ((*grid)[GridBase::META_FILE_DELAYED_LOAD]) { + grid->removeMeta(GridBase::META_FILE_DELAYED_LOAD); + } + } + + if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_GRID_INSTANCING) { + grid->readTransform(is); + if (!gd.isInstance()) { + grid->readTopology(is); + Local::readBuffers(*grid, is, bbox); + } + } else { + // Older versions of the library stored the transform after the topology. + grid->readTopology(is); + grid->readTransform(is); + Local::readBuffers(*grid, is, bbox); + } + if (getFormatVersion(is) < OPENVDB_FILE_VERSION_NO_GRIDMAP) { + // Older versions of the library didn't store grid names as metadata, + // so when reading older files, copy the grid name from the descriptor + // to the grid's metadata. + if (grid->getName().empty()) { + grid->setName(gd.gridName()); + } + } +} + +} // unnamed namespace + + +void +Archive::readGrid(GridBase::Ptr grid, const GridDescriptor& gd, std::istream& is) +{ + // Read the compression settings for this grid and tag the stream with them + // so that downstream functions can reference them. + readGridCompression(is); + + doReadGrid(grid, gd, is, NoBBox()); +} + +void +Archive::readGrid(GridBase::Ptr grid, const GridDescriptor& gd, + std::istream& is, const BBoxd& worldBBox) +{ + readGridCompression(is); + doReadGrid(grid, gd, is, worldBBox); +} + +void +Archive::readGrid(GridBase::Ptr grid, const GridDescriptor& gd, + std::istream& is, const CoordBBox& indexBBox) +{ + readGridCompression(is); + doReadGrid(grid, gd, is, indexBBox); +} + + +//////////////////////////////////////// + + +void +Archive::write(std::ostream& os, const GridPtrVec& grids, bool seekable, + const MetaMap& metadata) const +{ + this->write(os, GridCPtrVec(grids.begin(), grids.end()), seekable, metadata); +} + + +void +Archive::write(std::ostream& os, const GridCPtrVec& grids, bool seekable, + const MetaMap& metadata) const +{ + // Set stream flags so that downstream functions can reference them. + io::StreamMetadata::Ptr streamMetadata = io::getStreamMetadataPtr(os); + if (!streamMetadata) { + streamMetadata.reset(new StreamMetadata); + io::setStreamMetadataPtr(os, streamMetadata, /*transfer=*/false); + } + io::setDataCompression(os, compression()); + io::setWriteGridStatsMetadata(os, isGridStatsMetadataEnabled()); + + this->writeHeader(os, seekable); + + metadata.writeMeta(os); + + // Write the number of non-null grids. + int32_t gridCount = 0; + for (GridCPtrVecCIter i = grids.begin(), e = grids.end(); i != e; ++i) { + if (*i) ++gridCount; + } + os.write(reinterpret_cast(&gridCount), sizeof(int32_t)); + + using TreeMap = std::map; + using TreeMapIter = TreeMap::iterator; + TreeMap treeMap; + + // Determine which grid names are unique and which are not. + using NameHistogram = std::map; + NameHistogram nameCount; + for (GridCPtrVecCIter i = grids.begin(), e = grids.end(); i != e; ++i) { + if (const GridBase::ConstPtr& grid = *i) { + const std::string name = grid->getName(); + NameHistogram::iterator it = nameCount.find(name); + if (it != nameCount.end()) it->second++; + else nameCount[name] = 1; + } + } + + std::set uniqueNames; + + // Write out the non-null grids. + for (GridCPtrVecCIter i = grids.begin(), e = grids.end(); i != e; ++i) { + if (const GridBase::ConstPtr& grid = *i) { + + // Ensure that the grid's descriptor has a unique grid name, by appending + // a number to it if a grid with the same name was already written. + // Always add a number if the grid name is empty, so that the grid can be + // properly identified as an instance parent, if necessary. + std::string name = grid->getName(); + if (name.empty() || nameCount[name] > 1) { + name = GridDescriptor::addSuffix(name, 0); + } + for (int n = 1; uniqueNames.find(name) != uniqueNames.end(); ++n) { + name = GridDescriptor::addSuffix(grid->getName(), n); + } + uniqueNames.insert(name); + + // Create a grid descriptor. + GridDescriptor gd(name, grid->type(), grid->saveFloatAsHalf()); + + // Check if this grid's tree is shared with a grid that has already been written. + const TreeBase* treePtr = &(grid->baseTree()); + TreeMapIter mapIter = treeMap.find(treePtr); + + bool isInstance = ((mapIter != treeMap.end()) + && (mapIter->second.saveFloatAsHalf() == gd.saveFloatAsHalf())); + + if (mEnableInstancing && isInstance) { + // This grid's tree is shared with another grid that has already been written. + // Get the name of the other grid. + gd.setInstanceParentName(mapIter->second.uniqueName()); + // Write out this grid's descriptor and metadata, but not its tree. + writeGridInstance(gd, grid, os, seekable); + + OPENVDB_LOG_DEBUG_RUNTIME("io::Archive::write(): " + << GridDescriptor::nameAsString(gd.uniqueName()) + << " (" << std::hex << treePtr << std::dec << ")" + << " is an instance of " + << GridDescriptor::nameAsString(gd.instanceParentName())); + } else { + // Write out the grid descriptor and its associated grid. + writeGrid(gd, grid, os, seekable); + // Record the grid's tree pointer so that the tree doesn't get written + // more than once. + treeMap[treePtr] = gd; + } + } + + // Some compression options (e.g., mask compression) are set per grid. + // Restore the original settings before writing the next grid. + io::setDataCompression(os, compression()); + } +} + + +void +Archive::writeGrid(GridDescriptor& gd, GridBase::ConstPtr grid, + std::ostream& os, bool seekable) const +{ + // Restore file-level stream metadata on exit. + struct OnExit { + OnExit(std::ios_base& strm_): strm(&strm_), ptr(strm_.pword(sStreamState.metadata)) {} + ~OnExit() { strm->pword(sStreamState.metadata) = ptr; } + std::ios_base* strm; + void* ptr; + }; + OnExit restore(os); + + // Stream metadata varies per grid, so make a copy of the file-level stream metadata. + io::StreamMetadata::Ptr streamMetadata; + if (io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(os)) { + streamMetadata.reset(new StreamMetadata(*meta)); + } else { + streamMetadata.reset(new StreamMetadata); + } + streamMetadata->setHalfFloat(grid->saveFloatAsHalf()); + streamMetadata->gridMetadata() = static_cast(*grid); + io::setStreamMetadataPtr(os, streamMetadata, /*transfer=*/false); + + // Write out the Descriptor's header information (grid name and type) + gd.writeHeader(os); + + // Save the curent stream position as postion to where the offsets for + // this GridDescriptor will be written to. + int64_t offsetPos = (seekable ? int64_t(os.tellp()) : 0); + + // Write out the offset information. At this point it will be incorrect. + // But we need to write it out to move the stream head forward. + gd.writeStreamPos(os); + + // Now we know the starting grid storage position. + if (seekable) gd.setGridPos(os.tellp()); + + // Save the compression settings for this grid. + setGridCompression(os, *grid); + + // copy grid and add delay load metadata + const auto copyOfGrid = grid->copyGrid(); // shallow copy + const auto nonConstCopyOfGrid = ConstPtrCast(copyOfGrid); + nonConstCopyOfGrid->insertMeta(GridBase::META_FILE_DELAYED_LOAD, + DelayedLoadMetadata()); + DelayedLoadMetadata::Ptr delayLoadMeta = + nonConstCopyOfGrid->getMetadata(GridBase::META_FILE_DELAYED_LOAD); + if (!populateDelayedLoadMetadata(*delayLoadMeta, *grid, compression())) { + nonConstCopyOfGrid->removeMeta(GridBase::META_FILE_DELAYED_LOAD); + } + + // Save the grid's metadata and transform. + if (getWriteGridStatsMetadata(os)) { + // Compute and add grid statistics metadata. + nonConstCopyOfGrid->addStatsMetadata(); + nonConstCopyOfGrid->insertMeta(GridBase::META_FILE_COMPRESSION, + StringMetadata(compressionToString(getDataCompression(os)))); + } + copyOfGrid->writeMeta(os); + grid->writeTransform(os); + + // Save the grid's structure. + grid->writeTopology(os); + + // Now we know the grid block storage position. + if (seekable) gd.setBlockPos(os.tellp()); + + // Save out the data blocks of the grid. + grid->writeBuffers(os); + + // Now we know the end position of this grid. + if (seekable) gd.setEndPos(os.tellp()); + + if (seekable) { + // Now, go back to where the Descriptor's offset information is written + // and write the offsets again. + os.seekp(offsetPos, std::ios_base::beg); + gd.writeStreamPos(os); + + // Now seek back to the end. + gd.seekToEnd(os); + } +} + + +void +Archive::writeGridInstance(GridDescriptor& gd, GridBase::ConstPtr grid, + std::ostream& os, bool seekable) const +{ + // Write out the Descriptor's header information (grid name, type + // and instance parent name). + gd.writeHeader(os); + + // Save the curent stream position as postion to where the offsets for + // this GridDescriptor will be written to. + int64_t offsetPos = (seekable ? int64_t(os.tellp()) : 0); + + // Write out the offset information. At this point it will be incorrect. + // But we need to write it out to move the stream head forward. + gd.writeStreamPos(os); + + // Now we know the starting grid storage position. + if (seekable) gd.setGridPos(os.tellp()); + + // Save the compression settings for this grid. + setGridCompression(os, *grid); + + // Save the grid's metadata and transform. + grid->writeMeta(os); + grid->writeTransform(os); + + // Now we know the end position of this grid. + if (seekable) gd.setEndPos(os.tellp()); + + if (seekable) { + // Now, go back to where the Descriptor's offset information is written + // and write the offsets again. + os.seekp(offsetPos, std::ios_base::beg); + gd.writeStreamPos(os); + + // Now seek back to the end. + gd.seekToEnd(os); + } +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/io/Archive.h b/openvdb/io/Archive.h new file mode 100644 index 00000000..b04239c0 --- /dev/null +++ b/openvdb/io/Archive.h @@ -0,0 +1,197 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_IO_ARCHIVE_HAS_BEEN_INCLUDED +#define OPENVDB_IO_ARCHIVE_HAS_BEEN_INCLUDED + +#include +#include "Compression.h" // for COMPRESS_ZIP, etc. +#include +#include +#include +#include // for VersionId +#include +#include +#include +#include +#include +#include + + +class TestFile; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +class GridDescriptor; + + +/// Grid serializer/unserializer +class OPENVDB_API Archive +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + static const uint32_t DEFAULT_COMPRESSION_FLAGS; + + Archive(); + Archive(const Archive&) = default; + Archive& operator=(const Archive&) = default; + virtual ~Archive(); + + /// @brief Return a copy of this archive. + virtual Ptr copy() const; + + /// @brief Return the UUID that was most recently written (or read, + /// if no UUID has been written yet). + std::string getUniqueTag() const; + /// @brief Return @c true if the given UUID matches this archive's UUID. + bool isIdentical(const std::string& uuidStr) const; + + /// @brief Return the file format version number of the input stream. + uint32_t fileVersion() const { return mFileVersion; } + /// @brief Return the (major, minor) version number of the library that was + /// used to write the input stream. + VersionId libraryVersion() const { return mLibraryVersion; } + /// @brief Return a string of the form "./", giving the + /// library and file format version numbers associated with the input stream. + std::string version() const; + + /// @brief Return @c true if trees shared by multiple grids are written out + /// only once, @c false if they are written out once per grid. + bool isInstancingEnabled() const { return mEnableInstancing; } + /// @brief Specify whether trees shared by multiple grids should be + /// written out only once (@c true) or once per grid (@c false). + /// @note Instancing is enabled by default. + void setInstancingEnabled(bool b) { mEnableInstancing = b; } + + /// Return @c true if the OpenVDB library includes support for the Blosc compressor. + static bool hasBloscCompression(); + + /// Return a bit mask specifying compression options for the data stream. + uint32_t compression() const { return mCompression; } + /// @brief Specify whether and how the data stream should be compressed. + /// @param c bitwise OR (e.g., COMPRESS_ZIP | COMPRESS_ACTIVE_MASK) of + /// compression option flags (see Compression.h for the available flags) + /// @note Not all combinations of compression options are supported. + void setCompression(uint32_t c) { mCompression = c; } + + /// @brief Return @c true if grid statistics (active voxel count and + /// bounding box, etc.) are computed and written as grid metadata. + bool isGridStatsMetadataEnabled() const { return mEnableGridStats; } + /// @brief Specify whether grid statistics (active voxel count and + /// bounding box, etc.) should be computed and written as grid metadata. + void setGridStatsMetadataEnabled(bool b) { mEnableGridStats = b; } + + /// @brief Write the grids in the given container to this archive's output stream. + virtual void write(const GridCPtrVec&, const MetaMap& = MetaMap()) const {} + + /// @brief Return @c true if delayed loading is enabled. + /// @details If enabled, delayed loading can be disabled for individual files, + /// but not vice-versa. + /// @note Define the environment variable @c OPENVDB_DISABLE_DELAYED_LOAD + /// to disable delayed loading unconditionally. + static bool isDelayedLoadingEnabled(); + +protected: + /// @brief Return @c true if the input stream contains grid offsets + /// that allow for random access or partial reading. + bool inputHasGridOffsets() const { return mInputHasGridOffsets; } + void setInputHasGridOffsets(bool b) { mInputHasGridOffsets = b; } + + /// @brief Tag the given input stream with the input file format version number. + /// + /// The tag can be retrieved with getFormatVersion(). + /// @sa getFormatVersion() + void setFormatVersion(std::istream&); + + /// @brief Tag the given input stream with the version number of + /// the library with which the input stream was created. + /// + /// The tag can be retrieved with getLibraryVersion(). + /// @sa getLibraryVersion() + void setLibraryVersion(std::istream&); + + /// @brief Tag the given input stream with flags indicating whether + /// the input stream contains compressed data and how it is compressed. + void setDataCompression(std::istream&); + + /// @brief Tag an output stream with flags specifying only those + /// compression options that are applicable to the given grid. + void setGridCompression(std::ostream&, const GridBase&) const; + /// @brief Read in the compression flags for a grid and + /// tag the given input stream with those flags. + static void readGridCompression(std::istream&); + + /// Read in and return the number of grids on the input stream. + static int32_t readGridCount(std::istream&); + + /// Populate the given grid from the input stream. + static void readGrid(GridBase::Ptr, const GridDescriptor&, std::istream&); + /// @brief Populate the given grid from the input stream, but only where it + /// intersects the given world-space bounding box. + static void readGrid(GridBase::Ptr, const GridDescriptor&, std::istream&, const BBoxd&); + /// @brief Populate the given grid from the input stream, but only where it + /// intersects the given index-space bounding box. + static void readGrid(GridBase::Ptr, const GridDescriptor&, std::istream&, const CoordBBox&); + + using NamedGridMap = std::map; + + /// @brief If the grid represented by the given grid descriptor + /// is an instance, connect it with its instance parent. + void connectInstance(const GridDescriptor&, const NamedGridMap&) const; + + /// Write the given grid descriptor and grid to an output stream + /// and update the GridDescriptor offsets. + /// @param seekable if true, the output stream supports seek operations + void writeGrid(GridDescriptor&, GridBase::ConstPtr, std::ostream&, bool seekable) const; + /// Write the given grid descriptor and grid metadata to an output stream + /// and update the GridDescriptor offsets, but don't write the grid's tree, + /// since it is shared with another grid. + /// @param seekable if true, the output stream supports seek operations + void writeGridInstance(GridDescriptor&, GridBase::ConstPtr, + std::ostream&, bool seekable) const; + + /// @brief Read the magic number, version numbers, UUID, etc. from the given input stream. + /// @return @c true if the input UUID differs from the previously-read UUID. + bool readHeader(std::istream&); + /// @brief Write the magic number, version numbers, UUID, etc. to the given output stream. + /// @param seekable if true, the output stream supports seek operations + /// @todo This method should not be const since it actually redefines the UUID! + void writeHeader(std::ostream&, bool seekable) const; + + //@{ + /// Write the given grids to an output stream. + void write(std::ostream&, const GridPtrVec&, bool seekable, const MetaMap& = MetaMap()) const; + void write(std::ostream&, const GridCPtrVec&, bool seekable, const MetaMap& = MetaMap()) const; + //@} + +private: + friend class ::TestFile; + + /// The version of the file that was read + uint32_t mFileVersion; + /// The version of the library that was used to create the file that was read + VersionId mLibraryVersion; + /// 16-byte (128-bit) UUID + mutable boost::uuids::uuid mUuid;// needs to be mutable since writeHeader is const! + /// Flag indicating whether the input stream contains grid offsets + /// and therefore supports partial reading + bool mInputHasGridOffsets; + /// Flag indicating whether a tree shared by multiple grids should be + /// written out only once (true) or once per grid (false) + bool mEnableInstancing; + /// Flags indicating whether and how the data stream is compressed + uint32_t mCompression; + /// Flag indicating whether grid statistics metadata should be written + bool mEnableGridStats; +}; // class Archive + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_ARCHIVE_HAS_BEEN_INCLUDED diff --git a/openvdb/io/Compression.cc b/openvdb/io/Compression.cc new file mode 100644 index 00000000..43c929cc --- /dev/null +++ b/openvdb/io/Compression.cc @@ -0,0 +1,281 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Compression.h" + +#include +#include +#include +#include +#ifdef OPENVDB_USE_BLOSC +#include +#endif + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +std::string +compressionToString(uint32_t flags) +{ + if (flags == COMPRESS_NONE) return "none"; + + std::vector words; + if (flags & COMPRESS_ZIP) words.push_back("zip"); + if (flags & COMPRESS_BLOSC) words.push_back("blosc"); + if (flags & COMPRESS_ACTIVE_MASK) words.push_back("active values"); + return boost::join(words, " + "); +} + + +//////////////////////////////////////// + + +namespace { +const int ZIP_COMPRESSION_LEVEL = Z_DEFAULT_COMPRESSION; ///< @todo use Z_BEST_SPEED? +} + + +size_t +zipToStreamSize(const char* data, size_t numBytes) +{ + // Get an upper bound on the size of the compressed data. + uLongf numZippedBytes = compressBound(numBytes); + // Compress the data. + std::unique_ptr zippedData(new Bytef[numZippedBytes]); + int status = compress2( + /*dest=*/zippedData.get(), &numZippedBytes, + /*src=*/reinterpret_cast(data), numBytes, + /*level=*/ZIP_COMPRESSION_LEVEL); + if (status == Z_OK && numZippedBytes < numBytes) { + return size_t(numZippedBytes); + } else { + return size_t(numBytes); + } +} + + +void +zipToStream(std::ostream& os, const char* data, size_t numBytes) +{ + // Get an upper bound on the size of the compressed data. + uLongf numZippedBytes = compressBound(numBytes); + // Compress the data. + std::unique_ptr zippedData(new Bytef[numZippedBytes]); + int status = compress2( + /*dest=*/zippedData.get(), &numZippedBytes, + /*src=*/reinterpret_cast(data), numBytes, + /*level=*/ZIP_COMPRESSION_LEVEL); + if (status != Z_OK) { + std::string errDescr; + if (const char* s = zError(status)) errDescr = s; + if (!errDescr.empty()) errDescr = " (" + errDescr + ")"; + OPENVDB_LOG_DEBUG("zlib compress2() returned error code " << status << errDescr); + } + if (status == Z_OK && numZippedBytes < numBytes) { + // Write the size of the compressed data. + Int64 outZippedBytes = numZippedBytes; + os.write(reinterpret_cast(&outZippedBytes), 8); + // Write the compressed data. + os.write(reinterpret_cast(zippedData.get()), outZippedBytes); + } else { + // Write the size of the uncompressed data. + Int64 negBytes = -numBytes; + os.write(reinterpret_cast(&negBytes), 8); + // Write the uncompressed data. + os.write(reinterpret_cast(data), numBytes); + } +} + + +void +unzipFromStream(std::istream& is, char* data, size_t numBytes) +{ + // Read the size of the compressed data. + // A negative size indicates uncompressed data. + Int64 numZippedBytes; + is.read(reinterpret_cast(&numZippedBytes), 8); + + if (numZippedBytes <= 0) { + // Read the uncompressed data. + if (data == nullptr) { + is.seekg(-numZippedBytes, std::ios_base::cur); + } else { + is.read(data, -numZippedBytes); + } + if (size_t(-numZippedBytes) != numBytes) { + OPENVDB_THROW(RuntimeError, "Expected to read a " << numBytes + << "-byte chunk, got a " << -numZippedBytes << "-byte chunk"); + } + } else { + if (data == nullptr) { + // Seek over the compressed data. + is.seekg(numZippedBytes, std::ios_base::cur); + } else { + // Read the compressed data. + std::unique_ptr zippedData(new Bytef[numZippedBytes]); + is.read(reinterpret_cast(zippedData.get()), numZippedBytes); + // Uncompress the data. + uLongf numUnzippedBytes = numBytes; + int status = uncompress( + /*dest=*/reinterpret_cast(data), &numUnzippedBytes, + /*src=*/zippedData.get(), static_cast(numZippedBytes)); + if (status != Z_OK) { + std::string errDescr; + if (const char* s = zError(status)) errDescr = s; + if (!errDescr.empty()) errDescr = " (" + errDescr + ")"; + OPENVDB_LOG_DEBUG("zlib uncompress() returned error code " << status << errDescr); + } + if (numUnzippedBytes != numBytes) { + OPENVDB_THROW(RuntimeError, "Expected to decompress " << numBytes + << " byte" << (numBytes == 1 ? "" : "s") << ", got " + << numZippedBytes << " byte" << (numZippedBytes == 1 ? "" : "s")); + } + } + } +} + + +namespace { + +#ifdef OPENVDB_USE_BLOSC +int bloscCompress(size_t inBytes, const char* data, char* compressedData, int outBytes) +{ + return blosc_compress_ctx( + /*clevel=*/9, // 0 (no compression) to 9 (maximum compression) + /*doshuffle=*/true, + /*typesize=*/sizeof(float), //for optimal float and Vec3f compression + /*srcsize=*/inBytes, + /*src=*/data, + /*dest=*/compressedData, + /*destsize=*/outBytes, + BLOSC_LZ4_COMPNAME, + /*blocksize=*/inBytes,//previously set to 256 (in v3.x) + /*numthreads=*/1); +} +#endif + +} // namespace + + +#ifndef OPENVDB_USE_BLOSC +size_t +bloscToStreamSize(const char*, size_t, size_t) +{ + OPENVDB_THROW(IoError, "Blosc encoding is not supported"); +} +#else +size_t +bloscToStreamSize(const char* data, size_t valSize, size_t numVals) +{ + const size_t inBytes = valSize * numVals; + + int outBytes = int(inBytes) + BLOSC_MAX_OVERHEAD; + std::unique_ptr compressedData(new char[outBytes]); + + outBytes = bloscCompress(inBytes, data, compressedData.get(), outBytes); + + if (outBytes <= 0) { + return size_t(inBytes); + } + + return size_t(outBytes); +} +#endif + + +#ifndef OPENVDB_USE_BLOSC +void +bloscToStream(std::ostream&, const char*, size_t, size_t) +{ + OPENVDB_THROW(IoError, "Blosc encoding is not supported"); +} +#else +void +bloscToStream(std::ostream& os, const char* data, size_t valSize, size_t numVals) +{ + const size_t inBytes = valSize * numVals; + + int outBytes = int(inBytes) + BLOSC_MAX_OVERHEAD; + std::unique_ptr compressedData(new char[outBytes]); + + outBytes = bloscCompress(inBytes, data, compressedData.get(), outBytes); + + if (outBytes <= 0) { + std::ostringstream ostr; + ostr << "Blosc failed to compress " << inBytes << " byte" << (inBytes == 1 ? "" : "s"); + if (outBytes < 0) ostr << " (internal error " << outBytes << ")"; + OPENVDB_LOG_DEBUG(ostr.str()); + + // Write the size of the uncompressed data. + Int64 negBytes = -inBytes; + os.write(reinterpret_cast(&negBytes), 8); + // Write the uncompressed data. + os.write(reinterpret_cast(data), inBytes); + } else { + // Write the size of the compressed data. + Int64 numBytes = outBytes; + os.write(reinterpret_cast(&numBytes), 8); + // Write the compressed data. + os.write(reinterpret_cast(compressedData.get()), outBytes); + } +} +#endif + + +#ifndef OPENVDB_USE_BLOSC +void +bloscFromStream(std::istream&, char*, size_t) +{ + OPENVDB_THROW(IoError, "Blosc decoding is not supported"); +} +#else +void +bloscFromStream(std::istream& is, char* data, size_t numBytes) +{ + // Read the size of the compressed data. + // A negative size indicates uncompressed data. + Int64 numCompressedBytes; + is.read(reinterpret_cast(&numCompressedBytes), 8); + + if (numCompressedBytes <= 0) { + // Read the uncompressed data. + if (data == nullptr) { + is.seekg(-numCompressedBytes, std::ios_base::cur); + } else { + is.read(data, -numCompressedBytes); + } + if (size_t(-numCompressedBytes) != numBytes) { + OPENVDB_THROW(RuntimeError, "Expected to read a " << numBytes + << "-byte uncompressed chunk, got a " << -numCompressedBytes << "-byte chunk"); + } + } else { + if (data == nullptr) { + // Seek over the compressed data. + is.seekg(numCompressedBytes, std::ios_base::cur); + } else { + // Read the compressed data. + std::unique_ptr compressedData(new char[numCompressedBytes]); + is.read(reinterpret_cast(compressedData.get()), numCompressedBytes); + // Uncompress the data. + const int numUncompressedBytes = blosc_decompress_ctx( + /*src=*/compressedData.get(), /*dest=*/data, numBytes, /*numthreads=*/1); + if (numUncompressedBytes < 1) { + OPENVDB_LOG_DEBUG("blosc_decompress() returned error code " + << numUncompressedBytes); + } + if (numUncompressedBytes != Int64(numBytes)) { + OPENVDB_THROW(RuntimeError, "Expected to decompress " << numBytes + << " byte" << (numBytes == 1 ? "" : "s") << ", got " + << numUncompressedBytes << " byte" << (numUncompressedBytes == 1 ? "" : "s")); + } + } + } +} +#endif + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/io/Compression.h b/openvdb/io/Compression.h new file mode 100644 index 00000000..9f1b75fe --- /dev/null +++ b/openvdb/io/Compression.h @@ -0,0 +1,755 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED +#define OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED + +#include +#include +#include // for negative() +#include "io.h" // for getDataCompression(), etc. +#include "DelayedLoadMetadata.h" +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +/// @brief OR-able bit flags for compression options on input and output streams +/// @details +///
+///
COMPRESS_NONE +///
On write, don't compress data.
+/// On read, the input stream contains uncompressed data. +/// +///
COMPRESS_ZIP +///
When writing grids other than level sets or fog volumes, apply +/// ZLIB compression to internal and leaf node value buffers.
+/// When reading grids other than level sets or fog volumes, indicate that +/// the value buffers of internal and leaf nodes are ZLIB-compressed.
+/// ZLIB compresses well but is slow. +/// +///
COMPRESS_ACTIVE_MASK +///
When writing a grid of any class, don't output a node's inactive values +/// if it has two or fewer distinct values. Instead, output minimal information +/// to permit the lossless reconstruction of inactive values.
+/// On read, nodes might have been stored without inactive values. +/// Where necessary, reconstruct inactive values from available information. +/// +///
COMPRESS_BLOSC +///
When writing grids other than level sets or fog volumes, apply +/// Blosc compression to internal and leaf node value buffers.
+/// When reading grids other than level sets or fog volumes, indicate that +/// the value buffers of internal and leaf nodes are Blosc-compressed.
+/// Blosc is much faster than ZLIB and produces comparable file sizes. +///
+enum { + COMPRESS_NONE = 0, + COMPRESS_ZIP = 0x1, + COMPRESS_ACTIVE_MASK = 0x2, + COMPRESS_BLOSC = 0x4 +}; + +/// Return a string describing the given compression flags. +OPENVDB_API std::string compressionToString(uint32_t flags); + + +//////////////////////////////////////// + + +/// @internal Per-node indicator byte that specifies what additional metadata +/// is stored to permit reconstruction of inactive values +enum { + /*0*/ NO_MASK_OR_INACTIVE_VALS, // no inactive vals, or all inactive vals are +background + /*1*/ NO_MASK_AND_MINUS_BG, // all inactive vals are -background + /*2*/ NO_MASK_AND_ONE_INACTIVE_VAL, // all inactive vals have the same non-background val + /*3*/ MASK_AND_NO_INACTIVE_VALS, // mask selects between -background and +background + /*4*/ MASK_AND_ONE_INACTIVE_VAL, // mask selects between backgd and one other inactive val + /*5*/ MASK_AND_TWO_INACTIVE_VALS, // mask selects between two non-background inactive vals + /*6*/ NO_MASK_AND_ALL_VALS // > 2 inactive vals, so no mask compression at all +}; + + +template +struct MaskCompress +{ + // Comparison function for values + static inline bool eq(const ValueT& a, const ValueT& b) { + return math::isExactlyEqual(a, b); + } + + MaskCompress( + const MaskT& valueMask, const MaskT& childMask, + const ValueT* srcBuf, const ValueT& background) + { + /// @todo Consider all values, not just inactive values? + inactiveVal[0] = inactiveVal[1] = background; + int numUniqueInactiveVals = 0; + for (typename MaskT::OffIterator it = valueMask.beginOff(); + numUniqueInactiveVals < 3 && it; ++it) + { + const Index32 idx = it.pos(); + + // Skip inactive values that are actually child node pointers. + if (childMask.isOn(idx)) continue; + + const ValueT& val = srcBuf[idx]; + const bool unique = !( + (numUniqueInactiveVals > 0 && MaskCompress::eq(val, inactiveVal[0])) || + (numUniqueInactiveVals > 1 && MaskCompress::eq(val, inactiveVal[1])) + ); + if (unique) { + if (numUniqueInactiveVals < 2) inactiveVal[numUniqueInactiveVals] = val; + ++numUniqueInactiveVals; + } + } + + metadata = NO_MASK_OR_INACTIVE_VALS; + + if (numUniqueInactiveVals == 1) { + if (!MaskCompress::eq(inactiveVal[0], background)) { + if (MaskCompress::eq(inactiveVal[0], math::negative(background))) { + metadata = NO_MASK_AND_MINUS_BG; + } else { + metadata = NO_MASK_AND_ONE_INACTIVE_VAL; + } + } + } else if (numUniqueInactiveVals == 2) { + metadata = NO_MASK_OR_INACTIVE_VALS; + if (!MaskCompress::eq(inactiveVal[0], background) && !MaskCompress::eq(inactiveVal[1], background)) { + // If neither inactive value is equal to the background, both values + // need to be saved, along with a mask that selects between them. + metadata = MASK_AND_TWO_INACTIVE_VALS; + + } else if (MaskCompress::eq(inactiveVal[1], background)) { + if (MaskCompress::eq(inactiveVal[0], math::negative(background))) { + // If the second inactive value is equal to the background and + // the first is equal to -background, neither value needs to be saved, + // but save a mask that selects between -background and +background. + metadata = MASK_AND_NO_INACTIVE_VALS; + } else { + // If the second inactive value is equal to the background, only + // the first value needs to be saved, along with a mask that selects + // between it and the background. + metadata = MASK_AND_ONE_INACTIVE_VAL; + } + } else if (MaskCompress::eq(inactiveVal[0], background)) { + if (MaskCompress::eq(inactiveVal[1], math::negative(background))) { + // If the first inactive value is equal to the background and + // the second is equal to -background, neither value needs to be saved, + // but save a mask that selects between -background and +background. + metadata = MASK_AND_NO_INACTIVE_VALS; + std::swap(inactiveVal[0], inactiveVal[1]); + } else { + // If the first inactive value is equal to the background, swap it + // with the second value and save only that value, along with a mask + // that selects between it and the background. + std::swap(inactiveVal[0], inactiveVal[1]); + metadata = MASK_AND_ONE_INACTIVE_VAL; + } + } + } else if (numUniqueInactiveVals > 2) { + metadata = NO_MASK_AND_ALL_VALS; + } + } + + int8_t metadata = NO_MASK_AND_ALL_VALS; + ValueT inactiveVal[2]; +}; + + +//////////////////////////////////////// + + +/// @brief RealToHalf and its specializations define a mapping from +/// floating-point data types to analogous half float types. +template +struct RealToHalf { + enum { isReal = false }; // unless otherwise specified, type T is not a floating-point type + using HalfT = T; // type T's half float analogue is T itself + static HalfT convert(const T& val) { return val; } +}; +template<> struct RealToHalf { + enum { isReal = true }; + using HalfT = half; + static HalfT convert(float val) { return HalfT(val); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + using HalfT = half; + // A half can only be constructed from a float, so cast the value to a float first. + static HalfT convert(double val) { return HalfT(float(val)); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + using HalfT = Vec2H; + static HalfT convert(const Vec2s& val) { return HalfT(val); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + using HalfT = Vec2H; + // A half can only be constructed from a float, so cast the vector's elements to floats first. + static HalfT convert(const Vec2d& val) { return HalfT(Vec2s(val)); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + using HalfT = Vec3H; + static HalfT convert(const Vec3s& val) { return HalfT(val); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + using HalfT = Vec3H; + // A half can only be constructed from a float, so cast the vector's elements to floats first. + static HalfT convert(const Vec3d& val) { return HalfT(Vec3s(val)); } +}; + + +/// Return the given value truncated to 16-bit float precision. +template +inline T +truncateRealToHalf(const T& val) +{ + return T(RealToHalf::convert(val)); +} + + +//////////////////////////////////////// + + +OPENVDB_API size_t zipToStreamSize(const char* data, size_t numBytes); +OPENVDB_API void zipToStream(std::ostream&, const char* data, size_t numBytes); +OPENVDB_API void unzipFromStream(std::istream&, char* data, size_t numBytes); +OPENVDB_API size_t bloscToStreamSize(const char* data, size_t valSize, size_t numVals); +OPENVDB_API void bloscToStream(std::ostream&, const char* data, size_t valSize, size_t numVals); +OPENVDB_API void bloscFromStream(std::istream&, char* data, size_t numBytes); + +/// @brief Read data from a stream. +/// @param is the input stream +/// @param data the contiguous array of data to read in +/// @param count the number of elements to read in +/// @param compression whether and how the data is compressed (either COMPRESS_NONE, +/// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC) +/// @param metadata optional pointer to a DelayedLoadMetadata object that stores +/// the size of the compressed buffer +/// @param metadataOffset offset into DelayedLoadMetadata, ignored if pointer is null +/// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled +/// without Blosc support. +/// @details This default implementation is instantiated only for types +/// whose size can be determined by the sizeof() operator. +template +inline void +readData(std::istream& is, T* data, Index count, uint32_t compression, + DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0)) +{ + const bool seek = data == nullptr; + if (seek) { + assert(!getStreamMetadataPtr(is) || getStreamMetadataPtr(is)->seekable()); + } + const bool hasCompression = compression & (COMPRESS_BLOSC | COMPRESS_ZIP); + + if (metadata && seek && hasCompression) { + size_t compressedSize = metadata->getCompressedSize(metadataOffset); + is.seekg(compressedSize, std::ios_base::cur); + } else if (compression & COMPRESS_BLOSC) { + bloscFromStream(is, reinterpret_cast(data), sizeof(T) * count); + } else if (compression & COMPRESS_ZIP) { + unzipFromStream(is, reinterpret_cast(data), sizeof(T) * count); + } else if (seek) { + is.seekg(sizeof(T) * count, std::ios_base::cur); + } else { + is.read(reinterpret_cast(data), sizeof(T) * count); + } +} + +/// Specialization for std::string input +template<> +inline void +readData(std::istream& is, std::string* data, Index count, uint32_t /*compression*/, + DelayedLoadMetadata* /*metadata*/, size_t /*metadataOffset*/) +{ + for (Index i = 0; i < count; ++i) { + size_t len = 0; + is >> len; + //data[i].resize(len); + //is.read(&(data[i][0]), len); + + std::string buffer(len+1, ' '); + is.read(&buffer[0], len+1); + if (data != nullptr) data[i].assign(buffer, 0, len); + } +} + +/// HalfReader wraps a static function, read(), that is analogous to readData(), above, +/// except that it is partially specialized for floating-point types in order to promote +/// 16-bit half float values to full float. A wrapper class is required because +/// only classes, not functions, can be partially specialized. +template struct HalfReader; +/// Partial specialization for non-floating-point types (no half to float promotion) +template +struct HalfReader { + static inline void read(std::istream& is, T* data, Index count, uint32_t compression, + DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0)) { + readData(is, data, count, compression, metadata, metadataOffset); + } +}; +/// Partial specialization for floating-point types +template +struct HalfReader { + using HalfT = typename RealToHalf::HalfT; + static inline void read(std::istream& is, T* data, Index count, uint32_t compression, + DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0)) { + if (count < 1) return; + if (data == nullptr) { + // seek mode - pass through null pointer + readData(is, nullptr, count, compression, metadata, metadataOffset); + } else { + std::vector halfData(count); // temp buffer into which to read half float values + readData(is, reinterpret_cast(&halfData[0]), count, compression, + metadata, metadataOffset); + // Copy half float values from the temporary buffer to the full float output array. + std::copy(halfData.begin(), halfData.end(), data); + } + } +}; + + +template +inline size_t +writeDataSize(const T *data, Index count, uint32_t compression) +{ + if (compression & COMPRESS_BLOSC) { + return bloscToStreamSize(reinterpret_cast(data), sizeof(T), count); + } else if (compression & COMPRESS_ZIP) { + return zipToStreamSize(reinterpret_cast(data), sizeof(T) * count); + } else { + return sizeof(T) * count; + } +} + + +/// Specialization for std::string output +template<> +inline size_t +writeDataSize(const std::string* data, Index count, + uint32_t /*compression*/) ///< @todo add compression +{ + size_t size(0); + for (Index i = 0; i < count; ++i) { + const size_t len = data[i].size(); + size += sizeof(size_t) + (len+1); + } + return size; +} + + +/// Write data to a stream. +/// @param os the output stream +/// @param data the contiguous array of data to write +/// @param count the number of elements to write out +/// @param compression whether and how to compress the data (either COMPRESS_NONE, +/// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC) +/// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled +/// without Blosc support. +/// @details This default implementation is instantiated only for types +/// whose size can be determined by the sizeof() operator. +template +inline void +writeData(std::ostream &os, const T *data, Index count, uint32_t compression) +{ + if (compression & COMPRESS_BLOSC) { + bloscToStream(os, reinterpret_cast(data), sizeof(T), count); + } else if (compression & COMPRESS_ZIP) { + zipToStream(os, reinterpret_cast(data), sizeof(T) * count); + } else { + os.write(reinterpret_cast(data), sizeof(T) * count); + } +} + +/// Specialization for std::string output +template<> +inline void +writeData(std::ostream& os, const std::string* data, Index count, + uint32_t /*compression*/) ///< @todo add compression +{ + for (Index i = 0; i < count; ++i) { + const size_t len = data[i].size(); + os << len; + os.write(data[i].c_str(), len+1); + //os.write(&(data[i][0]), len ); + } +} + +/// HalfWriter wraps a static function, write(), that is analogous to writeData(), above, +/// except that it is partially specialized for floating-point types in order to quantize +/// floating-point values to 16-bit half float. A wrapper class is required because +/// only classes, not functions, can be partially specialized. +template struct HalfWriter; +/// Partial specialization for non-floating-point types (no float to half quantization) +template +struct HalfWriter { + static inline size_t writeSize(const T* data, Index count, uint32_t compression) { + return writeDataSize(data, count, compression); + } + static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) { + writeData(os, data, count, compression); + } +}; +/// Partial specialization for floating-point types +template +struct HalfWriter { + using HalfT = typename RealToHalf::HalfT; + static inline size_t writeSize(const T* data, Index count, uint32_t compression) { + if (count < 1) return size_t(0); + // Convert full float values to half float, then output the half float array. + std::vector halfData(count); + for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf::convert(data[i]); + return writeDataSize(reinterpret_cast(&halfData[0]), count, compression); + } + static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) { + if (count < 1) return; + // Convert full float values to half float, then output the half float array. + std::vector halfData(count); + for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf::convert(data[i]); + writeData(os, reinterpret_cast(&halfData[0]), count, compression); + } +}; +#ifdef _MSC_VER +/// Specialization to avoid double to float warnings in MSVC +template<> +struct HalfWriter { + using HalfT = RealToHalf::HalfT; + static inline size_t writeSize(const double* data, Index count, uint32_t compression) + { + if (count < 1) return size_t(0); + // Convert full float values to half float, then output the half float array. + std::vector halfData(count); + for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf::convert(data[i]); + return writeDataSize(reinterpret_cast(&halfData[0]), count, compression); + } + static inline void write(std::ostream& os, const double* data, Index count, + uint32_t compression) + { + if (count < 1) return; + // Convert full float values to half float, then output the half float array. + std::vector halfData(count); + for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf::convert(data[i]); + writeData(os, reinterpret_cast(&halfData[0]), count, compression); + } +}; +#endif // _MSC_VER + + +//////////////////////////////////////// + + +/// Populate the given buffer with @a destCount values of type @c ValueT +/// read from the given stream, taking into account that the stream might +/// have been compressed via one of several supported schemes. +/// [Mainly for internal use] +/// @param is a stream from which to read data (possibly compressed, +/// depending on the stream's compression settings) +/// @param destBuf a buffer into which to read values of type @c ValueT +/// @param destCount the number of values to be stored in the buffer +/// @param valueMask a bitmask (typically, a node's value mask) indicating +/// which positions in the buffer correspond to active values +/// @param fromHalf if true, read 16-bit half floats from the input stream +/// and convert them to full floats +template +inline void +readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount, + const MaskT& valueMask, bool fromHalf) +{ + // Get the stream's compression settings. + auto meta = getStreamMetadataPtr(is); + const uint32_t compression = getDataCompression(is); + const bool maskCompressed = compression & COMPRESS_ACTIVE_MASK; + + const bool seek = (destBuf == nullptr); + assert(!seek || (!meta || meta->seekable())); + + // Get delayed load metadata if it exists + + DelayedLoadMetadata::Ptr delayLoadMeta; + uint64_t leafIndex(0); + if (seek && meta && meta->delayedLoadMeta()) { + delayLoadMeta = + meta->gridMetadata().getMetadata("file_delayed_load"); + leafIndex = meta->leaf(); + } + + int8_t metadata = NO_MASK_AND_ALL_VALS; + if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) { + // Read the flag that specifies what, if any, additional metadata + // (selection mask and/or inactive value(s)) is saved. + if (seek && !maskCompressed) { + is.seekg(/*bytes=*/1, std::ios_base::cur); + } else if (seek && delayLoadMeta) { + metadata = delayLoadMeta->getMask(leafIndex); + is.seekg(/*bytes=*/1, std::ios_base::cur); + } else { + is.read(reinterpret_cast(&metadata), /*bytes=*/1); + } + } + + ValueT background = zeroVal(); + if (const void* bgPtr = getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + ValueT inactiveVal1 = background; + ValueT inactiveVal0 = + ((metadata == NO_MASK_OR_INACTIVE_VALS) ? background : math::negative(background)); + + if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_TWO_INACTIVE_VALS) + { + // Read one of at most two distinct inactive values. + if (seek) { + is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur); + } else { + is.read(reinterpret_cast(&inactiveVal0), /*bytes=*/sizeof(ValueT)); + } + if (metadata == MASK_AND_TWO_INACTIVE_VALS) { + // Read the second of two distinct inactive values. + if (seek) { + is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur); + } else { + is.read(reinterpret_cast(&inactiveVal1), /*bytes=*/sizeof(ValueT)); + } + } + } + + MaskT selectionMask; + if (metadata == MASK_AND_NO_INACTIVE_VALS || + metadata == MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_TWO_INACTIVE_VALS) + { + // For use in mask compression (only), read the bitmask that selects + // between two distinct inactive values. + if (seek) { + is.seekg(/*bytes=*/selectionMask.memUsage(), std::ios_base::cur); + } else { + selectionMask.load(is); + } + } + + ValueT* tempBuf = destBuf; + std::unique_ptr scopedTempBuf; + + Index tempCount = destCount; + + if (maskCompressed && metadata != NO_MASK_AND_ALL_VALS + && getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) + { + tempCount = valueMask.countOn(); + if (!seek && tempCount != destCount) { + // If this node has inactive voxels, allocate a temporary buffer + // into which to read just the active values. + scopedTempBuf.reset(new ValueT[tempCount]); + tempBuf = scopedTempBuf.get(); + } + } + + // Read in the buffer. + if (fromHalf) { + HalfReader::isReal, ValueT>::read( + is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get(), leafIndex); + } else { + readData( + is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get(), leafIndex); + } + + // If mask compression is enabled and the number of active values read into + // the temp buffer is smaller than the size of the destination buffer, + // then there are missing (inactive) values. + if (!seek && maskCompressed && tempCount != destCount) { + // Restore inactive values, using the background value and, if available, + // the inside/outside mask. (For fog volumes, the destination buffer is assumed + // to be initialized to background value zero, so inactive values can be ignored.) + for (Index destIdx = 0, tempIdx = 0; destIdx < MaskT::SIZE; ++destIdx) { + if (valueMask.isOn(destIdx)) { + // Copy a saved active value into this node's buffer. + destBuf[destIdx] = tempBuf[tempIdx]; + ++tempIdx; + } else { + // Reconstruct an unsaved inactive value and copy it into this node's buffer. + destBuf[destIdx] = (selectionMask.isOn(destIdx) ? inactiveVal1 : inactiveVal0); + } + } + } +} + + +template +inline size_t +writeCompressedValuesSize(ValueT* srcBuf, Index srcCount, + const MaskT& valueMask, uint8_t maskMetadata, bool toHalf, uint32_t compress) +{ + using NonConstValueT = typename std::remove_const::type; + + const bool maskCompress = compress & COMPRESS_ACTIVE_MASK; + + Index tempCount = srcCount; + ValueT* tempBuf = srcBuf; + std::unique_ptr scopedTempBuf; + + if (maskCompress && maskMetadata != NO_MASK_AND_ALL_VALS) { + + tempCount = 0; + + Index64 onVoxels = valueMask.countOn(); + if (onVoxels > Index64(0)) { + // Create a new array to hold just the active values. + scopedTempBuf.reset(new NonConstValueT[onVoxels]); + NonConstValueT* localTempBuf = scopedTempBuf.get(); + + // Copy active values to a new, contiguous array. + for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) { + localTempBuf[tempCount] = srcBuf[it.pos()]; + } + + tempBuf = scopedTempBuf.get(); + } + } + + // Return the buffer size. + if (toHalf) { + return HalfWriter::isReal, NonConstValueT>::writeSize( + tempBuf, tempCount, compress); + } else { + return writeDataSize(tempBuf, tempCount, compress); + } +} + + +/// Write @a srcCount values of type @c ValueT to the given stream, optionally +/// after compressing the values via one of several supported schemes. +/// [Mainly for internal use] +/// @param os a stream to which to write data (possibly compressed, depending +/// on the stream's compression settings) +/// @param srcBuf a buffer containing values of type @c ValueT to be written +/// @param srcCount the number of values stored in the buffer +/// @param valueMask a bitmask (typically, a node's value mask) indicating +/// which positions in the buffer correspond to active values +/// @param childMask a bitmask (typically, a node's child mask) indicating +/// which positions in the buffer correspond to child node pointers +/// @param toHalf if true, convert floating-point values to 16-bit half floats +template +inline void +writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount, + const MaskT& valueMask, const MaskT& childMask, bool toHalf) +{ + // Get the stream's compression settings. + const uint32_t compress = getDataCompression(os); + const bool maskCompress = compress & COMPRESS_ACTIVE_MASK; + + Index tempCount = srcCount; + ValueT* tempBuf = srcBuf; + std::unique_ptr scopedTempBuf; + + int8_t metadata = NO_MASK_AND_ALL_VALS; + + if (!maskCompress) { + os.write(reinterpret_cast(&metadata), /*bytes=*/1); + } else { + // A valid level set's inactive values are either +background (outside) + // or -background (inside), and a fog volume's inactive values are all zero. + // Rather than write out all of these values, we can store just the active values + // (given that the value mask specifies their positions) and, if necessary, + // an inside/outside bitmask. + + const ValueT zero = zeroVal(); + ValueT background = zero; + if (const void* bgPtr = getGridBackgroundValuePtr(os)) { + background = *static_cast(bgPtr); + } + + MaskCompress maskCompressData(valueMask, childMask, srcBuf, background); + metadata = maskCompressData.metadata; + + os.write(reinterpret_cast(&metadata), /*bytes=*/1); + + if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_TWO_INACTIVE_VALS) + { + if (!toHalf) { + // Write one of at most two distinct inactive values. + os.write(reinterpret_cast(&maskCompressData.inactiveVal[0]), sizeof(ValueT)); + if (metadata == MASK_AND_TWO_INACTIVE_VALS) { + // Write the second of two distinct inactive values. + os.write(reinterpret_cast(&maskCompressData.inactiveVal[1]), sizeof(ValueT)); + } + } else { + // Write one of at most two distinct inactive values. + ValueT truncatedVal = static_cast(truncateRealToHalf(maskCompressData.inactiveVal[0])); + os.write(reinterpret_cast(&truncatedVal), sizeof(ValueT)); + if (metadata == MASK_AND_TWO_INACTIVE_VALS) { + // Write the second of two distinct inactive values. + truncatedVal = truncateRealToHalf(maskCompressData.inactiveVal[1]); + os.write(reinterpret_cast(&truncatedVal), sizeof(ValueT)); + } + } + } + + if (metadata == NO_MASK_AND_ALL_VALS) { + // If there are more than two unique inactive values, the entire input buffer + // needs to be saved (both active and inactive values). + /// @todo Save the selection mask as long as most of the inactive values + /// are one of two values? + } else { + // Create a new array to hold just the active values. + scopedTempBuf.reset(new ValueT[srcCount]); + tempBuf = scopedTempBuf.get(); + + if (metadata == NO_MASK_OR_INACTIVE_VALS || + metadata == NO_MASK_AND_MINUS_BG || + metadata == NO_MASK_AND_ONE_INACTIVE_VAL) + { + // Copy active values to the contiguous array. + tempCount = 0; + for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) { + tempBuf[tempCount] = srcBuf[it.pos()]; + } + } else { + // Copy active values to a new, contiguous array and populate a bitmask + // that selects between two distinct inactive values. + MaskT selectionMask; + tempCount = 0; + for (Index srcIdx = 0; srcIdx < srcCount; ++srcIdx) { + if (valueMask.isOn(srcIdx)) { // active value + tempBuf[tempCount] = srcBuf[srcIdx]; + ++tempCount; + } else { // inactive value + if (MaskCompress::eq(srcBuf[srcIdx], maskCompressData.inactiveVal[1])) { + selectionMask.setOn(srcIdx); // inactive value 1 + } // else inactive value 0 + } + } + assert(tempCount == valueMask.countOn()); + + // Write out the mask that selects between two inactive values. + selectionMask.save(os); + } + } + } + + // Write out the buffer. + if (toHalf) { + HalfWriter::isReal, ValueT>::write(os, tempBuf, tempCount, compress); + } else { + writeData(os, tempBuf, tempCount, compress); + } +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED \ No newline at end of file diff --git a/openvdb/io/DelayedLoadMetadata.cc b/openvdb/io/DelayedLoadMetadata.cc new file mode 100644 index 00000000..73879683 --- /dev/null +++ b/openvdb/io/DelayedLoadMetadata.cc @@ -0,0 +1,299 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "DelayedLoadMetadata.h" + +#include + +#ifdef OPENVDB_USE_BLOSC +#include + +namespace { + +inline size_t padMask(size_t bytes) +{ + return size_t(std::ceil(static_cast(bytes+1) / + sizeof(openvdb::io::DelayedLoadMetadata::MaskType))); +} + +inline size_t padCompressedSize(size_t bytes) +{ + return size_t(std::ceil(static_cast(bytes+1) / + sizeof(openvdb::io::DelayedLoadMetadata::CompressedSizeType))); +} + +} // namespace + +#endif + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +DelayedLoadMetadata::DelayedLoadMetadata(const DelayedLoadMetadata& other) + : Metadata() + , mMask(other.mMask) + , mCompressedSize(other.mCompressedSize) +{ +} + +Name DelayedLoadMetadata::typeName() const +{ + return DelayedLoadMetadata::staticTypeName(); +} + +Metadata::Ptr DelayedLoadMetadata::copy() const +{ + Metadata::Ptr metadata(new DelayedLoadMetadata()); + metadata->copy(*this); + return metadata; +} + +void DelayedLoadMetadata::copy(const Metadata& other) +{ + const DelayedLoadMetadata* t = dynamic_cast(&other); + if (t == nullptr) OPENVDB_THROW(TypeError, "Incompatible type during copy"); + mMask = t->mMask; + mCompressedSize = t->mCompressedSize; +} + +std::string DelayedLoadMetadata::str() const +{ + return ""; +} + +bool DelayedLoadMetadata::asBool() const +{ + return false; +} + +Index32 DelayedLoadMetadata::size() const +{ + if (mMask.empty() && mCompressedSize.empty()) return Index32(0); + + // count + size_t size = sizeof(Index32); + + { // mask + size += sizeof(Index32); + size_t compressedSize = compression::bloscCompressedSize( + reinterpret_cast(mMask.data()), mMask.size()*sizeof(MaskType)); + + if (compressedSize > 0) size += compressedSize; + else size += mMask.size()*sizeof(MaskType); + } + { // compressed size + size += sizeof(Index32); + if (!mCompressedSize.empty()) { + size_t compressedSize = compression::bloscCompressedSize( + reinterpret_cast(mCompressedSize.data()), mCompressedSize.size()*sizeof(CompressedSizeType)); + + if (compressedSize > 0) size += compressedSize; + else size += mCompressedSize.size()*sizeof(CompressedSizeType); + } + } + + return static_cast(size); +} + +void DelayedLoadMetadata::clear() +{ + mMask.clear(); + mCompressedSize.clear(); +} + +bool DelayedLoadMetadata::empty() const +{ + return mMask.empty() && mCompressedSize.empty(); +} + +void DelayedLoadMetadata::resizeMask(size_t size) +{ + mMask.resize(size); +} + +void DelayedLoadMetadata::resizeCompressedSize(size_t size) +{ + mCompressedSize.resize(size); +} + +DelayedLoadMetadata::MaskType DelayedLoadMetadata::getMask(size_t index) const +{ + assert(DelayedLoadMetadata::isRegisteredType()); + assert(index < mMask.size()); + return mMask[index]; +} + +void DelayedLoadMetadata::setMask(size_t index, const MaskType& value) +{ + assert(index < mMask.size()); + mMask[index] = value; +} + +DelayedLoadMetadata::CompressedSizeType DelayedLoadMetadata::getCompressedSize(size_t index) const +{ + assert(DelayedLoadMetadata::isRegisteredType()); + assert(index < mCompressedSize.size()); + return mCompressedSize[index]; +} + +void DelayedLoadMetadata::setCompressedSize(size_t index, const CompressedSizeType& value) +{ + assert(index < mCompressedSize.size()); + mCompressedSize[index] = value; +} + +void DelayedLoadMetadata::readValue(std::istream& is, Index32 numBytes) +{ + if (numBytes == 0) return; + + // initial header size + size_t total = sizeof(Index32); + + Index32 count = 0; + is.read(reinterpret_cast(&count), sizeof(Index32)); + total += sizeof(Index32); + + Index32 bytes = 0; + is.read(reinterpret_cast(&bytes), sizeof(Index32)); + total += sizeof(Index32); + + if (bytes > Index32(0)) { + std::unique_ptr compressedBuffer(new char[bytes]); + is.read(reinterpret_cast(compressedBuffer.get()), bytes); + + total += bytes; + +#ifdef OPENVDB_USE_BLOSC + // pad to include BLOSC_MAX_OVERHEAD + size_t uncompressedBytes = openvdb::compression::bloscUncompressedSize(compressedBuffer.get()); + const size_t paddedCount = padMask(uncompressedBytes + BLOSC_MAX_OVERHEAD); + + mMask.reserve(paddedCount); + mMask.resize(count); + + // resize should never modify capacity for smaller vector sizes + assert(mMask.capacity() >= paddedCount); + + compression::bloscDecompress(reinterpret_cast(mMask.data()), count*sizeof(MaskType), mMask.capacity()*sizeof(MaskType), compressedBuffer.get()); +#endif + } else { + mMask.resize(count); + is.read(reinterpret_cast(mMask.data()), count*sizeof(MaskType)); + total += count*sizeof(MaskType); + } + + is.read(reinterpret_cast(&bytes), sizeof(Index32)); + + if (bytes != std::numeric_limits::max()) { + if (bytes > Index32(0)) { + std::unique_ptr compressedBuffer(new char[bytes]); + is.read(reinterpret_cast(compressedBuffer.get()), bytes); + + total += size_t(bytes); + +#ifdef OPENVDB_USE_BLOSC + // pad to include BLOSC_MAX_OVERHEAD + size_t uncompressedBytes = openvdb::compression::bloscUncompressedSize(compressedBuffer.get()); + const size_t paddedCount = padCompressedSize(uncompressedBytes + BLOSC_MAX_OVERHEAD); + + mCompressedSize.reserve(paddedCount); + mCompressedSize.resize(count); + + // resize should never modify capacity for smaller vector sizes + assert(mCompressedSize.capacity() >= paddedCount); + + compression::bloscDecompress(reinterpret_cast(mCompressedSize.data()), count*sizeof(CompressedSizeType), mCompressedSize.capacity()*sizeof(CompressedSizeType), compressedBuffer.get()); +#endif + } else { + mCompressedSize.resize(count); + is.read(reinterpret_cast(mCompressedSize.data()), count*sizeof(CompressedSizeType)); + total += count*sizeof(CompressedSizeType); + } + } + + Index32 totalBytes = static_cast(total); + + if (totalBytes < numBytes) { + // Read and discard any unknown bytes at the end of the metadata for forwards-compatibility + // (without seeking, because the stream might not be seekable). + const size_t BUFFER_SIZE = 1024; + std::vector buffer(BUFFER_SIZE); + for (Index32 bytesRemaining = numBytes - totalBytes; bytesRemaining > 0; ) { + const Index32 bytesToSkip = std::min(bytesRemaining, BUFFER_SIZE); + is.read(&buffer[0], bytesToSkip); + bytesRemaining -= bytesToSkip; + } + } +} + +void DelayedLoadMetadata::writeValue(std::ostream& os) const +{ + // metadata has a limit of 2^32 bytes + assert(mMask.size() < std::numeric_limits::max()); + assert(mCompressedSize.size() < std::numeric_limits::max()); + + if (mMask.empty() && mCompressedSize.empty()) return; + + assert(mCompressedSize.empty() || (mMask.size() == mCompressedSize.size())); + + Index32 count = static_cast(mMask.size()); + os.write(reinterpret_cast(&count), sizeof(Index32)); + + const Index32 zeroSize(0); + const Index32 maxSize(std::numeric_limits::max()); + + { // mask buffer + size_t compressedBytes(0); + std::unique_ptr compressedBuffer; + if (compression::bloscCanCompress()) { + compressedBuffer = compression::bloscCompress( + reinterpret_cast(mMask.data()), + mMask.size()*sizeof(MaskType), compressedBytes, /*resize=*/false); + } + + if (compressedBuffer) { + assert(compressedBytes < std::numeric_limits::max()); + Index32 bytes(static_cast(compressedBytes)); + os.write(reinterpret_cast(&bytes), sizeof(Index32)); + os.write(reinterpret_cast(compressedBuffer.get()), compressedBytes); + } + else { + os.write(reinterpret_cast(&zeroSize), sizeof(Index32)); + os.write(reinterpret_cast(mMask.data()), + mMask.size()*sizeof(MaskType)); + } + } + + // compressed size buffer + + if (mCompressedSize.empty()) { + // write out maximum Index32 value to denote no compressed sizes stored + os.write(reinterpret_cast(&maxSize), sizeof(Index32)); + } else { + size_t compressedBytes(0); + std::unique_ptr compressedBuffer; + if (compression::bloscCanCompress()) { + compressedBuffer = compression::bloscCompress( + reinterpret_cast(mCompressedSize.data()), + mCompressedSize.size()*sizeof(CompressedSizeType), compressedBytes, /*resize=*/false); + } + + if (compressedBuffer) { + assert(compressedBytes < std::numeric_limits::max()); + Index32 bytes(static_cast(compressedBytes)); + os.write(reinterpret_cast(&bytes), sizeof(Index32)); + os.write(reinterpret_cast(compressedBuffer.get()), compressedBytes); + } + else { + os.write(reinterpret_cast(&zeroSize), sizeof(Index32)); + os.write(reinterpret_cast(mCompressedSize.data()), + mCompressedSize.size()*sizeof(CompressedSizeType)); + } + } +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/io/DelayedLoadMetadata.h b/openvdb/io/DelayedLoadMetadata.h new file mode 100644 index 00000000..b80c666d --- /dev/null +++ b/openvdb/io/DelayedLoadMetadata.h @@ -0,0 +1,102 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_DELAYED_LOAD_METADATA_HAS_BEEN_INCLUDED +#define OPENVDB_DELAYED_LOAD_METADATA_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +/// @brief Store a buffer of data that can be optionally used +/// during reading for faster delayed-load I/O performance +class OPENVDB_API DelayedLoadMetadata: public Metadata +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + using MaskType = int8_t; + using CompressedSizeType = int64_t; + + DelayedLoadMetadata() = default; + DelayedLoadMetadata(const DelayedLoadMetadata& other); + ~DelayedLoadMetadata() override = default; + + Name typeName() const override; + Metadata::Ptr copy() const override; + void copy(const Metadata&) override; + std::string str() const override; + bool asBool() const override; + Index32 size() const override; + + static Name staticTypeName() { return "__delayedload"; } + + static Metadata::Ptr createMetadata() + { + Metadata::Ptr ret(new DelayedLoadMetadata); + return ret; + } + + static void registerType() + { + Metadata::registerType(DelayedLoadMetadata::staticTypeName(), + DelayedLoadMetadata::createMetadata); + } + + static void unregisterType() + { + Metadata::unregisterType(DelayedLoadMetadata::staticTypeName()); + } + + static bool isRegisteredType() + { + return Metadata::isRegisteredType(DelayedLoadMetadata::staticTypeName()); + } + + /// @brief Delete the contents of the mask and compressed size arrays + void clear(); + /// @brief Return @c true if both arrays are empty + bool empty() const; + + /// @brief Resize the mask array + void resizeMask(size_t size); + /// @brief Resize the compressed size array + void resizeCompressedSize(size_t size); + + /// @brief Return the mask value for a specific index + /// @note throws if index is out-of-range or DelayedLoadMask not registered + MaskType getMask(size_t index) const; + /// @brief Set the mask value for a specific index + /// @note throws if index is out-of-range + void setMask(size_t index, const MaskType& value); + + /// @brief Return the compressed size value for a specific index + /// @note throws if index is out-of-range or DelayedLoadMask not registered + CompressedSizeType getCompressedSize(size_t index) const; + /// @brief Set the compressed size value for a specific index + /// @note throws if index is out-of-range + void setCompressedSize(size_t index, const CompressedSizeType& value); + +protected: + void readValue(std::istream&, Index32 numBytes) override; + void writeValue(std::ostream&) const override; + +private: + std::vector mMask; + std::vector mCompressedSize; +}; // class DelayedLoadMetadata + + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_DELAYED_LOAD_METADATA_HAS_BEEN_INCLUDED diff --git a/openvdb/io/File.cc b/openvdb/io/File.cc new file mode 100644 index 00000000..44b7b115 --- /dev/null +++ b/openvdb/io/File.cc @@ -0,0 +1,833 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file io/File.cc + +#include "File.h" + +#include "TempFile.h" +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#include +#include +#endif +#include +#include // for getenv(), strtoul() +#include // for strerror_r() +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +// Implementation details of the File class +struct File::Impl +{ + enum { DEFAULT_COPY_MAX_BYTES = 500000000 }; // 500 MB + + struct NoBBox {}; + + // Common implementation of the various File::readGrid() overloads, + // with and without bounding box clipping + template + static GridBase::Ptr readGrid(const File& file, const GridDescriptor& gd, const BoxType& bbox) + { + // This method should not be called for files that don't contain grid offsets. + assert(file.inputHasGridOffsets()); + + GridBase::Ptr grid = file.createGrid(gd); + gd.seekToGrid(file.inputStream()); + unarchive(file, grid, gd, bbox); + return grid; + } + + static void unarchive(const File& file, GridBase::Ptr& grid, + const GridDescriptor& gd, NoBBox) + { + file.Archive::readGrid(grid, gd, file.inputStream()); + } + + static void unarchive(const File& file, GridBase::Ptr& grid, + const GridDescriptor& gd, const CoordBBox& indexBBox) + { + file.Archive::readGrid(grid, gd, file.inputStream(), indexBBox); + } + + static void unarchive(const File& file, GridBase::Ptr& grid, + const GridDescriptor& gd, const BBoxd& worldBBox) + { + file.Archive::readGrid(grid, gd, file.inputStream(), worldBBox); + } + + static Index64 getDefaultCopyMaxBytes() + { + Index64 result = DEFAULT_COPY_MAX_BYTES; + if (const char* s = std::getenv("OPENVDB_DELAYED_LOAD_COPY_MAX_BYTES")) { + char* endptr = nullptr; + result = std::strtoul(s, &endptr, /*base=*/10); + } + return result; + } + + std::string mFilename; + // The file-level metadata + MetaMap::Ptr mMeta; + // The memory-mapped file + MappedFile::Ptr mFileMapping; + // The buffer for the input stream, if it is a memory-mapped file + SharedPtr mStreamBuf; + // The file stream that is open for reading + std::unique_ptr mInStream; + // File-level stream metadata (file format, compression, etc.) + StreamMetadata::Ptr mStreamMetadata; + // Flag indicating if we have read in the global information (header, + // metadata, and grid descriptors) for this VDB file + bool mIsOpen; + // File size limit for copying during delayed loading + Index64 mCopyMaxBytes; + // Grid descriptors for all grids stored in the file, indexed by grid name + NameMap mGridDescriptors; + // All grids, indexed by unique name (used only when mHasGridOffsets is false) + Archive::NamedGridMap mNamedGrids; + // All grids stored in the file (used only when mHasGridOffsets is false) + GridPtrVecPtr mGrids; +}; // class File::Impl + + +//////////////////////////////////////// + + +File::File(const std::string& filename): mImpl(new Impl) +{ + mImpl->mFilename = filename; + mImpl->mIsOpen = false; + mImpl->mCopyMaxBytes = Impl::getDefaultCopyMaxBytes(); + setInputHasGridOffsets(true); +} + + +File::~File() +{ +} + + +File::File(const File& other) + : Archive(other) + , mImpl(new Impl) +{ + *this = other; +} + + +File& +File::operator=(const File& other) +{ + if (&other != this) { + Archive::operator=(other); + const Impl& otherImpl = *other.mImpl; + mImpl->mFilename = otherImpl.mFilename; + mImpl->mMeta = otherImpl.mMeta; + mImpl->mIsOpen = false; // don't want two file objects reading from the same stream + mImpl->mCopyMaxBytes = otherImpl.mCopyMaxBytes; + mImpl->mGridDescriptors = otherImpl.mGridDescriptors; + mImpl->mNamedGrids = otherImpl.mNamedGrids; + mImpl->mGrids = otherImpl.mGrids; + } + return *this; +} + + +SharedPtr +File::copy() const +{ + return SharedPtr{new File{*this}}; +} + + +//////////////////////////////////////// + + +const std::string& +File::filename() const +{ + return mImpl->mFilename; +} + + +MetaMap::Ptr +File::fileMetadata() +{ + return mImpl->mMeta; +} + +MetaMap::ConstPtr +File::fileMetadata() const +{ + return mImpl->mMeta; +} + + +const File::NameMap& +File::gridDescriptors() const +{ + return mImpl->mGridDescriptors; +} + +File::NameMap& +File::gridDescriptors() +{ + return mImpl->mGridDescriptors; +} + + +std::istream& +File::inputStream() const +{ + if (!mImpl->mInStream) { + OPENVDB_THROW(IoError, filename() << " is not open for reading"); + } + return *mImpl->mInStream; +} + + +//////////////////////////////////////// + + +Index64 +File::getSize() const +{ + /// @internal boost::filesystem::file_size() would be a more portable alternative, + /// but as of 9/2014, Houdini ships without the Boost.Filesystem library, + /// which makes it much less convenient to use that library. + + Index64 result = std::numeric_limits::max(); + + std::string mesg = "could not get size of file " + filename(); + +#ifdef _MSC_VER + // Get the file size by seeking to the end of the file. + std::ifstream fstrm(filename()); + if (fstrm) { + fstrm.seekg(0, fstrm.end); + result = static_cast(fstrm.tellg()); + } else { + OPENVDB_THROW(IoError, mesg); + } +#else + // Get the file size using the stat() system call. + struct stat info; + if (0 != ::stat(filename().c_str(), &info)) { + std::string s = getErrorString(); + if (!s.empty()) mesg += " (" + s + ")"; + OPENVDB_THROW(IoError, mesg); + } + if (!S_ISREG(info.st_mode)) { + mesg += " (not a regular file)"; + OPENVDB_THROW(IoError, mesg); + } + result = static_cast(info.st_size); +#endif + + return result; +} + + +Index64 +File::copyMaxBytes() const +{ + return mImpl->mCopyMaxBytes; +} + + +void +File::setCopyMaxBytes(Index64 bytes) +{ + mImpl->mCopyMaxBytes = bytes; +} + + +//////////////////////////////////////// + + +bool +File::isOpen() const +{ + return mImpl->mIsOpen; +} + + +bool +File::open(bool delayLoad, const MappedFile::Notifier& notifier) +{ + if (isOpen()) { + OPENVDB_THROW(IoError, filename() << " is already open"); + } + mImpl->mInStream.reset(); + + // Open the file. + std::unique_ptr newStream; + SharedPtr newStreamBuf; + MappedFile::Ptr newFileMapping; + if (!delayLoad || !Archive::isDelayedLoadingEnabled()) { + newStream.reset(new std::ifstream( + filename().c_str(), std::ios_base::in | std::ios_base::binary)); + } else { + bool isTempFile = false; + std::string fname = filename(); + if (getSize() < copyMaxBytes()) { + // If the file is not too large, make a temporary private copy of it + // and open the copy instead. The original file can then be modified + // or removed without affecting delayed load. + try { + TempFile tempFile; + std::ifstream fstrm(filename().c_str(), + std::ios_base::in | std::ios_base::binary); + boost::iostreams::copy(fstrm, tempFile); + fname = tempFile.filename(); + isTempFile = true; + } catch (std::exception& e) { + std::string mesg; + if (e.what()) mesg = std::string(" (") + e.what() + ")"; + OPENVDB_LOG_WARN("failed to create a temporary copy of " << filename() + << " for delayed loading" << mesg + << "; will read directly from " << filename() << " instead"); + } + } + + // While the file is open, its mapping, stream buffer and stream + // must all be maintained. Once the file is closed, the buffer and + // the stream can be discarded, but the mapping needs to persist + // if any grids were lazily loaded. + try { + newFileMapping.reset(new MappedFile(fname, /*autoDelete=*/isTempFile)); + newStreamBuf = newFileMapping->createBuffer(); + newStream.reset(new std::istream(newStreamBuf.get())); + } catch (std::exception& e) { + std::ostringstream ostr; + ostr << "could not open file " << filename(); + if (e.what() != nullptr) ostr << " (" << e.what() << ")"; + OPENVDB_THROW(IoError, ostr.str()); + } + } + + if (newStream->fail()) { + OPENVDB_THROW(IoError, "could not open file " << filename()); + } + + // Read in the file header. + bool newFile = false; + try { + newFile = Archive::readHeader(*newStream); + } catch (IoError& e) { + if (e.what() && std::string("not a VDB file") == e.what()) { + // Rethrow, adding the filename. + OPENVDB_THROW(IoError, filename() << " is not a VDB file"); + } + throw; + } + + mImpl->mFileMapping = newFileMapping; + if (mImpl->mFileMapping) mImpl->mFileMapping->setNotifier(notifier); + mImpl->mStreamBuf = newStreamBuf; + mImpl->mInStream.swap(newStream); + + // Tag the input stream with the file format and library version numbers + // and other metadata. + mImpl->mStreamMetadata.reset(new StreamMetadata); + mImpl->mStreamMetadata->setSeekable(true); + io::setStreamMetadataPtr(inputStream(), mImpl->mStreamMetadata, /*transfer=*/false); + Archive::setFormatVersion(inputStream()); + Archive::setLibraryVersion(inputStream()); + Archive::setDataCompression(inputStream()); + io::setMappedFilePtr(inputStream(), mImpl->mFileMapping); + + // Read in the VDB metadata. + mImpl->mMeta = MetaMap::Ptr(new MetaMap); + mImpl->mMeta->readMeta(inputStream()); + + if (!inputHasGridOffsets()) { + OPENVDB_LOG_DEBUG_RUNTIME("file " << filename() << " does not support partial reading"); + + mImpl->mGrids.reset(new GridPtrVec); + mImpl->mNamedGrids.clear(); + + // Stream in the entire contents of the file and append all grids to mGrids. + const int32_t gridCount = readGridCount(inputStream()); + for (int32_t i = 0; i < gridCount; ++i) { + GridDescriptor gd; + gd.read(inputStream()); + + GridBase::Ptr grid = createGrid(gd); + Archive::readGrid(grid, gd, inputStream()); + + gridDescriptors().insert(std::make_pair(gd.gridName(), gd)); + mImpl->mGrids->push_back(grid); + mImpl->mNamedGrids[gd.uniqueName()] = grid; + } + // Connect instances (grids that share trees with other grids). + for (NameMapCIter it = gridDescriptors().begin(); it != gridDescriptors().end(); ++it) { + Archive::connectInstance(it->second, mImpl->mNamedGrids); + } + } else { + // Read in just the grid descriptors. + readGridDescriptors(inputStream()); + } + + mImpl->mIsOpen = true; + return newFile; // true if file is not identical to opened file +} + + +void +File::close() +{ + // Reset all data. + mImpl->mMeta.reset(); + mImpl->mGridDescriptors.clear(); + mImpl->mGrids.reset(); + mImpl->mNamedGrids.clear(); + mImpl->mInStream.reset(); + mImpl->mStreamBuf.reset(); + mImpl->mStreamMetadata.reset(); + mImpl->mFileMapping.reset(); + + mImpl->mIsOpen = false; + setInputHasGridOffsets(true); +} + + +//////////////////////////////////////// + + +bool +File::hasGrid(const Name& name) const +{ + if (!isOpen()) { + OPENVDB_THROW(IoError, filename() << " is not open for reading"); + } + return (findDescriptor(name) != gridDescriptors().end()); +} + + +MetaMap::Ptr +File::getMetadata() const +{ + if (!isOpen()) { + OPENVDB_THROW(IoError, filename() << " is not open for reading"); + } + // Return a deep copy of the file-level metadata, which was read + // when the file was opened. + return MetaMap::Ptr(new MetaMap(*mImpl->mMeta)); +} + + +GridPtrVecPtr +File::getGrids() const +{ + if (!isOpen()) { + OPENVDB_THROW(IoError, filename() << " is not open for reading"); + } + + GridPtrVecPtr ret; + if (!inputHasGridOffsets()) { + // If the input file doesn't have grid offsets, then all of the grids + // have already been streamed in and stored in mGrids. + ret = mImpl->mGrids; + } else { + ret.reset(new GridPtrVec); + + Archive::NamedGridMap namedGrids; + + // Read all grids represented by the GridDescriptors. + for (NameMapCIter i = gridDescriptors().begin(), e = gridDescriptors().end(); i != e; ++i) { + const GridDescriptor& gd = i->second; + GridBase::Ptr grid = readGrid(gd); + ret->push_back(grid); + namedGrids[gd.uniqueName()] = grid; + } + + // Connect instances (grids that share trees with other grids). + for (NameMapCIter i = gridDescriptors().begin(), e = gridDescriptors().end(); i != e; ++i) { + Archive::connectInstance(i->second, namedGrids); + } + } + return ret; +} + + +GridBase::Ptr +File::retrieveCachedGrid(const Name& name) const +{ + // If the file has grid offsets, grids are read on demand + // and not cached in mNamedGrids. + if (inputHasGridOffsets()) return GridBase::Ptr(); + + // If the file does not have grid offsets, mNamedGrids should already + // contain the entire contents of the file. + + // Search by unique name. + Archive::NamedGridMap::const_iterator it = + mImpl->mNamedGrids.find(GridDescriptor::stringAsUniqueName(name)); + // If not found, search by grid name. + if (it == mImpl->mNamedGrids.end()) it = mImpl->mNamedGrids.find(name); + if (it == mImpl->mNamedGrids.end()) { + OPENVDB_THROW(KeyError, filename() << " has no grid named \"" << name << "\""); + } + return it->second; +} + + +//////////////////////////////////////// + + +GridPtrVecPtr +File::readAllGridMetadata() +{ + if (!isOpen()) { + OPENVDB_THROW(IoError, filename() << " is not open for reading"); + } + + GridPtrVecPtr ret(new GridPtrVec); + + if (!inputHasGridOffsets()) { + // If the input file doesn't have grid offsets, then all of the grids + // have already been streamed in and stored in mGrids. + for (size_t i = 0, N = mImpl->mGrids->size(); i < N; ++i) { + // Return copies of the grids, but with empty trees. + ret->push_back((*mImpl->mGrids)[i]->copyGridWithNewTree()); + } + } else { + // Read just the metadata and transforms for all grids. + for (NameMapCIter i = gridDescriptors().begin(), e = gridDescriptors().end(); i != e; ++i) { + const GridDescriptor& gd = i->second; + GridBase::ConstPtr grid = readGridPartial(gd, /*readTopology=*/false); + // Return copies of the grids, but with empty trees. + // (As of 0.98.0, at least, it would suffice to just const cast + // the grid pointers returned by readGridPartial(), but shallow + // copying the grids helps to ensure future compatibility.) + ret->push_back(grid->copyGridWithNewTree()); + } + } + return ret; +} + + +GridBase::Ptr +File::readGridMetadata(const Name& name) +{ + if (!isOpen()) { + OPENVDB_THROW(IoError, filename() << " is not open for reading."); + } + + GridBase::ConstPtr ret; + if (!inputHasGridOffsets()) { + // Retrieve the grid from mGrids, which should already contain + // the entire contents of the file. + ret = readGrid(name); + } else { + NameMapCIter it = findDescriptor(name); + if (it == gridDescriptors().end()) { + OPENVDB_THROW(KeyError, filename() << " has no grid named \"" << name << "\""); + } + + // Seek to and read in the grid from the file. + const GridDescriptor& gd = it->second; + ret = readGridPartial(gd, /*readTopology=*/false); + } + return ret->copyGridWithNewTree(); +} + + +//////////////////////////////////////// + + +GridBase::Ptr +File::readGrid(const Name& name) +{ + return readGridByName(name, BBoxd()); +} + + +GridBase::Ptr +File::readGrid(const Name& name, const BBoxd& bbox) +{ + return readGridByName(name, bbox); +} + + +GridBase::Ptr +File::readGridByName(const Name& name, const BBoxd& bbox) +{ + if (!isOpen()) { + OPENVDB_THROW(IoError, filename() << " is not open for reading."); + } + + const bool clip = bbox.isSorted(); + + // If a grid with the given name was already read and cached + // (along with the entire contents of the file, because the file + // doesn't support random access), retrieve and return it. + GridBase::Ptr grid = retrieveCachedGrid(name); + if (grid) { + if (clip) { + grid = grid->deepCopyGrid(); + grid->clipGrid(bbox); + } + return grid; + } + + NameMapCIter it = findDescriptor(name); + if (it == gridDescriptors().end()) { + OPENVDB_THROW(KeyError, filename() << " has no grid named \"" << name << "\""); + } + + // Seek to and read in the grid from the file. + const GridDescriptor& gd = it->second; + grid = (clip ? readGrid(gd, bbox) : readGrid(gd)); + + if (gd.isInstance()) { + /// @todo Refactor to share code with Archive::connectInstance()? + NameMapCIter parentIt = + findDescriptor(GridDescriptor::nameAsString(gd.instanceParentName())); + if (parentIt == gridDescriptors().end()) { + OPENVDB_THROW(KeyError, "missing instance parent \"" + << GridDescriptor::nameAsString(gd.instanceParentName()) + << "\" for grid " << GridDescriptor::nameAsString(gd.uniqueName()) + << " in file " << filename()); + } + + GridBase::Ptr parent; + if (clip) { + const CoordBBox indexBBox = grid->constTransform().worldToIndexNodeCentered(bbox); + parent = readGrid(parentIt->second, indexBBox); + } else { + parent = readGrid(parentIt->second); + } + if (parent) grid->setTree(parent->baseTreePtr()); + } + return grid; +} + + +//////////////////////////////////////// + + +void +File::writeGrids(const GridCPtrVec& grids, const MetaMap& meta) const +{ + if (isOpen()) { + OPENVDB_THROW(IoError, + filename() << " cannot be written because it is open for reading"); + } + + // Create a file stream and write it out. + std::ofstream file; + file.open(filename().c_str(), + std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + + if (file.fail()) { + OPENVDB_THROW(IoError, "could not open " << filename() << " for writing"); + } + + // Write out the vdb. + Archive::write(file, grids, /*seekable=*/true, meta); + + file.close(); +} + + +//////////////////////////////////////// + + +void +File::readGridDescriptors(std::istream& is) +{ + // This method should not be called for files that don't contain grid offsets. + assert(inputHasGridOffsets()); + + gridDescriptors().clear(); + + for (int32_t i = 0, N = readGridCount(is); i < N; ++i) { + // Read the grid descriptor. + GridDescriptor gd; + gd.read(is); + + // Add the descriptor to the dictionary. + gridDescriptors().insert(std::make_pair(gd.gridName(), gd)); + + // Skip forward to the next descriptor. + gd.seekToEnd(is); + } +} + + +//////////////////////////////////////// + + +File::NameMapCIter +File::findDescriptor(const Name& name) const +{ + const Name uniqueName = GridDescriptor::stringAsUniqueName(name); + + // Find all descriptors with the given grid name. + std::pair range = gridDescriptors().equal_range(name); + + if (range.first == range.second) { + // If no descriptors were found with the given grid name, the name might have + // a suffix ("name[N]"). In that case, remove the "[N]" suffix and search again. + range = gridDescriptors().equal_range(GridDescriptor::stripSuffix(uniqueName)); + } + + const size_t count = size_t(std::distance(range.first, range.second)); + if (count > 1 && name == uniqueName) { + OPENVDB_LOG_WARN(filename() << " has more than one grid named \"" << name << "\""); + } + + NameMapCIter ret = gridDescriptors().end(); + + if (count > 0) { + if (name == uniqueName) { + // If the given grid name is unique or if no "[N]" index was given, + // use the first matching descriptor. + ret = range.first; + } else { + // If the given grid name has a "[N]" index, find the descriptor + // with a matching unique name. + for (NameMapCIter it = range.first; it != range.second; ++it) { + const Name candidateName = it->second.uniqueName(); + if (candidateName == uniqueName || candidateName == name) { + ret = it; + break; + } + } + } + } + return ret; +} + + +//////////////////////////////////////// + + +GridBase::Ptr +File::createGrid(const GridDescriptor& gd) const +{ + // Create the grid. + if (!GridBase::isRegistered(gd.gridType())) { + OPENVDB_THROW(KeyError, "Cannot read grid " + << GridDescriptor::nameAsString(gd.uniqueName()) + << " from " << filename() << ": grid type " + << gd.gridType() << " is not registered"); + } + + GridBase::Ptr grid = GridBase::createGrid(gd.gridType()); + if (grid) grid->setSaveFloatAsHalf(gd.saveFloatAsHalf()); + + return grid; +} + + +GridBase::ConstPtr +File::readGridPartial(const GridDescriptor& gd, bool readTopology) const +{ + // This method should not be called for files that don't contain grid offsets. + assert(inputHasGridOffsets()); + + GridBase::Ptr grid = createGrid(gd); + + // Seek to grid. + gd.seekToGrid(inputStream()); + + // Read the grid partially. + readGridPartial(grid, inputStream(), gd.isInstance(), readTopology); + + // Promote to a const grid. + GridBase::ConstPtr constGrid = grid; + + return constGrid; +} + + +GridBase::Ptr +File::readGrid(const GridDescriptor& gd) const +{ + return Impl::readGrid(*this, gd, Impl::NoBBox()); +} + + +GridBase::Ptr +File::readGrid(const GridDescriptor& gd, const BBoxd& bbox) const +{ + return Impl::readGrid(*this, gd, bbox); +} + + +GridBase::Ptr +File::readGrid(const GridDescriptor& gd, const CoordBBox& bbox) const +{ + return Impl::readGrid(*this, gd, bbox); +} + + +void +File::readGridPartial(GridBase::Ptr grid, std::istream& is, + bool isInstance, bool readTopology) const +{ + // This method should not be called for files that don't contain grid offsets. + assert(inputHasGridOffsets()); + + // This code needs to stay in sync with io::Archive::readGrid(), in terms of + // the order of operations. + readGridCompression(is); + grid->readMeta(is); + + // drop DelayedLoadMetadata from the grid as it is only useful for IO + if ((*grid)[GridBase::META_FILE_DELAYED_LOAD]) { + grid->removeMeta(GridBase::META_FILE_DELAYED_LOAD); + } + + if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_GRID_INSTANCING) { + grid->readTransform(is); + if (!isInstance && readTopology) { + grid->readTopology(is); + } + } else { + if (readTopology) { + grid->readTopology(is); + grid->readTransform(is); + } + } +} + + +//////////////////////////////////////// + + +File::NameIterator +File::beginName() const +{ + if (!isOpen()) { + OPENVDB_THROW(IoError, filename() << " is not open for reading"); + } + return File::NameIterator(gridDescriptors().begin()); +} + + +File::NameIterator +File::endName() const +{ + return File::NameIterator(gridDescriptors().end()); +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/io/File.h b/openvdb/io/File.h new file mode 100644 index 00000000..27b2abb7 --- /dev/null +++ b/openvdb/io/File.h @@ -0,0 +1,236 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file File.h + +#ifndef OPENVDB_IO_FILE_HAS_BEEN_INCLUDED +#define OPENVDB_IO_FILE_HAS_BEEN_INCLUDED + +#include +#include "io.h" // for MappedFile::Notifier +#include "Archive.h" +#include "GridDescriptor.h" +#include // for std::copy() +#include +#include // for std::back_inserter() +#include +#include +#include + + +class TestFile; +class TestStream; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +/// Grid archive associated with a file on disk +class OPENVDB_API File: public Archive +{ +public: + using NameMap = std::multimap; + using NameMapCIter = NameMap::const_iterator; + + explicit File(const std::string& filename); + ~File() override; + + /// @brief Copy constructor + /// @details The copy will be closed and will not reference the same + /// file descriptor as the original. + File(const File& other); + /// @brief Assignment + /// @details After assignment, this File will be closed and will not + /// reference the same file descriptor as the source File. + File& operator=(const File& other); + + /// @brief Return a copy of this archive. + /// @details The copy will be closed and will not reference the same + /// file descriptor as the original. + SharedPtr copy() const override; + + /// @brief Return the name of the file with which this archive is associated. + /// @details The file does not necessarily exist on disk yet. + const std::string& filename() const; + + /// @brief Open the file, read the file header and the file-level metadata, + /// and populate the grid descriptors, but do not load any grids into memory. + /// @details If @a delayLoad is true, map the file into memory and enable delayed loading + /// of grids, and if a notifier is provided, call it when the file gets unmapped. + /// @note Define the environment variable @c OPENVDB_DISABLE_DELAYED_LOAD to disable + /// delayed loading unconditionally. + /// @throw IoError if the file is not a valid VDB file. + /// @return @c true if the file's UUID has changed since it was last read. + /// @see setCopyMaxBytes + bool open(bool delayLoad = true, const MappedFile::Notifier& = MappedFile::Notifier()); + + /// Return @c true if the file has been opened for reading. + bool isOpen() const; + + /// Close the file once we are done reading from it. + void close(); + + /// @brief Return this file's current size on disk in bytes. + /// @throw IoError if the file size cannot be determined. + Index64 getSize() const; + + /// @brief Return the size in bytes above which this file will not be + /// automatically copied during delayed loading. + Index64 copyMaxBytes() const; + /// @brief If this file is opened with delayed loading enabled, make a private copy + /// of the file if its size in bytes is less than the specified value. + /// @details Making a private copy ensures that the file can't change on disk + /// before it has been fully read. + /// @warning If the file is larger than this size, it is the user's responsibility + /// to ensure that it does not change on disk before it has been fully read. + /// Undefined behavior and/or a crash might result otherwise. + /// @note Copying is enabled by default, but it can be disabled for individual files + /// by setting the maximum size to zero bytes. A default size limit can be specified + /// by setting the environment variable @c OPENVDB_DELAYED_LOAD_COPY_MAX_BYTES + /// to the desired number of bytes. + void setCopyMaxBytes(Index64 bytes); + + /// Return @c true if a grid of the given name exists in this file. + bool hasGrid(const Name&) const; + + /// Return (in a newly created MetaMap) the file-level metadata. + MetaMap::Ptr getMetadata() const; + + /// Read the entire contents of the file and return a list of grid pointers. + GridPtrVecPtr getGrids() const; + + /// @brief Read just the grid metadata and transforms from the file and return a list + /// of pointers to grids that are empty except for their metadata and transforms. + /// @throw IoError if this file is not open for reading. + GridPtrVecPtr readAllGridMetadata(); + + /// @brief Read a grid's metadata and transform only. + /// @return A pointer to a grid that is empty except for its metadata and transform. + /// @throw IoError if this file is not open for reading. + /// @throw KeyError if no grid with the given name exists in this file. + GridBase::Ptr readGridMetadata(const Name&); + + /// Read an entire grid, including all of its data blocks. + GridBase::Ptr readGrid(const Name&); + /// @brief Read a grid, including its data blocks, but only where it + /// intersects the given world-space bounding box. + GridBase::Ptr readGrid(const Name&, const BBoxd&); + + /// @todo GridPtrVec readAllGrids(const Name&) + + /// @brief Write the grids in the given container to the file whose name + /// was given in the constructor. + void write(const GridCPtrVec&, const MetaMap& = MetaMap()) const override; + + /// @brief Write the grids in the given container to the file whose name + /// was given in the constructor. + template + void write(const GridPtrContainerT&, const MetaMap& = MetaMap()) const; + + /// A const iterator that iterates over all names in the file. This is only + /// valid once the file has been opened. + class NameIterator + { + public: + NameIterator(const NameMapCIter& iter): mIter(iter) {} + NameIterator(const NameIterator&) = default; + ~NameIterator() {} + + NameIterator& operator++() { mIter++; return *this; } + + bool operator==(const NameIterator& iter) const { return mIter == iter.mIter; } + bool operator!=(const NameIterator& iter) const { return mIter != iter.mIter; } + + Name operator*() const { return this->gridName(); } + + Name gridName() const { return GridDescriptor::nameAsString(mIter->second.uniqueName()); } + + private: + NameMapCIter mIter; + }; + + /// @return a NameIterator to iterate over all grid names in the file. + NameIterator beginName() const; + + /// @return the ending iterator for all grid names in the file. + NameIterator endName() const; + +private: + /// Read in all grid descriptors that are stored in the given stream. + void readGridDescriptors(std::istream&); + + /// @brief Return an iterator to the descriptor for the grid with the given name. + /// If the name is non-unique, return an iterator to the first matching descriptor. + NameMapCIter findDescriptor(const Name&) const; + + /// Return a newly created, empty grid of the type specified by the given grid descriptor. + GridBase::Ptr createGrid(const GridDescriptor&) const; + + /// @brief Read a grid, including its data blocks, but only where it + /// intersects the given world-space bounding box. + GridBase::Ptr readGridByName(const Name&, const BBoxd&); + + /// Read in and return the partially-populated grid specified by the given grid descriptor. + GridBase::ConstPtr readGridPartial(const GridDescriptor&, bool readTopology) const; + + /// Read in and return the grid specified by the given grid descriptor. + GridBase::Ptr readGrid(const GridDescriptor&) const; + /// Read in and return the region of the grid specified by the given grid descriptor + /// that intersects the given world-space bounding box. + GridBase::Ptr readGrid(const GridDescriptor&, const BBoxd&) const; + /// Read in and return the region of the grid specified by the given grid descriptor + /// that intersects the given index-space bounding box. + GridBase::Ptr readGrid(const GridDescriptor&, const CoordBBox&) const; + + /// @brief Partially populate the given grid by reading its metadata and transform and, + /// if the grid is not an instance, its tree structure, but not the tree's leaf nodes. + void readGridPartial(GridBase::Ptr, std::istream&, bool isInstance, bool readTopology) const; + + /// @brief Retrieve a grid from @c mNamedGrids. Return a null pointer + /// if @c mNamedGrids was not populated (because this file is random-access). + /// @throw KeyError if no grid with the given name exists in this file. + GridBase::Ptr retrieveCachedGrid(const Name&) const; + + void writeGrids(const GridCPtrVec&, const MetaMap&) const; + + MetaMap::Ptr fileMetadata(); + MetaMap::ConstPtr fileMetadata() const; + + const NameMap& gridDescriptors() const; + NameMap& gridDescriptors(); + + std::istream& inputStream() const; + + friend class ::TestFile; + friend class ::TestStream; + + struct Impl; + std::unique_ptr mImpl; +}; + + +//////////////////////////////////////// + + +inline void +File::write(const GridCPtrVec& grids, const MetaMap& meta) const +{ + this->writeGrids(grids, meta); +} + + +template +inline void +File::write(const GridPtrContainerT& container, const MetaMap& meta) const +{ + GridCPtrVec grids; + std::copy(container.begin(), container.end(), std::back_inserter(grids)); + this->writeGrids(grids, meta); +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_FILE_HAS_BEEN_INCLUDED diff --git a/openvdb/io/GridDescriptor.cc b/openvdb/io/GridDescriptor.cc new file mode 100644 index 00000000..c4bd08a5 --- /dev/null +++ b/openvdb/io/GridDescriptor.cc @@ -0,0 +1,196 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "GridDescriptor.h" + +#include +#include // for boost::ends_with() +#include // for boost::erase_last() +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +namespace { + +// In order not to break backward compatibility with existing VDB files, +// grids stored using 16-bit half floats are flagged by adding the following +// suffix to the grid's type name on output. The suffix is removed on input +// and the grid's "save float as half" flag set accordingly. +const char* HALF_FLOAT_TYPENAME_SUFFIX = "_HalfFloat"; + +const char* SEP = "\x1e"; // ASCII "record separator" + +} + + +GridDescriptor::GridDescriptor(): + mSaveFloatAsHalf(false), + mGridPos(0), + mBlockPos(0), + mEndPos(0) +{ +} + +GridDescriptor::GridDescriptor(const Name &name, const Name &type, bool half): + mGridName(stripSuffix(name)), + mUniqueName(name), + mGridType(type), + mSaveFloatAsHalf(half), + mGridPos(0), + mBlockPos(0), + mEndPos(0) +{ +} + +GridDescriptor::~GridDescriptor() +{ +} + +void +GridDescriptor::writeHeader(std::ostream &os) const +{ + writeString(os, mUniqueName); + + Name gridType = mGridType; + if (mSaveFloatAsHalf) gridType += HALF_FLOAT_TYPENAME_SUFFIX; + writeString(os, gridType); + + writeString(os, mInstanceParentName); +} + +void +GridDescriptor::writeStreamPos(std::ostream &os) const +{ + os.write(reinterpret_cast(&mGridPos), sizeof(boost::int64_t)); + os.write(reinterpret_cast(&mBlockPos), sizeof(boost::int64_t)); + os.write(reinterpret_cast(&mEndPos), sizeof(boost::int64_t)); +} + +GridBase::Ptr +GridDescriptor::read(std::istream &is) +{ + // Read in the name. + mUniqueName = readString(is); + mGridName = stripSuffix(mUniqueName); + + // Read in the grid type. + mGridType = readString(is); + if (boost::ends_with(mGridType, HALF_FLOAT_TYPENAME_SUFFIX)) { + mSaveFloatAsHalf = true; + boost::erase_last(mGridType, HALF_FLOAT_TYPENAME_SUFFIX); + } + + if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_GRID_INSTANCING) { + mInstanceParentName = readString(is); + } + + // Create the grid of the type if it has been registered. + if (!GridBase::isRegistered(mGridType)) { + OPENVDB_THROW(LookupError, "Cannot read grid." << + " Grid type " << mGridType << " is not registered."); + } + // else + GridBase::Ptr grid = GridBase::createGrid(mGridType); + if (grid) grid->setSaveFloatAsHalf(mSaveFloatAsHalf); + + // Read in the offsets. + is.read(reinterpret_cast(&mGridPos), sizeof(boost::int64_t)); + is.read(reinterpret_cast(&mBlockPos), sizeof(boost::int64_t)); + is.read(reinterpret_cast(&mEndPos), sizeof(boost::int64_t)); + + return grid; +} + +void +GridDescriptor::seekToGrid(std::istream &is) const +{ + is.seekg(mGridPos, std::ios_base::beg); +} + +void +GridDescriptor::seekToBlocks(std::istream &is) const +{ + is.seekg(mBlockPos, std::ios_base::beg); +} + +void +GridDescriptor::seekToEnd(std::istream &is) const +{ + is.seekg(mEndPos, std::ios_base::beg); +} + + +void +GridDescriptor::seekToGrid(std::ostream &os) const +{ + os.seekp(mGridPos, std::ios_base::beg); +} + +void +GridDescriptor::seekToBlocks(std::ostream &os) const +{ + os.seekp(mBlockPos, std::ios_base::beg); +} + +void +GridDescriptor::seekToEnd(std::ostream &os) const +{ + os.seekp(mEndPos, std::ios_base::beg); +} + + +//////////////////////////////////////// + + +// static +Name +GridDescriptor::addSuffix(const Name& name, int n) +{ + std::ostringstream ostr; + ostr << name << SEP << n; + return ostr.str(); +} + + +// static +Name +GridDescriptor::stripSuffix(const Name& name) +{ + return name.substr(0, name.find(SEP)); +} + + +// static +std::string +GridDescriptor::nameAsString(const Name& name) +{ + std::string::size_type pos = name.find(SEP); + if (pos == std::string::npos) return name; + + return name.substr(0, pos) + "[" + name.substr(pos + 1) + "]"; +} + + +//static +Name +GridDescriptor::stringAsUniqueName(const std::string& s) +{ + Name ret = s; + if (!ret.empty() && *ret.rbegin() == ']') { // found trailing ']' + std::string::size_type pos = ret.find("["); + // Replace "[N]" with SEP "N". + if (pos != std::string::npos) { + ret.resize(ret.size() - 1); // drop trailing ']' + ret.replace(ret.find("["), 1, SEP); + } + } + return ret; +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/io/GridDescriptor.h b/openvdb/io/GridDescriptor.h new file mode 100644 index 00000000..93cd6285 --- /dev/null +++ b/openvdb/io/GridDescriptor.h @@ -0,0 +1,105 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_IO_GRIDDESCRIPTOR_HAS_BEEN_INCLUDED +#define OPENVDB_IO_GRIDDESCRIPTOR_HAS_BEEN_INCLUDED + +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +/// This structure stores useful information that describes a grid on disk. +/// It can be used to retrieve I/O information about the grid such as +/// offsets into the file where the grid is located, its type, etc. +class OPENVDB_API GridDescriptor +{ +public: + GridDescriptor(); + GridDescriptor(const Name& name, const Name& gridType, bool saveFloatAsHalf = false); + GridDescriptor(const GridDescriptor&) = default; + GridDescriptor& operator=(const GridDescriptor&) = default; + ~GridDescriptor(); + + const Name& gridType() const { return mGridType; } + const Name& gridName() const { return mGridName; } + const Name& uniqueName() const { return mUniqueName; } + + const Name& instanceParentName() const { return mInstanceParentName; } + void setInstanceParentName(const Name& name) { mInstanceParentName = name; } + bool isInstance() const { return !mInstanceParentName.empty(); } + + bool saveFloatAsHalf() const { return mSaveFloatAsHalf; } + + void setGridPos(int64_t pos) { mGridPos = pos; } + int64_t getGridPos() const { return mGridPos; } + + void setBlockPos(int64_t pos) { mBlockPos = pos; } + int64_t getBlockPos() const { return mBlockPos; } + + void setEndPos(int64_t pos) { mEndPos = pos; } + int64_t getEndPos() const { return mEndPos; } + + // These methods seek to the right position in the given stream. + void seekToGrid(std::istream&) const; + void seekToBlocks(std::istream&) const; + void seekToEnd(std::istream&) const; + + void seekToGrid(std::ostream&) const; + void seekToBlocks(std::ostream&) const; + void seekToEnd(std::ostream&) const; + + /// @brief Write out this descriptor's header information (all data except for + /// stream offsets). + void writeHeader(std::ostream&) const; + + /// @brief Since positions into the stream are known at a later time, they are + /// written out separately. + void writeStreamPos(std::ostream&) const; + + /// @brief Read a grid descriptor from the given stream. + /// @return an empty grid of the type specified by the grid descriptor. + GridBase::Ptr read(std::istream&); + + /// @brief Append the number @a n to the given name (separated by an ASCII + /// "record separator" character) and return the resulting name. + static Name addSuffix(const Name&, int n); + /// @brief Strip from the given name any suffix that is separated by an ASCII + /// "record separator" character and return the resulting name. + static Name stripSuffix(const Name&); + /// @brief Given a name with suffix N, return "name[N]", otherwise just return "name". + /// Use this to produce a human-readable string from a descriptor's unique name. + static std::string nameAsString(const Name&); + /// @brief Given a string of the form "name[N]", return "name" with the suffix N + /// separated by an ASCII "record separator" character). Otherwise just return + /// the string as is. + static Name stringAsUniqueName(const std::string&); + +private: + /// Name of the grid + Name mGridName; + /// Unique name for this descriptor + Name mUniqueName; + /// If nonempty, the name of another grid that shares this grid's tree + Name mInstanceParentName; + /// The type of the grid + Name mGridType; + /// Are floats quantized to 16 bits on disk? + bool mSaveFloatAsHalf; + /// Location in the stream where the grid data is stored + int64_t mGridPos; + /// Location in the stream where the grid blocks are stored + int64_t mBlockPos; + /// Location in the stream where the next grid descriptor begins + int64_t mEndPos; +}; + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_GRIDDESCRIPTOR_HAS_BEEN_INCLUDED diff --git a/openvdb/io/Queue.cc b/openvdb/io/Queue.cc new file mode 100644 index 00000000..706fbf3e --- /dev/null +++ b/openvdb/io/Queue.cc @@ -0,0 +1,313 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file Queue.cc +/// @author Peter Cucka + +#include "Queue.h" + +#include "File.h" +#include "Stream.h" +#include +#include +#include +#include +#include +#include +#include // for tbb::this_tbb_thread::sleep() +#include +#include // for std::max() +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +namespace { + +using Mutex = tbb::mutex; +using Lock = Mutex::scoped_lock; + + +// Abstract base class for queuable TBB tasks that adds a task completion callback +class Task: public tbb::task +{ +public: + Task(Queue::Id id): mId(id) {} + ~Task() override {} + + Queue::Id id() const { return mId; } + + void setNotifier(Queue::Notifier& notifier) { mNotify = notifier; } + +protected: + void notify(Queue::Status status) { if (mNotify) mNotify(this->id(), status); } + +private: + Queue::Id mId; + Queue::Notifier mNotify; +}; + + +// Queuable TBB task that writes one or more grids to a .vdb file or an output stream +class OutputTask: public Task +{ +public: + OutputTask(Queue::Id id, const GridCPtrVec& grids, const Archive& archive, + const MetaMap& metadata) + : Task(id) + , mGrids(grids) + , mArchive(archive.copy()) + , mMetadata(metadata) + {} + + tbb::task* execute() override + { + Queue::Status status = Queue::FAILED; + try { + mArchive->write(mGrids, mMetadata); + status = Queue::SUCCEEDED; + } catch (std::exception& e) { + if (const char* msg = e.what()) { + OPENVDB_LOG_ERROR(msg); + } + } catch (...) { + } + this->notify(status); + return nullptr; // no successor to this task + } + +private: + GridCPtrVec mGrids; + SharedPtr mArchive; + MetaMap mMetadata; +}; + +} // unnamed namespace + + +//////////////////////////////////////// + + +// Private implementation details of a Queue +struct Queue::Impl +{ + using NotifierMap = std::map; + /// @todo Provide more information than just "succeeded" or "failed"? + using StatusMap = tbb::concurrent_hash_map; + + + Impl() + : mTimeout(Queue::DEFAULT_TIMEOUT) + , mCapacity(Queue::DEFAULT_CAPACITY) + , mNextId(1) + , mNextNotifierId(1) + { + mNumTasks = 0; // note: must explicitly zero-initialize atomics + } + ~Impl() {} + + // Disallow copying of instances of this class. + Impl(const Impl&); + Impl& operator=(const Impl&); + + // This method might be called from multiple threads. + void setStatus(Queue::Id id, Queue::Status status) + { + StatusMap::accessor acc; + mStatus.insert(acc, id); + acc->second = status; + } + + // This method might be called from multiple threads. + void setStatusWithNotification(Queue::Id id, Queue::Status status) + { + const bool completed = (status == SUCCEEDED || status == FAILED); + + // Update the task's entry in the status map with the new status. + this->setStatus(id, status); + + // If the client registered any callbacks, call them now. + bool didNotify = false; + { + // tbb::concurrent_hash_map does not support concurrent iteration + // (i.e., iteration concurrent with insertion or deletion), + // so we use a mutex-protected STL map instead. But if a callback + // invokes a notifier method such as removeNotifier() on this queue, + // the result will be a deadlock. + /// @todo Is it worth trying to avoid such deadlocks? + Lock lock(mNotifierMutex); + if (!mNotifiers.empty()) { + didNotify = true; + for (NotifierMap::const_iterator it = mNotifiers.begin(); + it != mNotifiers.end(); ++it) + { + it->second(id, status); + } + } + } + // If the task completed and callbacks were called, remove + // the task's entry from the status map. + if (completed) { + if (didNotify) { + StatusMap::accessor acc; + if (mStatus.find(acc, id)) { + mStatus.erase(acc); + } + } + --mNumTasks; + } + } + + bool canEnqueue() const { return mNumTasks < Int64(mCapacity); } + + void enqueue(Task& task) + { + tbb::tick_count start = tbb::tick_count::now(); + while (!canEnqueue()) { + tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(0.5/*sec*/)); + if ((tbb::tick_count::now() - start).seconds() > double(mTimeout)) { + OPENVDB_THROW(RuntimeError, + "unable to queue I/O task; " << mTimeout << "-second time limit expired"); + } + } + Queue::Notifier notify = std::bind(&Impl::setStatusWithNotification, this, + std::placeholders::_1, std::placeholders::_2); + task.setNotifier(notify); + this->setStatus(task.id(), Queue::PENDING); + tbb::task::enqueue(task); + ++mNumTasks; + } + + Index32 mTimeout; + Index32 mCapacity; + tbb::atomic mNumTasks; + Index32 mNextId; + StatusMap mStatus; + NotifierMap mNotifiers; + Index32 mNextNotifierId; + Mutex mNotifierMutex; +}; + + +//////////////////////////////////////// + + +Queue::Queue(Index32 capacity): mImpl(new Impl) +{ + mImpl->mCapacity = capacity; +} + + +Queue::~Queue() +{ + // Wait for all queued tasks to complete (successfully or unsuccessfully). + /// @todo Allow the queue to be destroyed while there are uncompleted tasks + /// (e.g., by keeping a static registry of queues that also dispatches + /// or blocks notifications)? + while (mImpl->mNumTasks > 0) { + tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(0.5/*sec*/)); + } +} + + +//////////////////////////////////////// + + +bool Queue::empty() const { return (mImpl->mNumTasks == 0); } +Index32 Queue::size() const { return Index32(std::max(0, mImpl->mNumTasks)); } +Index32 Queue::capacity() const { return mImpl->mCapacity; } +void Queue::setCapacity(Index32 n) { mImpl->mCapacity = std::max(1, n); } + +/// @todo void Queue::setCapacity(Index64 bytes); + +/// @todo Provide a way to limit the number of tasks in flight +/// (e.g., by enqueueing tbb::tasks that pop Tasks off a concurrent_queue)? + +/// @todo Remove any tasks from the queue that are not currently executing. +//void clear() const; + +Index32 Queue::timeout() const { return mImpl->mTimeout; } +void Queue::setTimeout(Index32 sec) { mImpl->mTimeout = sec; } + + +//////////////////////////////////////// + + +Queue::Status +Queue::status(Id id) const +{ + Impl::StatusMap::const_accessor acc; + if (mImpl->mStatus.find(acc, id)) { + const Status status = acc->second; + if (status == SUCCEEDED || status == FAILED) { + mImpl->mStatus.erase(acc); + } + return status; + } + return UNKNOWN; +} + + +Queue::Id +Queue::addNotifier(Notifier notify) +{ + Lock lock(mImpl->mNotifierMutex); + Queue::Id id = mImpl->mNextNotifierId++; + mImpl->mNotifiers[id] = notify; + return id; +} + + +void +Queue::removeNotifier(Id id) +{ + Lock lock(mImpl->mNotifierMutex); + Impl::NotifierMap::iterator it = mImpl->mNotifiers.find(id); + if (it != mImpl->mNotifiers.end()) { + mImpl->mNotifiers.erase(it); + } +} + + +void +Queue::clearNotifiers() +{ + Lock lock(mImpl->mNotifierMutex); + mImpl->mNotifiers.clear(); +} + + +//////////////////////////////////////// + + +Queue::Id +Queue::writeGrid(GridBase::ConstPtr grid, const Archive& archive, const MetaMap& metadata) +{ + return writeGridVec(GridCPtrVec(1, grid), archive, metadata); +} + + +Queue::Id +Queue::writeGridVec(const GridCPtrVec& grids, const Archive& archive, const MetaMap& metadata) +{ + const Queue::Id taskId = mImpl->mNextId++; + // From the "GUI Thread" chapter in the TBB Design Patterns guide + OutputTask* task = + new(tbb::task::allocate_root()) OutputTask(taskId, grids, archive, metadata); + try { + mImpl->enqueue(*task); + } catch (openvdb::RuntimeError&) { + // Destroy the task if it could not be enqueued, then rethrow the exception. + tbb::task::destroy(*task); + throw; + } + return taskId; +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/io/Queue.h b/openvdb/io/Queue.h new file mode 100644 index 00000000..30257348 --- /dev/null +++ b/openvdb/io/Queue.h @@ -0,0 +1,247 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file Queue.h +/// @author Peter Cucka + +#ifndef OPENVDB_IO_QUEUE_HAS_BEEN_INCLUDED +#define OPENVDB_IO_QUEUE_HAS_BEEN_INCLUDED + +#include +#include +#include // for std::copy +#include +#include // for std::back_inserter +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +class Archive; + +/// @brief Queue for asynchronous output of grids to files or streams +/// +/// @warning The queue holds shared pointers to grids. It is not safe +/// to modify a grid that has been placed in the queue. Instead, +/// make a deep copy of the grid (Grid::deepCopy()). +/// +/// @par Example: +/// @code +/// #include +/// #include +/// #include +/// #include +/// +/// using openvdb::io::Queue; +/// +/// struct MyNotifier +/// { +/// // Use a concurrent container, because queue callback functions +/// // must be thread-safe. +/// using FilenameMap = tbb::concurrent_hash_map; +/// FilenameMap filenames; +/// +/// // Callback function that prints the status of a completed task. +/// void callback(Queue::Id id, Queue::Status status) +/// { +/// const bool ok = (status == Queue::SUCCEEDED); +/// FilenameMap::accessor acc; +/// if (filenames.find(acc, id)) { +/// std::cout << (ok ? "wrote " : "failed to write ") +/// << acc->second << std::endl; +/// filenames.erase(acc); +/// } +/// } +/// }; +/// +/// int main() +/// { +/// // Construct an object to receive notifications from the queue. +/// // The object's lifetime must exceed the queue's. +/// MyNotifier notifier; +/// +/// Queue queue; +/// +/// // Register the callback() method of the MyNotifier object +/// // to receive notifications of completed tasks. +/// queue.addNotifier(std::bind(&MyNotifier::callback, ¬ifier, +/// std::placeholders::_1, std::placeholders::_2)); +/// +/// // Queue grids for output (e.g., for each step of a simulation). +/// for (int step = 1; step <= 10; ++step) { +/// openvdb::FloatGrid::Ptr grid = ...; +/// +/// std::ostringstream os; +/// os << "mygrid." << step << ".vdb"; +/// const std::string filename = os.str(); +/// +/// Queue::Id id = queue.writeGrid(grid, openvdb::io::File(filename)); +/// +/// // Associate the filename with the ID of the queued task. +/// MyNotifier::FilenameMap::accessor acc; +/// notifier.filenames.insert(acc, id); +/// acc->second = filename; +/// } +/// } +/// @endcode +/// Output: +/// @code +/// wrote mygrid.1.vdb +/// wrote mygrid.2.vdb +/// wrote mygrid.4.vdb +/// wrote mygrid.3.vdb +/// ... +/// wrote mygrid.10.vdb +/// @endcode +/// Note that tasks do not necessarily complete in the order in which they were queued. +class OPENVDB_API Queue +{ +public: + /// Default maximum queue length (see setCapacity()) + static const Index32 DEFAULT_CAPACITY = 100; + /// @brief Default maximum time in seconds to wait to queue a task + /// when the queue is full (see setTimeout()) + static const Index32 DEFAULT_TIMEOUT = 120; // seconds + + /// ID number of a queued task or of a registered notification callback + using Id = Index32; + + /// Status of a queued task + enum Status { UNKNOWN, PENDING, SUCCEEDED, FAILED }; + + + /// Construct a queue with the given capacity. + explicit Queue(Index32 capacity = DEFAULT_CAPACITY); + /// Block until all queued tasks complete (successfully or unsuccessfully). + ~Queue(); + + /// @brief Return @c true if the queue is empty. + bool empty() const; + /// @brief Return the number of tasks currently in the queue. + Index32 size() const; + + /// @brief Return the maximum number of tasks allowed in the queue. + /// @details Once the queue has reached its maximum size, adding + /// a new task will block until an existing task has executed. + Index32 capacity() const; + /// Set the maximum number of tasks allowed in the queue. + void setCapacity(Index32); + + /// Return the maximum number of seconds to wait to queue a task when the queue is full. + Index32 timeout() const; + /// Set the maximum number of seconds to wait to queue a task when the queue is full. + void setTimeout(Index32 seconds = DEFAULT_TIMEOUT); + + /// @brief Return the status of the task with the given ID. + /// @note Querying the status of a task that has already completed + /// (whether successfully or not) removes the task from the status registry. + /// Subsequent queries of its status will return UNKNOWN. + Status status(Id) const; + + using Notifier = std::function; + /// @brief Register a function that will be called with a task's ID + /// and status when that task completes, whether successfully or not. + /// @return an ID that can be passed to removeNotifier() to deregister the function + /// @details When multiple notifiers are registered, they are called + /// in the order in which they were registered. + /// @warning Notifiers are called from worker threads, so they must be thread-safe + /// and their lifetimes must exceed that of the queue. They must also not call, + /// directly or indirectly, addNotifier(), removeNotifier() or clearNotifiers(), + /// as that can result in a deadlock. + Id addNotifier(Notifier); + /// Deregister the notifier with the given ID. + void removeNotifier(Id); + /// Deregister all notifiers. + void clearNotifiers(); + + /// @brief Queue a single grid for output to a file or stream. + /// @param grid the grid to be serialized + /// @param archive the io::File or io::Stream to which to output the grid + /// @param fileMetadata optional file-level metadata + /// @return an ID with which the status of the queued task can be queried + /// @throw RuntimeError if the task cannot be queued within the time limit + /// (see setTimeout()) because the queue is full + /// @par Example: + /// @code + /// openvdb::FloatGrid::Ptr grid = ...; + /// + /// openvdb::io::Queue queue; + /// + /// // Write the grid to the file mygrid.vdb. + /// queue.writeGrid(grid, openvdb::io::File("mygrid.vdb")); + /// + /// // Stream the grid to a binary string. + /// std::ostringstream ostr(std::ios_base::binary); + /// queue.writeGrid(grid, openvdb::io::Stream(ostr)); + /// @endcode + Id writeGrid(GridBase::ConstPtr grid, const Archive& archive, + const MetaMap& fileMetadata = MetaMap()); + + /// @brief Queue a container of grids for output to a file. + /// @param grids any iterable container of grid pointers + /// (e.g., a GridPtrVec or GridPtrSet) + /// @param archive the io::File or io::Stream to which to output the grids + /// @param fileMetadata optional file-level metadata + /// @return an ID with which the status of the queued task can be queried + /// @throw RuntimeError if the task cannot be queued within the time limit + /// (see setTimeout()) because the queue is full + /// @par Example: + /// @code + /// openvdb::FloatGrid::Ptr floatGrid = ...; + /// openvdb::BoolGrid::Ptr boolGrid = ...; + /// openvdb::GridPtrVec grids; + /// grids.push_back(floatGrid); + /// grids.push_back(boolGrid); + /// + /// openvdb::io::Queue queue; + /// + /// // Write the grids to the file mygrid.vdb. + /// queue.write(grids, openvdb::io::File("mygrid.vdb")); + /// + /// // Stream the grids to a (binary) string. + /// std::ostringstream ostr(std::ios_base::binary); + /// queue.write(grids, openvdb::io::Stream(ostr)); + /// @endcode + template + Id write(const GridPtrContainer& grids, const Archive& archive, + const MetaMap& fileMetadata = MetaMap()); + +private: + // Disallow copying of instances of this class. + Queue(const Queue&); + Queue& operator=(const Queue&); + + Id writeGridVec(const GridCPtrVec&, const Archive&, const MetaMap&); + + struct Impl; + std::unique_ptr mImpl; +}; // class Queue + + +template +inline Queue::Id +Queue::write(const GridPtrContainer& container, + const Archive& archive, const MetaMap& metadata) +{ + GridCPtrVec grids; + std::copy(container.begin(), container.end(), std::back_inserter(grids)); + return this->writeGridVec(grids, archive, metadata); +} + +// Specialization for vectors of const Grid pointers; no copying necessary +template<> +inline Queue::Id +Queue::write(const GridCPtrVec& grids, + const Archive& archive, const MetaMap& metadata) +{ + return this->writeGridVec(grids, archive, metadata); +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_QUEUE_HAS_BEEN_INCLUDED diff --git a/openvdb/io/Stream.cc b/openvdb/io/Stream.cc new file mode 100644 index 00000000..7dd66a8a --- /dev/null +++ b/openvdb/io/Stream.cc @@ -0,0 +1,238 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Stream.h" + +#include "File.h" ///< @todo refactor +#include "GridDescriptor.h" +#include "TempFile.h" +#include +#include +#include +#include // for remove() +#include // for std::bind() +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +struct Stream::Impl +{ + Impl(): mOutputStream{nullptr} {} + Impl(const Impl& other) { *this = other; } + Impl& operator=(const Impl& other) + { + if (&other != this) { + mMeta = other.mMeta; ///< @todo deep copy? + mGrids = other.mGrids; ///< @todo deep copy? + mOutputStream = other.mOutputStream; + mFile.reset(); + } + return *this; + } + + MetaMap::Ptr mMeta; + GridPtrVecPtr mGrids; + std::ostream* mOutputStream; + std::unique_ptr mFile; +}; + + +//////////////////////////////////////// + + +namespace { + +/// @todo Use MappedFile auto-deletion instead. +void +removeTempFile(const std::string expectedFilename, const std::string& filename) +{ + if (filename == expectedFilename) { + if (0 != std::remove(filename.c_str())) { + std::string mesg = getErrorString(); + if (!mesg.empty()) mesg = " (" + mesg + ")"; + OPENVDB_LOG_WARN("failed to remove temporary file " << filename << mesg); + } + } +} + +} + + +Stream::Stream(std::istream& is, bool delayLoad): mImpl(new Impl) +{ + if (!is) return; + + if (delayLoad && Archive::isDelayedLoadingEnabled()) { + // Copy the contents of the stream to a temporary private file + // and open the file instead. + std::unique_ptr tempFile; + try { + tempFile.reset(new TempFile); + } catch (std::exception& e) { + std::string mesg; + if (e.what()) mesg = std::string(" (") + e.what() + ")"; + OPENVDB_LOG_WARN("failed to create a temporary file for delayed loading" << mesg + << "; will read directly from the input stream instead"); + } + if (tempFile) { + boost::iostreams::copy(is, *tempFile); + const std::string& filename = tempFile->filename(); + mImpl->mFile.reset(new File(filename)); + mImpl->mFile->setCopyMaxBytes(0); // don't make a copy of the temporary file + /// @todo Need to pass auto-deletion flag to MappedFile. + mImpl->mFile->open(delayLoad, + std::bind(&removeTempFile, filename, std::placeholders::_1)); + } + } + + if (!mImpl->mFile) { + readHeader(is); + + // Tag the input stream with the library and file format version numbers + // and the compression options specified in the header. + StreamMetadata::Ptr streamMetadata(new StreamMetadata); + io::setStreamMetadataPtr(is, streamMetadata, /*transfer=*/false); + io::setVersion(is, libraryVersion(), fileVersion()); + io::setDataCompression(is, compression()); + + // Read in the VDB metadata. + mImpl->mMeta.reset(new MetaMap); + mImpl->mMeta->readMeta(is); + + // Read in the number of grids. + const int32_t gridCount = readGridCount(is); + + // Read in all grids and insert them into mGrids. + mImpl->mGrids.reset(new GridPtrVec); + std::vector descriptors; + descriptors.reserve(gridCount); + Archive::NamedGridMap namedGrids; + for (int32_t i = 0; i < gridCount; ++i) { + GridDescriptor gd; + gd.read(is); + descriptors.push_back(gd); + GridBase::Ptr grid = readGrid(gd, is); + mImpl->mGrids->push_back(grid); + namedGrids[gd.uniqueName()] = grid; + } + + // Connect instances (grids that share trees with other grids). + for (size_t i = 0, N = descriptors.size(); i < N; ++i) { + Archive::connectInstance(descriptors[i], namedGrids); + } + } +} + + +Stream::Stream(): mImpl(new Impl) +{ +} + + +Stream::Stream(std::ostream& os): mImpl(new Impl) +{ + mImpl->mOutputStream = &os; +} + + +Stream::~Stream() +{ +} + + +Stream::Stream(const Stream& other): Archive(other), mImpl(new Impl(*other.mImpl)) +{ +} + + +Stream& +Stream::operator=(const Stream& other) +{ + if (&other != this) { + mImpl.reset(new Impl(*other.mImpl)); + } + return *this; +} + + +SharedPtr +Stream::copy() const +{ + return SharedPtr(new Stream(*this)); +} + + +//////////////////////////////////////// + + +GridBase::Ptr +Stream::readGrid(const GridDescriptor& gd, std::istream& is) const +{ + GridBase::Ptr grid; + + if (!GridBase::isRegistered(gd.gridType())) { + OPENVDB_THROW(TypeError, "can't read grid \"" + << GridDescriptor::nameAsString(gd.uniqueName()) << + "\" from input stream because grid type " << gd.gridType() << " is unknown"); + } else { + grid = GridBase::createGrid(gd.gridType()); + if (grid) grid->setSaveFloatAsHalf(gd.saveFloatAsHalf()); + + Archive::readGrid(grid, gd, is); + } + return grid; +} + + +void +Stream::write(const GridCPtrVec& grids, const MetaMap& metadata) const +{ + if (mImpl->mOutputStream == nullptr) { + OPENVDB_THROW(ValueError, "no output stream was specified"); + } + this->writeGrids(*mImpl->mOutputStream, grids, metadata); +} + + +void +Stream::writeGrids(std::ostream& os, const GridCPtrVec& grids, const MetaMap& metadata) const +{ + Archive::write(os, grids, /*seekable=*/false, metadata); +} + + +//////////////////////////////////////// + + +MetaMap::Ptr +Stream::getMetadata() const +{ + MetaMap::Ptr result; + if (mImpl->mFile) { + result = mImpl->mFile->getMetadata(); + } else if (mImpl->mMeta) { + // Return a deep copy of the file-level metadata + // that was read when this object was constructed. + result.reset(new MetaMap(*mImpl->mMeta)); + } + return result; +} + + +GridPtrVecPtr +Stream::getGrids() +{ + if (mImpl->mFile) { + return mImpl->mFile->getGrids(); + } + return mImpl->mGrids; +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/io/Stream.h b/openvdb/io/Stream.h new file mode 100644 index 00000000..049bc43d --- /dev/null +++ b/openvdb/io/Stream.h @@ -0,0 +1,89 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_IO_STREAM_HAS_BEEN_INCLUDED +#define OPENVDB_IO_STREAM_HAS_BEEN_INCLUDED + +#include "Archive.h" +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +class GridDescriptor; + + +/// Grid archive associated with arbitrary input and output streams (not necessarily files) +class OPENVDB_API Stream: public Archive +{ +public: + /// @brief Read grids from an input stream. + /// @details If @a delayLoad is true, map the contents of the input stream + /// into memory and enable delayed loading of grids. + /// @note Define the environment variable @c OPENVDB_DISABLE_DELAYED_LOAD + /// to disable delayed loading unconditionally. + explicit Stream(std::istream&, bool delayLoad = true); + + /// Construct an archive for stream output. + Stream(); + /// Construct an archive for output to the given stream. + explicit Stream(std::ostream&); + + Stream(const Stream&); + Stream& operator=(const Stream&); + + ~Stream() override; + + /// @brief Return a copy of this archive. + Archive::Ptr copy() const override; + + /// Return the file-level metadata in a newly created MetaMap. + MetaMap::Ptr getMetadata() const; + + /// Return pointers to the grids that were read from the input stream. + GridPtrVecPtr getGrids(); + + /// @brief Write the grids in the given container to this archive's output stream. + /// @throw ValueError if this archive was constructed without specifying an output stream. + void write(const GridCPtrVec&, const MetaMap& = MetaMap()) const override; + + /// @brief Write the grids in the given container to this archive's output stream. + /// @throw ValueError if this archive was constructed without specifying an output stream. + template + void write(const GridPtrContainerT&, const MetaMap& = MetaMap()) const; + +private: + /// Create a new grid of the type specified by the given descriptor, + /// then populate the grid from the given input stream. + /// @return the newly created grid. + GridBase::Ptr readGrid(const GridDescriptor&, std::istream&) const; + + void writeGrids(std::ostream&, const GridCPtrVec&, const MetaMap&) const; + + + struct Impl; + std::unique_ptr mImpl; +}; + + +//////////////////////////////////////// + + +template +inline void +Stream::write(const GridPtrContainerT& container, const MetaMap& metadata) const +{ + GridCPtrVec grids; + std::copy(container.begin(), container.end(), std::back_inserter(grids)); + this->write(grids, metadata); +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_STREAM_HAS_BEEN_INCLUDED diff --git a/openvdb/io/TempFile.cc b/openvdb/io/TempFile.cc new file mode 100644 index 00000000..878fba11 --- /dev/null +++ b/openvdb/io/TempFile.cc @@ -0,0 +1,136 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file TempFile.cc + +#include "TempFile.h" + +#include +#ifndef _MSC_VER +#include +#include +#include // for std::getenv(), mkstemp() +#include // for mode_t +#include // for mkdir(), umask() +#include // for access() +#else +#include // for std::filebuf +#endif +#include // for std::tmpnam(), L_tmpnam, P_tmpdir +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +struct TempFile::TempFileImpl +{ + const std::string& filename() const { return mPath; } + + bool is_open() const { return mBuffer.is_open(); } + + /// @internal boost::filesystem::unique_path(), etc. might be useful here, + /// but as of 9/2014, Houdini ships without the Boost.Filesystem library, + /// which makes it much less convenient to use that library. +#ifndef _MSC_VER + TempFileImpl(std::ostream& os): mFileDescr(-1) { this->init(os); } + + void init(std::ostream& os) + { + std::string fn = this->getTempDir() + "/openvdb_temp_XXXXXX"; + std::vector fnbuf(fn.begin(), fn.end()); + fnbuf.push_back(char(0)); + + //const mode_t savedMode = ::umask(~(S_IRUSR | S_IWUSR)); + mFileDescr = ::mkstemp(&fnbuf[0]); + //::umask(savedMode); + if (mFileDescr < 0) { + OPENVDB_THROW(IoError, "failed to generate temporary file"); + } + + mPath.assign(&fnbuf[0]); + + mDevice = DeviceType(mFileDescr, boost::iostreams::never_close_handle); + mBuffer.open(mDevice); + os.rdbuf(&mBuffer); + + if (!os.good()) { + OPENVDB_THROW(IoError, "failed to open temporary file " + mPath); + } + } + + void close() { mBuffer.close(); if (mFileDescr >= 0) ::close(mFileDescr); } + + static std::string getTempDir() + { + if (const char* dir = std::getenv("OPENVDB_TEMP_DIR")) { + if (0 != ::access(dir, F_OK)) { + ::mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR); + if (0 != ::access(dir, F_OK)) { + OPENVDB_THROW(IoError, + "failed to create OPENVDB_TEMP_DIR (" + std::string(dir) + ")"); + } + } + return dir; + } + if (const char* dir = std::getenv("TMPDIR")) return dir; + return P_tmpdir; + } + + using DeviceType = boost::iostreams::file_descriptor_sink; + using BufferType = boost::iostreams::stream_buffer; + + std::string mPath; + DeviceType mDevice; + BufferType mBuffer; + int mFileDescr; +#else // _MSC_VER + // Use only standard library routines; no POSIX. + + TempFileImpl(std::ostream& os) { this->init(os); } + + void init(std::ostream& os) + { + char fnbuf[L_tmpnam]; + const char* filename = std::tmpnam(fnbuf); + if (!filename) { + OPENVDB_THROW(IoError, "failed to generate name for temporary file"); + } + /// @todo This is not safe, since another process could open a file + /// with this name before we do. Unfortunately, there is no safe, + /// portable way to create a temporary file. + mPath = filename; + + const std::ios_base::openmode mode = (std::ios_base::out | std::ios_base::binary); + os.rdbuf(mBuffer.open(mPath.c_str(), mode)); + if (!os.good()) { + OPENVDB_THROW(IoError, "failed to open temporary file " + mPath); + } + } + + void close() { mBuffer.close(); } + + std::string mPath; + std::filebuf mBuffer; +#endif // _MSC_VER + +private: + TempFileImpl(const TempFileImpl&); // disable copying + TempFileImpl& operator=(const TempFileImpl&); // disable assignment +}; + + +TempFile::TempFile(): std::ostream(nullptr), mImpl(new TempFileImpl(*this)) {} +TempFile::~TempFile() { this->close(); } +const std::string& TempFile::filename() const { return mImpl->filename(); } +bool TempFile::is_open() const { return mImpl->is_open(); } +void TempFile::close() { mImpl->close(); } + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/io/TempFile.h b/openvdb/io/TempFile.h new file mode 100644 index 00000000..6df21d03 --- /dev/null +++ b/openvdb/io/TempFile.h @@ -0,0 +1,50 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file TempFile.h + +#ifndef OPENVDB_IO_TEMPFILE_HAS_BEEN_INCLUDED +#define OPENVDB_IO_TEMPFILE_HAS_BEEN_INCLUDED + +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +/// Output stream to a unique temporary file +class OPENVDB_API TempFile: public std::ostream +{ +public: + /// @brief Create and open a unique file. + /// @details On UNIX systems, the file is created in the directory specified by + /// the environment variable @c OPENVDB_TEMP_DIR, if that variable is defined, + /// or else in the directory specified by @c TMPDIR, if that variable is defined. + /// Otherwise (and on non-UNIX systems), the file is created in the system default + /// temporary directory. + TempFile(); + ~TempFile(); + + /// Return the path to the temporary file. + const std::string& filename() const; + + /// Return @c true if the file is open for writing. + bool is_open() const; + + /// Close the file. + void close(); + +private: + struct TempFileImpl; + std::unique_ptr mImpl; +}; + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_TEMPFILE_HAS_BEEN_INCLUDED diff --git a/openvdb/io/io.h b/openvdb/io/io.h new file mode 100644 index 00000000..682429bb --- /dev/null +++ b/openvdb/io/io.h @@ -0,0 +1,274 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_IO_IO_HAS_BEEN_INCLUDED +#define OPENVDB_IO_IO_HAS_BEEN_INCLUDED + +#include +#include // for SharedPtr +#include +#include +#include +#include // for std::ios_base +#include +#include +#include + +class TestMappedFile; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +class MetaMap; + +namespace io { + +/// @brief Container for metadata describing how to unserialize grids from and/or +/// serialize grids to a stream (which file format, compression scheme, etc. to use) +/// @details This class is mainly for internal use. +class OPENVDB_API StreamMetadata +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + StreamMetadata(); + StreamMetadata(const StreamMetadata&); + explicit StreamMetadata(std::ios_base&); + ~StreamMetadata(); + + StreamMetadata& operator=(const StreamMetadata&); + + /// @brief Transfer metadata items directly to the given stream. + /// @todo Deprecate direct transfer; use StreamMetadata structs everywhere. + void transferTo(std::ios_base&) const; + + uint32_t fileVersion() const; + void setFileVersion(uint32_t); + + VersionId libraryVersion() const; + void setLibraryVersion(VersionId); + + uint32_t compression() const; + void setCompression(uint32_t); + + uint32_t gridClass() const; + void setGridClass(uint32_t); + + const void* backgroundPtr() const; + void setBackgroundPtr(const void*); + + bool halfFloat() const; + void setHalfFloat(bool); + + bool writeGridStats() const; + void setWriteGridStats(bool); + + bool seekable() const; + void setSeekable(bool); + + bool delayedLoadMeta() const; + + bool countingPasses() const; + void setCountingPasses(bool); + + uint32_t pass() const; + void setPass(uint32_t); + + uint64_t leaf() const; + void setLeaf(uint64_t); + + //@{ + /// @brief Return a (reference to a) copy of the metadata of the grid + /// currently being read or written. + /// @details Some grid metadata might duplicate information returned by + /// gridClass(), backgroundPtr() and other accessors, but those values + /// are not guaranteed to be kept in sync. + MetaMap& gridMetadata(); + const MetaMap& gridMetadata() const; + //@} + + using AuxDataMap = std::map; + //@{ + /// @brief Return a map that can be populated with arbitrary user data. + AuxDataMap& auxData(); + const AuxDataMap& auxData() const; + //@} + + /// @private + uint32_t __test() const; + /// @private + void __setTest(uint32_t); + + /// Return a string describing this stream metadata. + std::string str() const; + +private: + struct Impl; + std::unique_ptr mImpl; +}; // class StreamMetadata + + +/// Write a description of the given metadata to an output stream. +std::ostream& operator<<(std::ostream&, const StreamMetadata&); + +std::ostream& operator<<(std::ostream&, const StreamMetadata::AuxDataMap&); + + +//////////////////////////////////////// + + +/// @brief Leaf nodes that require multi-pass I/O must inherit from this struct. +/// @sa Grid::hasMultiPassIO() +struct MultiPass {}; + + +//////////////////////////////////////// + + +class File; + +/// @brief Handle to control the lifetime of a memory-mapped .vdb file +class OPENVDB_API MappedFile +{ +public: + using Ptr = SharedPtr; + + ~MappedFile(); + MappedFile(const MappedFile&) = delete; // not copyable + MappedFile& operator=(const MappedFile&) = delete; + + /// Return the filename of the mapped file. + std::string filename() const; + + /// @brief Return a new stream buffer for the mapped file. + /// @details Typical usage is + /// @code + /// openvdb::io::MappedFile::Ptr mappedFile = ...; + /// auto buf = mappedFile->createBuffer(); + /// std::istream istrm{buf.get()}; + /// // Read from istrm... + /// @endcode + /// The buffer must persist as long as the stream is open. + SharedPtr createBuffer() const; + + using Notifier = std::function; + /// @brief Register a function that will be called with this file's name + /// when the file is unmapped. + void setNotifier(const Notifier&); + /// Deregister the notifier. + void clearNotifier(); + +private: + friend class File; + friend class ::TestMappedFile; + + explicit MappedFile(const std::string& filename, bool autoDelete = false); + + class Impl; + std::unique_ptr mImpl; +}; // class MappedFile + + +//////////////////////////////////////// + + +/// Return a string (possibly empty) describing the given system error code. +std::string getErrorString(int errorNum); + + +/// Return a string (possibly empty) describing the most recent system error. +std::string getErrorString(); + + +//////////////////////////////////////// + + +/// @brief Return the file format version number associated with the given input stream. +/// @sa File::setFormatVersion() +OPENVDB_API uint32_t getFormatVersion(std::ios_base&); + +/// @brief Return the (major, minor) library version number associated with the given input stream. +/// @sa File::setLibraryVersion() +OPENVDB_API VersionId getLibraryVersion(std::ios_base&); + +/// @brief Return a string of the form "./", giving the library +/// and file format version numbers associated with the given input stream. +OPENVDB_API std::string getVersion(std::ios_base&); + +/// Associate the current file format and library version numbers with the given input stream. +OPENVDB_API void setCurrentVersion(std::istream&); + +/// @brief Associate specific file format and library version numbers with the given stream. +/// @details This is typically called immediately after reading a header that contains +/// the version numbers. Data read subsequently can then be interpreted appropriately. +OPENVDB_API void setVersion(std::ios_base&, const VersionId& libraryVersion, uint32_t fileVersion); + +/// @brief Return a bitwise OR of compression option flags (COMPRESS_ZIP, +/// COMPRESS_ACTIVE_MASK, etc.) specifying whether and how input data is compressed +/// or output data should be compressed. +OPENVDB_API uint32_t getDataCompression(std::ios_base&); +/// @brief Associate with the given stream a bitwise OR of compression option flags +/// (COMPRESS_ZIP, COMPRESS_ACTIVE_MASK, etc.) specifying whether and how input data +/// is compressed or output data should be compressed. +OPENVDB_API void setDataCompression(std::ios_base&, uint32_t compressionFlags); + +/// @brief Return the class (GRID_LEVEL_SET, GRID_UNKNOWN, etc.) of the grid +/// currently being read from or written to the given stream. +OPENVDB_API uint32_t getGridClass(std::ios_base&); +/// @brief Associate with the given stream the class (GRID_LEVEL_SET, GRID_UNKNOWN, etc.) +/// of the grid currently being read or written. +OPENVDB_API void setGridClass(std::ios_base&, uint32_t); + +/// @brief Return true if floating-point values should be quantized to 16 bits when writing +/// to the given stream or promoted back from 16-bit to full precision when reading from it. +OPENVDB_API bool getHalfFloat(std::ios_base&); +/// @brief Specify whether floating-point values should be quantized to 16 bits when writing +/// to the given stream or promoted back from 16-bit to full precision when reading from it. +OPENVDB_API void setHalfFloat(std::ios_base&, bool); + +/// @brief Return a pointer to the background value of the grid +/// currently being read from or written to the given stream. +OPENVDB_API const void* getGridBackgroundValuePtr(std::ios_base&); +/// @brief Specify (a pointer to) the background value of the grid +/// currently being read from or written to the given stream. +/// @note The pointer must remain valid until the entire grid has been read or written. +OPENVDB_API void setGridBackgroundValuePtr(std::ios_base&, const void* background); + +/// @brief Return @c true if grid statistics (active voxel count and bounding box, etc.) +/// should be computed and stored as grid metadata when writing to the given stream. +OPENVDB_API bool getWriteGridStatsMetadata(std::ios_base&); +/// @brief Specify whether to compute grid statistics (active voxel count and bounding box, etc.) +/// and store them as grid metadata when writing to the given stream. +OPENVDB_API void setWriteGridStatsMetadata(std::ios_base&, bool writeGridStats); + +/// @brief Return a shared pointer to the memory-mapped file with which the given stream +/// is associated, or a null pointer if the stream is not associated with a memory-mapped file. +OPENVDB_API SharedPtr getMappedFilePtr(std::ios_base&); +/// @brief Associate the given stream with (a shared pointer to) a memory-mapped file. +/// @note The shared pointer object (not just the io::MappedFile object to which it points) +/// must remain valid until the file is closed. +OPENVDB_API void setMappedFilePtr(std::ios_base&, SharedPtr&); + +/// @brief Return a shared pointer to an object that stores metadata (file format, +/// compression scheme, etc.) for use when reading from or writing to the given stream. +OPENVDB_API SharedPtr getStreamMetadataPtr(std::ios_base&); +/// @brief Associate the given stream with (a shared pointer to) an object that stores +/// metadata (file format, compression scheme, etc.) for use when reading from +/// or writing to the stream. +/// @details If @a transfer is true, copy metadata from the object directly to the stream +/// (for backward compatibility with older versions of the library). +/// @note The shared pointer object (not just the io::StreamMetadata object to which it points) +/// must remain valid until the file is closed. +OPENVDB_API void setStreamMetadataPtr(std::ios_base&, + SharedPtr&, bool transfer = true); +/// @brief Dissociate the given stream from its metadata object (if it has one) +/// and return a shared pointer to the object. +OPENVDB_API SharedPtr clearStreamMetadataPtr(std::ios_base&); + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_IO_HAS_BEEN_INCLUDED diff --git a/openvdb/lib/libopenvdb.7.0.0.dylib b/openvdb/lib/libopenvdb.7.0.0.dylib new file mode 100644 index 0000000000000000000000000000000000000000..ef1a60c2352511f63cd4d9c2cd708d27c2bf10f3 GIT binary patch literal 3765576 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 z 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� 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

J7sM^uQm-IdbqRq6Qs8*Zv^>KDyl@6PHR2&_r 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 literal 0 HcmV?d00001 diff --git a/openvdb/lib/libopenvdb.7.0.dylib b/openvdb/lib/libopenvdb.7.0.dylib new file mode 120000 index 00000000..d5938a34 --- /dev/null +++ b/openvdb/lib/libopenvdb.7.0.dylib @@ -0,0 +1 @@ +libopenvdb.7.0.0.dylib \ No newline at end of file diff --git a/openvdb/lib/libopenvdb.dylib b/openvdb/lib/libopenvdb.dylib new file mode 120000 index 00000000..d5938a34 --- /dev/null +++ b/openvdb/lib/libopenvdb.dylib @@ -0,0 +1 @@ +libopenvdb.7.0.0.dylib \ No newline at end of file diff --git a/openvdb/math/BBox.h b/openvdb/math/BBox.h new file mode 100644 index 00000000..b2e8d70e --- /dev/null +++ b/openvdb/math/BBox.h @@ -0,0 +1,423 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_BBOX_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_BBOX_HAS_BEEN_INCLUDED + +#include "Math.h" // for math::isApproxEqual() and math::Tolerance() +#include "Vec3.h" +#include // for std::min(), std::max() +#include // for std::abs() +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief Axis-aligned bounding box +template +class BBox +{ +public: + using Vec3Type = Vec3T; + using ValueType = Vec3T; + using VectorType = Vec3T; + using ElementType = typename Vec3Type::ValueType; + + /// @brief The default constructor creates an invalid bounding box. + BBox(); + /// @brief Construct a bounding box that exactly encloses the given + /// minimum and maximum points. + BBox(const Vec3T& xyzMin, const Vec3T& xyzMax); + /// @brief Construct a bounding box that exactly encloses the given + /// minimum and maximum points. + /// @details If @a sorted is false, sort the points by their + /// @e x, @e y and @e z components. + BBox(const Vec3T& xyzMin, const Vec3T& xyzMax, bool sorted); + /// @brief Contruct a cubical bounding box from a minimum coordinate + /// and an edge length. + /// @note Inclusive for integral ElementTypes + BBox(const Vec3T& xyzMin, const ElementType& length); + + /// @brief Construct a bounding box that exactly encloses two points, + /// whose coordinates are given by an array of six values, + /// x1, y1, z1, + /// x2, y2 and z2. + /// @details If @a sorted is false, sort the points by their + /// @e x, @e y and @e z components. + explicit BBox(const ElementType* xyz, bool sorted = true); + + BBox(const BBox&) = default; + BBox& operator=(const BBox&) = default; + + /// @brief Sort the mininum and maximum points of this bounding box + /// by their @e x, @e y and @e z components. + void sort(); + + /// @brief Return a const reference to the minimum point of this bounding box. + const Vec3T& min() const { return mMin; } + /// @brief Return a const reference to the maximum point of this bounding box. + const Vec3T& max() const { return mMax; } + /// @brief Return a non-const reference to the minimum point of this bounding box. + Vec3T& min() { return mMin; } + /// @brief Return a non-const reference to the maximum point of this bounding box. + Vec3T& max() { return mMax; } + + /// @brief Return @c true if this bounding box is identical to the given bounding box. + bool operator==(const BBox& rhs) const; + /// @brief Return @c true if this bounding box differs from the given bounding box. + bool operator!=(const BBox& rhs) const { return !(*this == rhs); } + + /// @brief Return @c true if this bounding box is empty, i.e., it has no (positive) volume. + bool empty() const; + /// @brief Return @c true if this bounding box has (positive) volume. + bool hasVolume() const { return !this->empty(); } + /// @brief Return @c true if this bounding box has (positive) volume. + operator bool() const { return !this->empty(); } + + /// @brief Return @c true if all components of the minimum point are less than + /// or equal to the corresponding components of the maximum point. + /// @details This is equivalent to testing whether this bounding box has nonnegative volume. + /// @note For floating-point ElementTypes a tolerance is used for this test. + bool isSorted() const; + + /// @brief Return the center point of this bounding box. + Vec3d getCenter() const; + + /// @brief Return the extents of this bounding box, i.e., the length along each axis. + /// @note Inclusive for integral ElementTypes + Vec3T extents() const; + /// @brief Return the index (0, 1 or 2) of the longest axis. + size_t maxExtent() const { return MaxIndex(mMax - mMin); } + /// @brief Return the index (0, 1 or 2) of the shortest axis. + size_t minExtent() const { return MinIndex(mMax - mMin); } + + /// @brief Return the volume enclosed by this bounding box. + ElementType volume() const { Vec3T e = this->extents(); return e[0] * e[1] * e[2]; } + + /// @brief Return @c true if the given point is inside this bounding box. + bool isInside(const Vec3T& xyz) const; + /// @brief Return @c true if the given bounding box is inside this bounding box. + bool isInside(const BBox&) const; + /// @brief Return @c true if the given bounding box overlaps with this bounding box. + bool hasOverlap(const BBox&) const; + /// @brief Return @c true if the given bounding box overlaps with this bounding box. + bool intersects(const BBox& other) const { return hasOverlap(other); } + + /// @brief Pad this bounding box. + void expand(ElementType padding); + /// @brief Expand this bounding box to enclose the given point. + void expand(const Vec3T& xyz); + /// @brief Union this bounding box with the given bounding box. + void expand(const BBox&); + /// @brief Union this bounding box with the cubical bounding box with + /// minimum point @a xyzMin and the given edge length. + /// @note Inclusive for integral ElementTypes + void expand(const Vec3T& xyzMin, const ElementType& length); + + /// @brief Translate this bounding box by + /// (tx, ty, tz). + void translate(const Vec3T& t); + + /// @brief Apply a map to this bounding box. + template + BBox applyMap(const MapType& map) const; + /// @brief Apply the inverse of a map to this bounding box + template + BBox applyInverseMap(const MapType& map) const; + + /// @brief Unserialize this bounding box from the given stream. + void read(std::istream& is) { mMin.read(is); mMax.read(is); } + /// @brief Serialize this bounding box to the given stream. + void write(std::ostream& os) const { mMin.write(os); mMax.write(os); } + +private: + Vec3T mMin, mMax; +}; // class BBox + + +//////////////////////////////////////// + + +template +inline +BBox::BBox(): + mMin( std::numeric_limits::max()), + mMax(-std::numeric_limits::max()) +{ +} + +template +inline +BBox::BBox(const Vec3T& xyzMin, const Vec3T& xyzMax): + mMin(xyzMin), mMax(xyzMax) +{ +} + +template +inline +BBox::BBox(const Vec3T& xyzMin, const Vec3T& xyzMax, bool sorted): + mMin(xyzMin), mMax(xyzMax) +{ + if (!sorted) this->sort(); +} + +template +inline +BBox::BBox(const Vec3T& xyzMin, const ElementType& length): + mMin(xyzMin), mMax(xyzMin) +{ + // min and max are inclusive for integral ElementType + const ElementType size = std::is_integral::value ? length-1 : length; + mMax[0] += size; + mMax[1] += size; + mMax[2] += size; +} + +template +inline +BBox::BBox(const ElementType* xyz, bool sorted): + mMin(xyz[0], xyz[1], xyz[2]), + mMax(xyz[3], xyz[4], xyz[5]) +{ + if (!sorted) this->sort(); +} + + +//////////////////////////////////////// + + +template +inline bool +BBox::empty() const +{ + if (std::is_integral::value) { + // min and max are inclusive for integral ElementType + return (mMin[0] > mMax[0] || mMin[1] > mMax[1] || mMin[2] > mMax[2]); + } + return mMin[0] >= mMax[0] || mMin[1] >= mMax[1] || mMin[2] >= mMax[2]; +} + + +template +inline bool +BBox::operator==(const BBox& rhs) const +{ + if (std::is_integral::value) { + return mMin == rhs.min() && mMax == rhs.max(); + } else { + return math::isApproxEqual(mMin, rhs.min()) && math::isApproxEqual(mMax, rhs.max()); + } +} + + +template +inline void +BBox::sort() +{ + Vec3T tMin(mMin), tMax(mMax); + for (int i = 0; i < 3; ++i) { + mMin[i] = std::min(tMin[i], tMax[i]); + mMax[i] = std::max(tMin[i], tMax[i]); + } +} + + +template +inline bool +BBox::isSorted() const +{ + if (std::is_integral::value) { + return (mMin[0] <= mMax[0] && mMin[1] <= mMax[1] && mMin[2] <= mMax[2]); + } else { + ElementType t = math::Tolerance::value(); + return (mMin[0] < (mMax[0] + t) && mMin[1] < (mMax[1] + t) && mMin[2] < (mMax[2] + t)); + } +} + + +template +inline Vec3d +BBox::getCenter() const +{ + return (Vec3d(mMin.asPointer()) + Vec3d(mMax.asPointer())) * 0.5; +} + + +template +inline Vec3T +BBox::extents() const +{ + if (std::is_integral::value) { + return (mMax - mMin) + Vec3T(1, 1, 1); + } else { + return (mMax - mMin); + } +} + +//////////////////////////////////////// + + +template +inline bool +BBox::isInside(const Vec3T& xyz) const +{ + if (std::is_integral::value) { + return xyz[0] >= mMin[0] && xyz[0] <= mMax[0] && + xyz[1] >= mMin[1] && xyz[1] <= mMax[1] && + xyz[2] >= mMin[2] && xyz[2] <= mMax[2]; + } else { + ElementType t = math::Tolerance::value(); + return xyz[0] > (mMin[0]-t) && xyz[0] < (mMax[0]+t) && + xyz[1] > (mMin[1]-t) && xyz[1] < (mMax[1]+t) && + xyz[2] > (mMin[2]-t) && xyz[2] < (mMax[2]+t); + } +} + + +template +inline bool +BBox::isInside(const BBox& b) const +{ + if (std::is_integral::value) { + return b.min()[0] >= mMin[0] && b.max()[0] <= mMax[0] && + b.min()[1] >= mMin[1] && b.max()[1] <= mMax[1] && + b.min()[2] >= mMin[2] && b.max()[2] <= mMax[2]; + } else { + ElementType t = math::Tolerance::value(); + return (b.min()[0]-t) > mMin[0] && (b.max()[0]+t) < mMax[0] && + (b.min()[1]-t) > mMin[1] && (b.max()[1]+t) < mMax[1] && + (b.min()[2]-t) > mMin[2] && (b.max()[2]+t) < mMax[2]; + } +} + + +template +inline bool +BBox::hasOverlap(const BBox& b) const +{ + if (std::is_integral::value) { + return mMax[0] >= b.min()[0] && mMin[0] <= b.max()[0] && + mMax[1] >= b.min()[1] && mMin[1] <= b.max()[1] && + mMax[2] >= b.min()[2] && mMin[2] <= b.max()[2]; + } else { + ElementType t = math::Tolerance::value(); + return mMax[0] > (b.min()[0]-t) && mMin[0] < (b.max()[0]+t) && + mMax[1] > (b.min()[1]-t) && mMin[1] < (b.max()[1]+t) && + mMax[2] > (b.min()[2]-t) && mMin[2] < (b.max()[2]+t); + } +} + + +//////////////////////////////////////// + + +template +inline void +BBox::expand(ElementType dx) +{ + dx = std::abs(dx); + for (int i = 0; i < 3; ++i) { + mMin[i] -= dx; + mMax[i] += dx; + } +} + + +template +inline void +BBox::expand(const Vec3T& xyz) +{ + for (int i = 0; i < 3; ++i) { + mMin[i] = std::min(mMin[i], xyz[i]); + mMax[i] = std::max(mMax[i], xyz[i]); + } +} + + +template +inline void +BBox::expand(const BBox& b) +{ + for (int i = 0; i < 3; ++i) { + mMin[i] = std::min(mMin[i], b.min()[i]); + mMax[i] = std::max(mMax[i], b.max()[i]); + } +} + +template +inline void +BBox::expand(const Vec3T& xyzMin, const ElementType& length) +{ + const ElementType size = std::is_integral::value ? length-1 : length; + for (int i = 0; i < 3; ++i) { + mMin[i] = std::min(mMin[i], xyzMin[i]); + mMax[i] = std::max(mMax[i], xyzMin[i] + size); + } +} + + +template +inline void +BBox::translate(const Vec3T& dx) +{ + mMin += dx; + mMax += dx; +} + +template +template +inline BBox +BBox::applyMap(const MapType& map) const +{ + using Vec3R = Vec3; + BBox bbox; + bbox.expand(map.applyMap(Vec3R(mMin[0], mMin[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMin[0], mMin[1], mMax[2]))); + bbox.expand(map.applyMap(Vec3R(mMin[0], mMax[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMin[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMax[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMin[1], mMax[2]))); + bbox.expand(map.applyMap(Vec3R(mMin[0], mMax[1], mMax[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMax[1], mMax[2]))); + return bbox; +} + +template +template +inline BBox +BBox::applyInverseMap(const MapType& map) const +{ + using Vec3R = Vec3; + BBox bbox; + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMin[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMin[1], mMax[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMax[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMin[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMax[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMin[1], mMax[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMax[1], mMax[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMax[1], mMax[2]))); + return bbox; +} + +//////////////////////////////////////// + + +template +inline std::ostream& +operator<<(std::ostream& os, const BBox& b) +{ + os << b.min() << " -> " << b.max(); + return os; +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_BBOX_HAS_BEEN_INCLUDED diff --git a/openvdb/math/ConjGradient.h b/openvdb/math/ConjGradient.h new file mode 100644 index 00000000..e8fb39e9 --- /dev/null +++ b/openvdb/math/ConjGradient.h @@ -0,0 +1,1771 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file ConjGradient.h +/// @authors D.J. Hill, Peter Cucka +/// @brief Preconditioned conjugate gradient solver (solves @e Ax = @e b using +/// the conjugate gradient method with one of a selection of preconditioners) + +#ifndef OPENVDB_MATH_CONJGRADIENT_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_CONJGRADIENT_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include "Math.h" // for Abs(), isZero(), Max(), Sqrt() +#include +#include +#include // for std::lower_bound() +#include +#include // for std::isfinite() +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { +namespace pcg { + +using SizeType = Index32; + +using SizeRange = tbb::blocked_range; + +template class Vector; + +template class SparseStencilMatrix; + +template class Preconditioner; +template class JacobiPreconditioner; +template class IncompleteCholeskyPreconditioner; + +/// Information about the state of a conjugate gradient solution +struct State { + bool success; + int iterations; + double relativeError; + double absoluteError; +}; + + +/// Return default termination conditions for a conjugate gradient solver. +template +inline State +terminationDefaults() +{ + State s; + s.success = false; + s.iterations = 50; + s.relativeError = 1.0e-6; + s.absoluteError = std::numeric_limits::epsilon() * 100.0; + return s; +} + + +//////////////////////////////////////// + + +/// @brief Solve @e Ax = @e b via the preconditioned conjugate gradient method. +/// +/// @param A a symmetric, positive-definite, @e N x @e N matrix +/// @param b a vector of size @e N +/// @param x a vector of size @e N +/// @param preconditioner a Preconditioner matrix +/// @param termination termination conditions given as a State object with the following fields: +///
+///
success +///
ignored +///
iterations +///
the maximum number of iterations, with or without convergence +///
relativeError +///
the relative error ||bAx|| / ||b|| +/// that denotes convergence +///
absoluteError +///
the absolute error ||bAx|| that denotes convergence +/// +/// @throw ArithmeticError if either @a x or @a b is not of the appropriate size. +template +inline State +solve( + const PositiveDefMatrix& A, + const Vector& b, + Vector& x, + Preconditioner& preconditioner, + const State& termination = terminationDefaults()); + + +/// @brief Solve @e Ax = @e b via the preconditioned conjugate gradient method. +/// +/// @param A a symmetric, positive-definite, @e N x @e N matrix +/// @param b a vector of size @e N +/// @param x a vector of size @e N +/// @param preconditioner a Preconditioner matrix +/// @param termination termination conditions given as a State object with the following fields: +///
+///
success +///
ignored +///
iterations +///
the maximum number of iterations, with or without convergence +///
relativeError +///
the relative error ||bAx|| / ||b|| +/// that denotes convergence +///
absoluteError +///
the absolute error ||bAx|| that denotes convergence +/// @param interrupter an object adhering to the util::NullInterrupter interface +/// with which computation can be interrupted +/// +/// @throw ArithmeticError if either @a x or @a b is not of the appropriate size. +/// @throw RuntimeError if the computation is interrupted. +template +inline State +solve( + const PositiveDefMatrix& A, + const Vector& b, + Vector& x, + Preconditioner& preconditioner, + Interrupter& interrupter, + const State& termination = terminationDefaults()); + + +//////////////////////////////////////// + + +/// Lightweight, variable-length vector +template +class Vector +{ +public: + using ValueType = T; + using Ptr = SharedPtr; + + /// Construct an empty vector. + Vector(): mData(nullptr), mSize(0) {} + /// Construct a vector of @a n elements, with uninitialized values. + Vector(SizeType n): mData(new T[n]), mSize(n) {} + /// Construct a vector of @a n elements and initialize each element to the given value. + Vector(SizeType n, const ValueType& val): mData(new T[n]), mSize(n) { this->fill(val); } + + ~Vector() { mSize = 0; delete[] mData; mData = nullptr; } + + /// Deep copy the given vector. + Vector(const Vector&); + /// Deep copy the given vector. + Vector& operator=(const Vector&); + + /// Return the number of elements in this vector. + SizeType size() const { return mSize; } + /// Return @c true if this vector has no elements. + bool empty() const { return (mSize == 0); } + + /// @brief Reset this vector to have @a n elements, with uninitialized values. + /// @warning All of this vector's existing values will be lost. + void resize(SizeType n); + + /// Swap internal storage with another vector, which need not be the same size. + void swap(Vector& other) { std::swap(mData, other.mData); std::swap(mSize, other.mSize); } + + /// Set all elements of this vector to @a value. + void fill(const ValueType& value); + + //@{ + /// @brief Multiply each element of this vector by @a s. + template void scale(const Scalar& s); + template Vector& operator*=(const Scalar& s) { this->scale(s); return *this; } + //@} + + /// Return the dot product of this vector with the given vector, which must be the same size. + ValueType dot(const Vector&) const; + + /// Return the infinity norm of this vector. + ValueType infNorm() const; + /// Return the L2 norm of this vector. + ValueType l2Norm() const { return Sqrt(this->dot(*this)); } + + /// Return @c true if every element of this vector has a finite value. + bool isFinite() const; + + /// @brief Return @c true if this vector is equivalent to the given vector + /// to within the specified tolerance. + template + bool eq(const Vector& other, + ValueType eps = Tolerance::value()) const; + + /// Return a string representation of this vector. + std::string str() const; + + //@{ + /// @brief Return the value of this vector's ith element. + inline T& at(SizeType i) { return mData[i]; } + inline const T& at(SizeType i) const { return mData[i]; } + inline T& operator[](SizeType i) { return this->at(i); } + inline const T& operator[](SizeType i) const { return this->at(i); } + //@} + + //@{ + /// @brief Return a pointer to this vector's elements. + inline T* data() { return mData; } + inline const T* data() const { return mData; } + inline const T* constData() const { return mData; } + //@} + +private: + // Functor for use with tbb::parallel_for() + template struct ScaleOp; + struct DeterministicDotProductOp; + // Functors for use with tbb::parallel_reduce() + template struct EqOp; + struct InfNormOp; + struct IsFiniteOp; + + T* mData; + SizeType mSize; +}; + +using VectorS = Vector; +using VectorD = Vector; + + +//////////////////////////////////////// + + +/// @brief Sparse, square matrix representing a 3D stencil operator of size @a STENCIL_SIZE +/// @details The implementation is a variation on compressed row storage (CRS). +template +class SparseStencilMatrix +{ +public: + using ValueType = ValueType_; + using VectorType = Vector; + using Ptr = SharedPtr; + + class ConstValueIter; + class ConstRow; + class RowEditor; + + static const ValueType sZeroValue; + + /// Construct an @a n x @a n matrix with at most @a STENCIL_SIZE nonzero elements per row. + SparseStencilMatrix(SizeType n); + + /// Deep copy the given matrix. + SparseStencilMatrix(const SparseStencilMatrix&); + + //@{ + /// Return the number of rows in this matrix. + SizeType numRows() const { return mNumRows; } + SizeType size() const { return mNumRows; } + //@} + + /// @brief Set the value at the given coordinates. + /// @warning It is not safe to set values in the same row simultaneously + /// from multiple threads. + void setValue(SizeType row, SizeType col, const ValueType&); + + //@{ + /// @brief Return the value at the given coordinates. + /// @warning It is not safe to get values from a row while another thread + /// is setting values in that row. + const ValueType& getValue(SizeType row, SizeType col) const; + const ValueType& operator()(SizeType row, SizeType col) const; + //@} + + /// Return a read-only view onto the given row of this matrix. + ConstRow getConstRow(SizeType row) const; + + /// Return a read/write view onto the given row of this matrix. + RowEditor getRowEditor(SizeType row); + + //@{ + /// @brief Multiply all elements in the matrix by @a s; + template void scale(const Scalar& s); + template + SparseStencilMatrix& operator*=(const Scalar& s) { this->scale(s); return *this; } + //@} + + /// @brief Multiply this matrix by @a inVec and return the result in @a resultVec. + /// @throw ArithmeticError if either @a inVec or @a resultVec is not of size @e N, + /// where @e N x @e N is the size of this matrix. + template + void vectorMultiply(const Vector& inVec, Vector& resultVec) const; + + /// @brief Multiply this matrix by the vector represented by the array @a inVec + /// and return the result in @a resultVec. + /// @warning Both @a inVec and @a resultVec must have at least @e N elements, + /// where @e N x @e N is the size of this matrix. + template + void vectorMultiply(const VecValueType* inVec, VecValueType* resultVec) const; + + /// @brief Return @c true if this matrix is equivalent to the given matrix + /// to within the specified tolerance. + template + bool eq(const SparseStencilMatrix& other, + ValueType eps = Tolerance::value()) const; + + /// Return @c true if every element of this matrix has a finite value. + bool isFinite() const; + + /// Return a string representation of this matrix. + std::string str() const; + +private: + struct RowData { + RowData(ValueType* v, SizeType* c, SizeType& s): mVals(v), mCols(c), mSize(s) {} + ValueType* mVals; SizeType* mCols; SizeType& mSize; + }; + + struct ConstRowData { + ConstRowData(const ValueType* v, const SizeType* c, const SizeType& s): + mVals(v), mCols(c), mSize(s) {} + const ValueType* mVals; const SizeType* mCols; const SizeType& mSize; + }; + + /// Base class for row accessors + template + class RowBase + { + public: + using DataType = DataType_; + + static SizeType capacity() { return STENCIL_SIZE; } + + RowBase(const DataType& data): mData(data) {} + + bool empty() const { return (mData.mSize == 0); } + const SizeType& size() const { return mData.mSize; } + + const ValueType& getValue(SizeType columnIdx, bool& active) const; + const ValueType& getValue(SizeType columnIdx) const; + + /// Return an iterator over the stored values in this row. + ConstValueIter cbegin() const; + + /// @brief Return @c true if this row is equivalent to the given row + /// to within the specified tolerance. + template + bool eq(const RowBase& other, + ValueType eps = Tolerance::value()) const; + + /// @brief Return the dot product of this row with the first + /// @a vecSize elements of @a inVec. + /// @warning @a inVec must have at least @a vecSize elements. + template + VecValueType dot(const VecValueType* inVec, SizeType vecSize) const; + + /// Return the dot product of this row with the given vector. + template + VecValueType dot(const Vector& inVec) const; + + /// Return a string representation of this row. + std::string str() const; + + protected: + friend class ConstValueIter; + + const ValueType& value(SizeType i) const { return mData.mVals[i]; } + SizeType column(SizeType i) const { return mData.mCols[i]; } + + /// @brief Return the array index of the first column index that is + /// equal to or greater than the given column index. + /// @note If @a columnIdx is larger than any existing column index, + /// the return value will point beyond the end of the array. + SizeType find(SizeType columnIdx) const; + + DataType mData; + }; + + using ConstRowBase = RowBase; + +public: + /// Iterator over the stored values in a row of this matrix + class ConstValueIter + { + public: + const ValueType& operator*() const + { + if (mData.mSize == 0) return SparseStencilMatrix::sZeroValue; + return mData.mVals[mCursor]; + } + + SizeType column() const { return mData.mCols[mCursor]; } + + void increment() { mCursor++; } + ConstValueIter& operator++() { increment(); return *this; } + operator bool() const { return (mCursor < mData.mSize); } + + void reset() { mCursor = 0; } + + private: + friend class SparseStencilMatrix; + ConstValueIter(const RowData& d): mData(d.mVals, d.mCols, d.mSize), mCursor(0) {} + ConstValueIter(const ConstRowData& d): mData(d), mCursor(0) {} + + const ConstRowData mData; + SizeType mCursor; + }; + + + /// Read-only accessor to a row of this matrix + class ConstRow: public ConstRowBase + { + public: + ConstRow(const ValueType* valueHead, const SizeType* columnHead, const SizeType& rowSize); + }; // class ConstRow + + + /// Read/write accessor to a row of this matrix + class RowEditor: public RowBase<> + { + public: + RowEditor(ValueType* valueHead, SizeType* columnHead, SizeType& rowSize, SizeType colSize); + + /// Set the number of entries in this row to zero. + void clear(); + + /// @brief Set the value of the entry in the specified column. + /// @return the current number of entries stored in this row. + SizeType setValue(SizeType column, const ValueType& value); + + //@{ + /// @brief Scale all of the entries in this row. + template void scale(const Scalar&); + template + RowEditor& operator*=(const Scalar& s) { this->scale(s); return *this; } + //@} + + private: + const SizeType mNumColumns; // used only for bounds checking + }; // class RowEditor + +private: + // Functors for use with tbb::parallel_for() + struct MatrixCopyOp; + template struct VecMultOp; + template struct RowScaleOp; + + // Functors for use with tbb::parallel_reduce() + struct IsFiniteOp; + template struct EqOp; + + const SizeType mNumRows; + std::unique_ptr mValueArray; + std::unique_ptr mColumnIdxArray; + std::unique_ptr mRowSizeArray; +}; // class SparseStencilMatrix + + +//////////////////////////////////////// + + +/// Base class for conjugate gradient preconditioners +template +class Preconditioner +{ +public: + using ValueType = T; + using Ptr = SharedPtr; + + template Preconditioner(const SparseStencilMatrix&) {} + virtual ~Preconditioner() = default; + + virtual bool isValid() const { return true; } + + /// @brief Apply this preconditioner to a residue vector: + /// @e z = M−1r + /// @param r residue vector + /// @param[out] z preconditioned residue vector + virtual void apply(const Vector& r, Vector& z) = 0; +}; + + +//////////////////////////////////////// + + +namespace internal { + +// Functor for use with tbb::parallel_for() to copy data from one array to another +template +struct CopyOp +{ + CopyOp(const T* from_, T* to_): from(from_), to(to_) {} + + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) to[n] = from[n]; + } + + const T* from; + T* to; +}; + + +// Functor for use with tbb::parallel_for() to fill an array with a constant value +template +struct FillOp +{ + FillOp(T* data_, const T& val_): data(data_), val(val_) {} + + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) data[n] = val; + } + + T* data; + const T val; +}; + + +// Functor for use with tbb::parallel_for() that computes a * x + y +template +struct LinearOp +{ + LinearOp(const T& a_, const T* x_, const T* y_, T* out_): a(a_), x(x_), y(y_), out(out_) {} + + void operator()(const SizeRange& range) const { + if (isExactlyEqual(a, T(1))) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = x[n] + y[n]; + } else if (isExactlyEqual(a, T(-1))) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = -x[n] + y[n]; + } else { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = a * x[n] + y[n]; + } + } + + const T a, *x, *y; + T* out; +}; + +} // namespace internal + + +//////////////////////////////////////// + + +inline std::ostream& +operator<<(std::ostream& os, const State& state) +{ + os << (state.success ? "succeeded with " : "") + << "rel. err. " << state.relativeError << ", abs. err. " << state.absoluteError + << " after " << state.iterations << " iteration" << (state.iterations == 1 ? "" : "s"); + return os; +} + + +//////////////////////////////////////// + + +template +inline +Vector::Vector(const Vector& other): mData(new T[other.mSize]), mSize(other.mSize) +{ + tbb::parallel_for(SizeRange(0, mSize), + internal::CopyOp(/*from=*/other.mData, /*to=*/mData)); +} + + +template +inline +Vector& Vector::operator=(const Vector& other) +{ + // Update the internal storage to the correct size + + if (mSize != other.mSize) { + mSize = other.mSize; + delete[] mData; + mData = new T[mSize]; + } + + // Deep copy the data + tbb::parallel_for(SizeRange(0, mSize), + internal::CopyOp(/*from=*/other.mData, /*to=*/mData)); + + return *this; +} + + +template +inline void +Vector::resize(SizeType n) +{ + if (n != mSize) { + if (mData) delete[] mData; + mData = new T[n]; + mSize = n; + } +} + + +template +inline void +Vector::fill(const ValueType& value) +{ + tbb::parallel_for(SizeRange(0, mSize), internal::FillOp(mData, value)); +} + + +template +template +struct Vector::ScaleOp +{ + ScaleOp(T* data_, const Scalar& s_): data(data_), s(s_) {} + + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) data[n] *= s; + } + + T* data; + const Scalar s; +}; + + +template +template +inline void +Vector::scale(const Scalar& s) +{ + tbb::parallel_for(SizeRange(0, mSize), ScaleOp(mData, s)); +} + + +template +struct Vector::DeterministicDotProductOp +{ + DeterministicDotProductOp(const T* a_, const T* b_, + const SizeType binCount_, const SizeType arraySize_, T* reducetmp_): + a(a_), b(b_), binCount(binCount_), arraySize(arraySize_), reducetmp(reducetmp_) {} + + void operator()(const SizeRange& range) const + { + const SizeType binSize = arraySize / binCount; + + // Iterate over bins (array segments) + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + const SizeType begin = n * binSize; + const SizeType end = (n == binCount-1) ? arraySize : begin + binSize; + + // Compute the partial sum for this array segment + T sum = zeroVal(); + for (SizeType i = begin; i < end; ++i) { sum += a[i] * b[i]; } + // Store the partial sum + reducetmp[n] = sum; + } + } + + + const T* a; + const T* b; + const SizeType binCount; + const SizeType arraySize; + T* reducetmp; +}; + +template +inline T +Vector::dot(const Vector& other) const +{ + assert(this->size() == other.size()); + + const T* aData = this->data(); + const T* bData = other.data(); + + SizeType arraySize = this->size(); + + T result = zeroVal(); + + if (arraySize < 1024) { + + // Compute the dot product in serial for small arrays + + for (SizeType n = 0; n < arraySize; ++n) { + result += aData[n] * bData[n]; + } + + } else { + + // Compute the dot product by segmenting the arrays into + // a predetermined number of sub arrays in parallel and + // accumulate the finial result in series. + + const SizeType binCount = 100; + T partialSums[100]; + + tbb::parallel_for(SizeRange(0, binCount), + DeterministicDotProductOp(aData, bData, binCount, arraySize, partialSums)); + + for (SizeType n = 0; n < binCount; ++n) { + result += partialSums[n]; + } + } + + return result; +} + + +template +struct Vector::InfNormOp +{ + InfNormOp(const T* data_): data(data_) {} + + T operator()(const SizeRange& range, T maxValue) const + { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + maxValue = Max(maxValue, Abs(data[n])); + } + return maxValue; + } + + const T* data; +}; + + +template +inline T +Vector::infNorm() const +{ + // Parallelize over the elements of this vector. + T result = tbb::parallel_reduce(SizeRange(0, this->size()), /*seed=*/zeroVal(), + InfNormOp(this->data()), /*join=*/[](T max1, T max2) { return Max(max1, max2); }); + return result; +} + + +template +struct Vector::IsFiniteOp +{ + IsFiniteOp(const T* data_): data(data_) {} + + bool operator()(const SizeRange& range, bool finite) const + { + if (finite) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + if (!std::isfinite(data[n])) return false; + } + } + return finite; + } + + const T* data; +}; + + +template +inline bool +Vector::isFinite() const +{ + // Parallelize over the elements of this vector. + bool finite = tbb::parallel_reduce(SizeRange(0, this->size()), /*seed=*/true, + IsFiniteOp(this->data()), + /*join=*/[](bool finite1, bool finite2) { return (finite1 && finite2); }); + return finite; +} + + +template +template +struct Vector::EqOp +{ + EqOp(const T* a_, const OtherValueType* b_, T e): a(a_), b(b_), eps(e) {} + + bool operator()(const SizeRange& range, bool equal) const + { + if (equal) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + if (!isApproxEqual(a[n], b[n], eps)) return false; + } + } + return equal; + } + + const T* a; + const OtherValueType* b; + const T eps; +}; + + +template +template +inline bool +Vector::eq(const Vector& other, ValueType eps) const +{ + if (this->size() != other.size()) return false; + bool equal = tbb::parallel_reduce(SizeRange(0, this->size()), /*seed=*/true, + EqOp(this->data(), other.data(), eps), + /*join=*/[](bool eq1, bool eq2) { return (eq1 && eq2); }); + return equal; +} + + +template +inline std::string +Vector::str() const +{ + std::ostringstream ostr; + ostr << "["; + std::string sep; + for (SizeType n = 0, N = this->size(); n < N; ++n) { + ostr << sep << (*this)[n]; + sep = ", "; + } + ostr << "]"; + return ostr.str(); +} + + +//////////////////////////////////////// + + +template +const ValueType SparseStencilMatrix::sZeroValue = zeroVal(); + + +template +inline +SparseStencilMatrix::SparseStencilMatrix(SizeType numRows) + : mNumRows(numRows) + , mValueArray(new ValueType[mNumRows * STENCIL_SIZE]) + , mColumnIdxArray(new SizeType[mNumRows * STENCIL_SIZE]) + , mRowSizeArray(new SizeType[mNumRows]) +{ + // Initialize the matrix to a null state by setting the size of each row to zero. + tbb::parallel_for(SizeRange(0, mNumRows), + internal::FillOp(mRowSizeArray.get(), /*value=*/0)); +} + + +template +struct SparseStencilMatrix::MatrixCopyOp +{ + MatrixCopyOp(const SparseStencilMatrix& from_, SparseStencilMatrix& to_): + from(&from_), to(&to_) {} + + void operator()(const SizeRange& range) const + { + const ValueType* fromVal = from->mValueArray.get(); + const SizeType* fromCol = from->mColumnIdxArray.get(); + ValueType* toVal = to->mValueArray.get(); + SizeType* toCol = to->mColumnIdxArray.get(); + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + toVal[n] = fromVal[n]; + toCol[n] = fromCol[n]; + } + } + + const SparseStencilMatrix* from; SparseStencilMatrix* to; +}; + + +template +inline +SparseStencilMatrix::SparseStencilMatrix(const SparseStencilMatrix& other) + : mNumRows(other.mNumRows) + , mValueArray(new ValueType[mNumRows * STENCIL_SIZE]) + , mColumnIdxArray(new SizeType[mNumRows * STENCIL_SIZE]) + , mRowSizeArray(new SizeType[mNumRows]) +{ + SizeType size = mNumRows * STENCIL_SIZE; + + // Copy the value and column index arrays from the other matrix to this matrix. + tbb::parallel_for(SizeRange(0, size), MatrixCopyOp(/*from=*/other, /*to=*/*this)); + + // Copy the row size array from the other matrix to this matrix. + tbb::parallel_for(SizeRange(0, mNumRows), + internal::CopyOp(/*from=*/other.mRowSizeArray.get(), /*to=*/mRowSizeArray.get())); +} + + +template +inline void +SparseStencilMatrix::setValue(SizeType row, SizeType col, + const ValueType& val) +{ + assert(row < mNumRows); + this->getRowEditor(row).setValue(col, val); +} + + +template +inline const ValueType& +SparseStencilMatrix::getValue(SizeType row, SizeType col) const +{ + assert(row < mNumRows); + return this->getConstRow(row).getValue(col); +} + + +template +inline const ValueType& +SparseStencilMatrix::operator()(SizeType row, SizeType col) const +{ + return this->getValue(row,col); +} + + +template +template +struct SparseStencilMatrix::RowScaleOp +{ + RowScaleOp(SparseStencilMatrix& m, const Scalar& s_): mat(&m), s(s_) {} + + void operator()(const SizeRange& range) const + { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + RowEditor row = mat->getRowEditor(n); + row.scale(s); + } + } + + SparseStencilMatrix* mat; + const Scalar s; +}; + + +template +template +inline void +SparseStencilMatrix::scale(const Scalar& s) +{ + // Parallelize over the rows in the matrix. + tbb::parallel_for(SizeRange(0, mNumRows), RowScaleOp(*this, s)); +} + + +template +template +struct SparseStencilMatrix::VecMultOp +{ + VecMultOp(const SparseStencilMatrix& m, const VecValueType* i, VecValueType* o): + mat(&m), in(i), out(o) {} + + void operator()(const SizeRange& range) const + { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + ConstRow row = mat->getConstRow(n); + out[n] = row.dot(in, mat->numRows()); + } + } + + const SparseStencilMatrix* mat; + const VecValueType* in; + VecValueType* out; +}; + + +template +template +inline void +SparseStencilMatrix::vectorMultiply( + const Vector& inVec, Vector& resultVec) const +{ + if (inVec.size() != mNumRows) { + OPENVDB_THROW(ArithmeticError, "matrix and input vector have incompatible sizes (" + << mNumRows << "x" << mNumRows << " vs. " << inVec.size() << ")"); + } + if (resultVec.size() != mNumRows) { + OPENVDB_THROW(ArithmeticError, "matrix and result vector have incompatible sizes (" + << mNumRows << "x" << mNumRows << " vs. " << resultVec.size() << ")"); + } + + vectorMultiply(inVec.data(), resultVec.data()); +} + + +template +template +inline void +SparseStencilMatrix::vectorMultiply( + const VecValueType* inVec, VecValueType* resultVec) const +{ + // Parallelize over the rows in the matrix. + tbb::parallel_for(SizeRange(0, mNumRows), + VecMultOp(*this, inVec, resultVec)); +} + + +template +template +struct SparseStencilMatrix::EqOp +{ + EqOp(const SparseStencilMatrix& a_, + const SparseStencilMatrix& b_, ValueType e): + a(&a_), b(&b_), eps(e) {} + + bool operator()(const SizeRange& range, bool equal) const + { + if (equal) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + if (!a->getConstRow(n).eq(b->getConstRow(n), eps)) return false; + } + } + return equal; + } + + const SparseStencilMatrix* a; + const SparseStencilMatrix* b; + const ValueType eps; +}; + + +template +template +inline bool +SparseStencilMatrix::eq( + const SparseStencilMatrix& other, ValueType eps) const +{ + if (this->numRows() != other.numRows()) return false; + bool equal = tbb::parallel_reduce(SizeRange(0, this->numRows()), /*seed=*/true, + EqOp(*this, other, eps), + /*join=*/[](bool eq1, bool eq2) { return (eq1 && eq2); }); + return equal; +} + + +template +struct SparseStencilMatrix::IsFiniteOp +{ + IsFiniteOp(const SparseStencilMatrix& m): mat(&m) {} + + bool operator()(const SizeRange& range, bool finite) const + { + if (finite) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + const ConstRow row = mat->getConstRow(n); + for (ConstValueIter it = row.cbegin(); it; ++it) { + if (!std::isfinite(*it)) return false; + } + } + } + return finite; + } + + const SparseStencilMatrix* mat; +}; + + +template +inline bool +SparseStencilMatrix::isFinite() const +{ + // Parallelize over the rows of this matrix. + bool finite = tbb::parallel_reduce(SizeRange(0, this->numRows()), /*seed=*/true, + IsFiniteOp(*this), /*join=*/[](bool finite1, bool finite2) { return (finite1&&finite2); }); + return finite; +} + + +template +inline std::string +SparseStencilMatrix::str() const +{ + std::ostringstream ostr; + for (SizeType n = 0, N = this->size(); n < N; ++n) { + ostr << n << ": " << this->getConstRow(n).str() << "\n"; + } + return ostr.str(); +} + + +template +inline typename SparseStencilMatrix::RowEditor +SparseStencilMatrix::getRowEditor(SizeType i) +{ + assert(i < mNumRows); + const SizeType head = i * STENCIL_SIZE; + return RowEditor(&mValueArray[head], &mColumnIdxArray[head], mRowSizeArray[i], mNumRows); +} + + +template +inline typename SparseStencilMatrix::ConstRow +SparseStencilMatrix::getConstRow(SizeType i) const +{ + assert(i < mNumRows); + const SizeType head = i * STENCIL_SIZE; // index for this row into main storage + return ConstRow(&mValueArray[head], &mColumnIdxArray[head], mRowSizeArray[i]); +} + + +template +template +inline SizeType +SparseStencilMatrix::RowBase::find(SizeType columnIdx) const +{ + if (this->empty()) return mData.mSize; + + // Get a pointer to the first column index that is equal to or greater than the given index. + // (This assumes that the data is sorted by column.) + const SizeType* colPtr = std::lower_bound(mData.mCols, mData.mCols + mData.mSize, columnIdx); + // Return the offset of the pointer from the beginning of the array. + return static_cast(colPtr - mData.mCols); +} + + +template +template +inline const ValueType& +SparseStencilMatrix::RowBase::getValue( + SizeType columnIdx, bool& active) const +{ + active = false; + SizeType idx = this->find(columnIdx); + if (idx < this->size() && this->column(idx) == columnIdx) { + active = true; + return this->value(idx); + } + return SparseStencilMatrix::sZeroValue; +} + +template +template +inline const ValueType& +SparseStencilMatrix::RowBase::getValue(SizeType columnIdx) const +{ + SizeType idx = this->find(columnIdx); + if (idx < this->size() && this->column(idx) == columnIdx) { + return this->value(idx); + } + return SparseStencilMatrix::sZeroValue; +} + + +template +template +inline typename SparseStencilMatrix::ConstValueIter +SparseStencilMatrix::RowBase::cbegin() const +{ + return ConstValueIter(mData); +} + + +template +template +template +inline bool +SparseStencilMatrix::RowBase::eq( + const RowBase& other, ValueType eps) const +{ + if (this->size() != other.size()) return false; + for (ConstValueIter it = cbegin(), oit = other.cbegin(); it || oit; ++it, ++oit) { + if (it.column() != oit.column()) return false; + if (!isApproxEqual(*it, *oit, eps)) return false; + } + return true; +} + + +template +template +template +inline VecValueType +SparseStencilMatrix::RowBase::dot( + const VecValueType* inVec, SizeType vecSize) const +{ + VecValueType result = zeroVal(); + for (SizeType idx = 0, N = std::min(vecSize, this->size()); idx < N; ++idx) { + result += static_cast(this->value(idx) * inVec[this->column(idx)]); + } + return result; +} + +template +template +template +inline VecValueType +SparseStencilMatrix::RowBase::dot( + const Vector& inVec) const +{ + return dot(inVec.data(), inVec.size()); +} + + +template +template +inline std::string +SparseStencilMatrix::RowBase::str() const +{ + std::ostringstream ostr; + std::string sep; + for (SizeType n = 0, N = this->size(); n < N; ++n) { + ostr << sep << "(" << this->column(n) << ", " << this->value(n) << ")"; + sep = ", "; + } + return ostr.str(); +} + + +template +inline +SparseStencilMatrix::ConstRow::ConstRow( + const ValueType* valueHead, const SizeType* columnHead, const SizeType& rowSize): + ConstRowBase(ConstRowData(const_cast(valueHead), + const_cast(columnHead), const_cast(rowSize))) +{ +} + + +template +inline +SparseStencilMatrix::RowEditor::RowEditor( + ValueType* valueHead, SizeType* columnHead, SizeType& rowSize, SizeType colSize): + RowBase<>(RowData(valueHead, columnHead, rowSize)), mNumColumns(colSize) +{ +} + + +template +inline void +SparseStencilMatrix::RowEditor::clear() +{ + // Note: since mSize is a reference, this modifies the underlying matrix. + RowBase<>::mData.mSize = 0; +} + + +template +inline SizeType +SparseStencilMatrix::RowEditor::setValue( + SizeType column, const ValueType& value) +{ + assert(column < mNumColumns); + + RowData& data = RowBase<>::mData; + + // Get the offset of the first column index that is equal to or greater than + // the column to be modified. + SizeType offset = this->find(column); + + if (offset < data.mSize && data.mCols[offset] == column) { + // If the column already exists, just update its value. + data.mVals[offset] = value; + return data.mSize; + } + + // Check that it is safe to add a new column. + assert(data.mSize < this->capacity()); + + if (offset >= data.mSize) { + // The new column's index is larger than any existing index. Append the new column. + data.mVals[data.mSize] = value; + data.mCols[data.mSize] = column; + } else { + // Insert the new column at the computed offset after shifting subsequent columns. + for (SizeType i = data.mSize; i > offset; --i) { + data.mVals[i] = data.mVals[i - 1]; + data.mCols[i] = data.mCols[i - 1]; + } + data.mVals[offset] = value; + data.mCols[offset] = column; + } + ++data.mSize; + + return data.mSize; +} + + +template +template +inline void +SparseStencilMatrix::RowEditor::scale(const Scalar& s) +{ + for (int idx = 0, N = this->size(); idx < N; ++idx) { + RowBase<>::mData.mVals[idx] *= s; + } +} + + +//////////////////////////////////////// + + +/// Diagonal preconditioner +template +class JacobiPreconditioner: public Preconditioner +{ +private: + struct InitOp; + struct ApplyOp; + +public: + using ValueType = typename MatrixType::ValueType; + using BaseType = Preconditioner; + using VectorType = Vector; + using Ptr = SharedPtr; + + JacobiPreconditioner(const MatrixType& A): BaseType(A), mDiag(A.numRows()) + { + // Initialize vector mDiag with the values from the matrix diagonal. + tbb::parallel_for(SizeRange(0, A.numRows()), InitOp(A, mDiag.data())); + } + + ~JacobiPreconditioner() override = default; + + void apply(const Vector& r, Vector& z) override + { + const SizeType size = mDiag.size(); + + assert(r.size() == z.size()); + assert(r.size() == size); + + tbb::parallel_for(SizeRange(0, size), ApplyOp(mDiag.data(), r.data(), z.data())); + } + + /// Return @c true if all values along the diagonal are finite. + bool isFinite() const { return mDiag.isFinite(); } + +private: + // Functor for use with tbb::parallel_for() + struct InitOp + { + InitOp(const MatrixType& m, ValueType* v): mat(&m), vec(v) {} + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + const ValueType val = mat->getValue(n, n); + assert(!isApproxZero(val, ValueType(0.0001))); + vec[n] = static_cast(1.0 / val); + } + } + const MatrixType* mat; ValueType* vec; + }; + + // Functor for use with tbb::parallel_reduce() + struct ApplyOp + { + ApplyOp(const ValueType* x_, const ValueType* y_, ValueType* out_): + x(x_), y(y_), out(out_) {} + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = x[n] * y[n]; + } + const ValueType *x, *y; ValueType* out; + }; + + // The Jacobi preconditioner is a diagonal matrix + VectorType mDiag; +}; // class JacobiPreconditioner + + +/// Preconditioner using incomplete Cholesky factorization +template +class IncompleteCholeskyPreconditioner: public Preconditioner +{ +private: + struct CopyToLowerOp; + struct TransposeOp; + +public: + using ValueType = typename MatrixType::ValueType; + using BaseType = Preconditioner; + using VectorType = Vector; + using Ptr = SharedPtr; + using TriangularMatrix = SparseStencilMatrix; + using TriangleConstRow = typename TriangularMatrix::ConstRow; + using TriangleRowEditor = typename TriangularMatrix::RowEditor; + + IncompleteCholeskyPreconditioner(const MatrixType& matrix) + : BaseType(matrix) + , mLowerTriangular(matrix.numRows()) + , mUpperTriangular(matrix.numRows()) + , mTempVec(matrix.numRows()) + { + // Size of matrix + const SizeType numRows = mLowerTriangular.numRows(); + + // Copy the upper triangular part to the lower triangular part. + tbb::parallel_for(SizeRange(0, numRows), CopyToLowerOp(matrix, mLowerTriangular)); + + // Build the Incomplete Cholesky Matrix + // + // Algorithm: + // + // for (k = 0; k < size; ++k) { + // A(k,k) = sqrt(A(k,k)); + // for (i = k +1, i < size; ++i) { + // if (A(i,k) == 0) continue; + // A(i,k) = A(i,k) / A(k,k); + // } + // for (j = k+1; j < size; ++j) { + // for (i = j; i < size; ++i) { + // if (A(i,j) == 0) continue; + // A(i,j) -= A(i,k)*A(j,k); + // } + // } + // } + + mPassedCompatibilityCondition = true; + + for (SizeType k = 0; k < numRows; ++k) { + + TriangleConstRow crow_k = mLowerTriangular.getConstRow(k); + ValueType diagonalValue = crow_k.getValue(k); + + // Test if the matrix build has failed. + if (diagonalValue < 1.e-5) { + mPassedCompatibilityCondition = false; + break; + } + + diagonalValue = Sqrt(diagonalValue); + + TriangleRowEditor row_k = mLowerTriangular.getRowEditor(k); + row_k.setValue(k, diagonalValue); + + // Exploit the fact that the matrix is symmetric. + typename MatrixType::ConstRow srcRow = matrix.getConstRow(k); + typename MatrixType::ConstValueIter citer = srcRow.cbegin(); + for ( ; citer; ++citer) { + SizeType ii = citer.column(); + if (ii < k+1) continue; // look above diagonal + + TriangleRowEditor row_ii = mLowerTriangular.getRowEditor(ii); + + row_ii.setValue(k, *citer / diagonalValue); + } + + // for (j = k+1; j < size; ++j) replaced by row iter below + citer.reset(); // k,j entries + for ( ; citer; ++citer) { + SizeType j = citer.column(); + if (j < k+1) continue; + + TriangleConstRow row_j = mLowerTriangular.getConstRow(j); + ValueType a_jk = row_j.getValue(k); // a_jk is non zero if a_kj is non zero + + // Entry (i,j) is non-zero if matrix(j,i) is nonzero + + typename MatrixType::ConstRow mask = matrix.getConstRow(j); + typename MatrixType::ConstValueIter maskIter = mask.cbegin(); + for ( ; maskIter; ++maskIter) { + SizeType i = maskIter.column(); + if (i < j) continue; + + TriangleConstRow crow_i = mLowerTriangular.getConstRow(i); + ValueType a_ij = crow_i.getValue(j); + ValueType a_ik = crow_i.getValue(k); + TriangleRowEditor row_i = mLowerTriangular.getRowEditor(i); + a_ij -= a_ik * a_jk; + + row_i.setValue(j, a_ij); + } + } + } + + // Build the transpose of the IC matrix: mUpperTriangular + tbb::parallel_for(SizeRange(0, numRows), + TransposeOp(matrix, mLowerTriangular, mUpperTriangular)); + } + + ~IncompleteCholeskyPreconditioner() override = default; + + bool isValid() const override { return mPassedCompatibilityCondition; } + + void apply(const Vector& rVec, Vector& zVec) override + { + if (!mPassedCompatibilityCondition) { + OPENVDB_THROW(ArithmeticError, "invalid Cholesky decomposition"); + } + + // Solve mUpperTriangular * mLowerTriangular * rVec = zVec; + + SizeType size = mLowerTriangular.numRows(); + + zVec.fill(zeroVal()); + ValueType* zData = zVec.data(); + + if (size == 0) return; + + assert(rVec.size() == size); + assert(zVec.size() == size); + + // Allocate a temp vector + mTempVec.fill(zeroVal()); + ValueType* tmpData = mTempVec.data(); + const ValueType* rData = rVec.data(); + + // Solve mLowerTriangular * tmp = rVec; + for (SizeType i = 0; i < size; ++i) { + typename TriangularMatrix::ConstRow row = mLowerTriangular.getConstRow(i); + ValueType diagonal = row.getValue(i); + ValueType dot = row.dot(mTempVec); + tmpData[i] = (rData[i] - dot) / diagonal; + if (!std::isfinite(tmpData[i])) { + OPENVDB_LOG_DEBUG_RUNTIME("1 diagonal was " << diagonal); + OPENVDB_LOG_DEBUG_RUNTIME("1a diagonal " << row.getValue(i)); + } + } + + // Solve mUpperTriangular * zVec = tmp; + for (SizeType ii = 0; ii < size; ++ii) { + SizeType i = size - 1 - ii; + typename TriangularMatrix::ConstRow row = mUpperTriangular.getConstRow(i); + ValueType diagonal = row.getValue(i); + ValueType dot = row.dot(zVec); + zData[i] = (tmpData[i] - dot) / diagonal; + if (!std::isfinite(zData[i])) { + OPENVDB_LOG_DEBUG_RUNTIME("2 diagonal was " << diagonal); + } + } + } + + const TriangularMatrix& lowerMatrix() const { return mLowerTriangular; } + const TriangularMatrix& upperMatrix() const { return mUpperTriangular; } + +private: + // Functor for use with tbb::parallel_for() + struct CopyToLowerOp + { + CopyToLowerOp(const MatrixType& m, TriangularMatrix& l): mat(&m), lower(&l) {} + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + typename TriangularMatrix::RowEditor outRow = lower->getRowEditor(n); + outRow.clear(); + typename MatrixType::ConstRow inRow = mat->getConstRow(n); + for (typename MatrixType::ConstValueIter it = inRow.cbegin(); it; ++it) { + if (it.column() > n) continue; // skip above diagonal + outRow.setValue(it.column(), *it); + } + } + } + const MatrixType* mat; TriangularMatrix* lower; + }; + + // Functor for use with tbb::parallel_for() + struct TransposeOp + { + TransposeOp(const MatrixType& m, const TriangularMatrix& l, TriangularMatrix& u): + mat(&m), lower(&l), upper(&u) {} + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + typename TriangularMatrix::RowEditor outRow = upper->getRowEditor(n); + outRow.clear(); + // Use the fact that matrix is symmetric. + typename MatrixType::ConstRow inRow = mat->getConstRow(n); + for (typename MatrixType::ConstValueIter it = inRow.cbegin(); it; ++it) { + const SizeType column = it.column(); + if (column < n) continue; // only set upper triangle + outRow.setValue(column, lower->getValue(column, n)); + } + } + } + const MatrixType* mat; const TriangularMatrix* lower; TriangularMatrix* upper; + }; + + TriangularMatrix mLowerTriangular; + TriangularMatrix mUpperTriangular; + Vector mTempVec; + bool mPassedCompatibilityCondition; +}; // class IncompleteCholeskyPreconditioner + + +//////////////////////////////////////// + + +namespace internal { + +/// Compute @e ax + @e y. +template +inline void +axpy(const T& a, const T* xVec, const T* yVec, T* resultVec, SizeType size) +{ + tbb::parallel_for(SizeRange(0, size), LinearOp(a, xVec, yVec, resultVec)); +} + +/// Compute @e ax + @e y. +template +inline void +axpy(const T& a, const Vector& xVec, const Vector& yVec, Vector& result) +{ + assert(xVec.size() == yVec.size()); + assert(xVec.size() == result.size()); + axpy(a, xVec.data(), yVec.data(), result.data(), xVec.size()); +} + + +/// Compute @e r = @e b − @e Ax. +template +inline void +computeResidual(const MatrixOperator& A, const VecValueType* x, + const VecValueType* b, VecValueType* r) +{ + // Compute r = A * x. + A.vectorMultiply(x, r); + // Compute r = b - r. + tbb::parallel_for(SizeRange(0, A.numRows()), LinearOp(-1.0, r, b, r)); +} + +/// Compute @e r = @e b − @e Ax. +template +inline void +computeResidual(const MatrixOperator& A, const Vector& x, const Vector& b, Vector& r) +{ + assert(x.size() == b.size()); + assert(x.size() == r.size()); + assert(x.size() == A.numRows()); + + computeResidual(A, x.data(), b.data(), r.data()); +} + +} // namespace internal + + +//////////////////////////////////////// + + +template +inline State +solve( + const PositiveDefMatrix& Amat, + const Vector& bVec, + Vector& xVec, + Preconditioner& precond, + const State& termination) +{ + util::NullInterrupter interrupter; + return solve(Amat, bVec, xVec, precond, interrupter, termination); +} + + +template +inline State +solve( + const PositiveDefMatrix& Amat, + const Vector& bVec, + Vector& xVec, + Preconditioner& precond, + Interrupter& interrupter, + const State& termination) +{ + using ValueType = typename PositiveDefMatrix::ValueType; + using VectorType = Vector; + + State result; + result.success = false; + result.iterations = 0; + result.relativeError = 0.0; + result.absoluteError = 0.0; + + const SizeType size = Amat.numRows(); + if (size == 0) { + OPENVDB_LOG_WARN("pcg::solve(): matrix has dimension zero"); + return result; + } + if (size != bVec.size()) { + OPENVDB_THROW(ArithmeticError, "A and b have incompatible sizes" + << size << "x" << size << " vs. " << bVec.size() << ")"); + } + if (size != xVec.size()) { + OPENVDB_THROW(ArithmeticError, "A and x have incompatible sizes" + << size << "x" << size << " vs. " << xVec.size() << ")"); + } + + // Temp vectors + VectorType zVec(size); // transformed residual (M^-1 r) + VectorType pVec(size); // search direction + VectorType qVec(size); // A * p + + // Compute norm of B (the source) + const ValueType tmp = bVec.infNorm(); + const ValueType infNormOfB = isZero(tmp) ? ValueType(1) : tmp; + + // Compute rVec: residual = b - Ax. + VectorType rVec(size); // vector of residuals + + internal::computeResidual(Amat, xVec, bVec, rVec); + + assert(rVec.isFinite()); + + // Normalize the residual norm with the source norm and look for early out. + result.absoluteError = static_cast(rVec.infNorm()); + result.relativeError = static_cast(result.absoluteError / infNormOfB); + if (result.relativeError <= termination.relativeError) { + result.success = true; + return result; + } + + // Iterations of the CG solve + + ValueType rDotZPrev(1); // inner product of + + // Keep track of the minimum error to monitor convergence. + ValueType minL2Error = std::numeric_limits::max(); + ValueType l2Error; + + int iteration = 0; + for ( ; iteration < termination.iterations; ++iteration) { + + if (interrupter.wasInterrupted()) { + OPENVDB_THROW(RuntimeError, "conjugate gradient solver was interrupted"); + } + + OPENVDB_LOG_DEBUG_RUNTIME("pcg::solve() " << result); + + result.iterations = iteration + 1; + + // Apply preconditioner to residual + // z_{k} = M^-1 r_{k} + precond.apply(rVec, zVec); + + // + const ValueType rDotZ = rVec.dot(zVec); + assert(std::isfinite(rDotZ)); + + if (0 == iteration) { + // Initialize + pVec = zVec; + } else { + const ValueType beta = rDotZ / rDotZPrev; + // p = beta * p + z + internal::axpy(beta, pVec, zVec, /*result */pVec); + } + + // q_{k} = A p_{k} + Amat.vectorMultiply(pVec, qVec); + + // alpha = / + const ValueType pAp = pVec.dot(qVec); + assert(std::isfinite(pAp)); + + const ValueType alpha = rDotZ / pAp; + rDotZPrev = rDotZ; + + // x_{k} = x_{k-1} + alpha * p_{k} + internal::axpy(alpha, pVec, xVec, /*result=*/xVec); + + // r_{k} = r_{k-1} - alpha_{k-1} A p_{k} + internal::axpy(-alpha, qVec, rVec, /*result=*/rVec); + + // update tolerances + l2Error = rVec.l2Norm(); + minL2Error = Min(l2Error, minL2Error); + + result.absoluteError = static_cast(rVec.infNorm()); + result.relativeError = static_cast(result.absoluteError / infNormOfB); + + if (l2Error > 2 * minL2Error) { + // The solution started to diverge. + result.success = false; + break; + } + if (!std::isfinite(result.absoluteError)) { + // Total divergence of solution + result.success = false; + break; + } + if (result.absoluteError <= termination.absoluteError) { + // Convergence + result.success = true; + break; + } + if (result.relativeError <= termination.relativeError) { + // Convergence + result.success = true; + break; + } + } + OPENVDB_LOG_DEBUG_RUNTIME("pcg::solve() " << result); + + return result; +} + +} // namespace pcg +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_CONJGRADIENT_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Coord.h b/openvdb/math/Coord.h new file mode 100644 index 00000000..504b5e9a --- /dev/null +++ b/openvdb/math/Coord.h @@ -0,0 +1,600 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_COORD_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_COORD_HAS_BEEN_INCLUDED + +#include // for std::hash +#include // for std::min(), std::max() +#include // for std::array +#include +#include +#include +#include "Math.h" +#include "Vec3.h" + +namespace tbb { class split; } // forward declaration + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief Signed (x, y, z) 32-bit integer coordinates +class Coord +{ +public: + using Int32 = int32_t; + using Index32 = uint32_t; + using Vec3i = Vec3; + using Vec3I = Vec3; + + using ValueType = Int32; + using Limits = std::numeric_limits; + + Coord(): mVec{{0, 0, 0}} {} + explicit Coord(Int32 xyz): mVec{{xyz, xyz, xyz}} {} + Coord(Int32 x, Int32 y, Int32 z): mVec{{x, y, z}} {} + explicit Coord(const Vec3i& v): mVec{{v[0], v[1], v[2]}} {} + explicit Coord(const Vec3I& v): mVec{{Int32(v[0]), Int32(v[1]), Int32(v[2])}} {} + explicit Coord(const Int32* v): mVec{{v[0], v[1], v[2]}} {} + + /// @brief Return the smallest possible coordinate + static Coord min() { return Coord(Limits::min()); } + + /// @brief Return the largest possible coordinate + static Coord max() { return Coord(Limits::max()); } + + /// @brief Return @a xyz rounded to the closest integer coordinates + /// (cell centered conversion). + template static Coord round(const Vec3& xyz) + { + return Coord(Int32(Round(xyz[0])), Int32(Round(xyz[1])), Int32(Round(xyz[2]))); + } + /// @brief Return the largest integer coordinates that are not greater + /// than @a xyz (node centered conversion). + template static Coord floor(const Vec3& xyz) + { + return Coord(Int32(Floor(xyz[0])), Int32(Floor(xyz[1])), Int32(Floor(xyz[2]))); + } + + /// @brief Return the largest integer coordinates that are not greater + /// than @a xyz+1 (node centered conversion). + template static Coord ceil(const Vec3& xyz) + { + return Coord(Int32(Ceil(xyz[0])), Int32(Ceil(xyz[1])), Int32(Ceil(xyz[2]))); + } + + /// @brief Reset all three coordinates with the specified arguments + Coord& reset(Int32 x, Int32 y, Int32 z) + { + mVec[0] = x; + mVec[1] = y; + mVec[2] = z; + return *this; + } + /// @brief Reset all three coordinates with the same specified argument + Coord& reset(Int32 xyz) { return this->reset(xyz, xyz, xyz); } + + Coord& setX(Int32 x) { mVec[0] = x; return *this; } + Coord& setY(Int32 y) { mVec[1] = y; return *this; } + Coord& setZ(Int32 z) { mVec[2] = z; return *this; } + + Coord& offset(Int32 dx, Int32 dy, Int32 dz) + { + mVec[0] += dx; + mVec[1] += dy; + mVec[2] += dz; + return *this; + } + Coord& offset(Int32 n) { return this->offset(n, n, n); } + Coord offsetBy(Int32 dx, Int32 dy, Int32 dz) const + { + return Coord(mVec[0] + dx, mVec[1] + dy, mVec[2] + dz); + } + Coord offsetBy(Int32 n) const { return offsetBy(n, n, n); } + + Coord& operator+=(const Coord& rhs) + { + mVec[0] += rhs[0]; + mVec[1] += rhs[1]; + mVec[2] += rhs[2]; + return *this; + } + Coord& operator-=(const Coord& rhs) + { + mVec[0] -= rhs[0]; + mVec[1] -= rhs[1]; + mVec[2] -= rhs[2]; + return *this; + } + Coord operator+(const Coord& rhs) const + { + return Coord(mVec[0] + rhs[0], mVec[1] + rhs[1], mVec[2] + rhs[2]); + } + Coord operator-(const Coord& rhs) const + { + return Coord(mVec[0] - rhs[0], mVec[1] - rhs[1], mVec[2] - rhs[2]); + } + Coord operator-() const { return Coord(-mVec[0], -mVec[1], -mVec[2]); } + + Coord operator>> (size_t n) const { return Coord(mVec[0]>>n, mVec[1]>>n, mVec[2]>>n); } + Coord operator<< (size_t n) const { return Coord(mVec[0]<>=(size_t n) { mVec[0]>>=n; mVec[1]>>=n; mVec[2]>>=n; return *this; } + Coord operator& (Int32 n) const { return Coord(mVec[0] & n, mVec[1] & n, mVec[2] & n); } + Coord operator| (Int32 n) const { return Coord(mVec[0] | n, mVec[1] | n, mVec[2] | n); } + Coord& operator&= (Int32 n) { mVec[0]&=n; mVec[1]&=n; mVec[2]&=n; return *this; } + Coord& operator|= (Int32 n) { mVec[0]|=n; mVec[1]|=n; mVec[2]|=n; return *this; } + + Int32 x() const { return mVec[0]; } + Int32 y() const { return mVec[1]; } + Int32 z() const { return mVec[2]; } + Int32 operator[](size_t i) const { assert(i < 3); return mVec[i]; } + Int32& x() { return mVec[0]; } + Int32& y() { return mVec[1]; } + Int32& z() { return mVec[2]; } + Int32& operator[](size_t i) { assert(i < 3); return mVec[i]; } + + const Int32* data() const { return mVec.data(); } + Int32* data() { return mVec.data(); } + const Int32* asPointer() const { return mVec.data(); } + Int32* asPointer() { return mVec.data(); } + Vec3d asVec3d() const { return Vec3d(double(mVec[0]), double(mVec[1]), double(mVec[2])); } + Vec3s asVec3s() const { return Vec3s(float(mVec[0]), float(mVec[1]), float(mVec[2])); } + Vec3i asVec3i() const { return Vec3i(mVec.data()); } + Vec3I asVec3I() const { return Vec3I(Index32(mVec[0]), Index32(mVec[1]), Index32(mVec[2])); } + void asXYZ(Int32& x, Int32& y, Int32& z) const { x = mVec[0]; y = mVec[1]; z = mVec[2]; } + + bool operator==(const Coord& rhs) const + { + return (mVec[0] == rhs.mVec[0] && mVec[1] == rhs.mVec[1] && mVec[2] == rhs.mVec[2]); + } + bool operator!=(const Coord& rhs) const { return !(*this == rhs); } + + /// Lexicographic less than + bool operator<(const Coord& rhs) const + { + return this->x() < rhs.x() ? true : this->x() > rhs.x() ? false + : this->y() < rhs.y() ? true : this->y() > rhs.y() ? false + : this->z() < rhs.z() ? true : false; + } + /// Lexicographic less than or equal to + bool operator<=(const Coord& rhs) const + { + return this->x() < rhs.x() ? true : this->x() > rhs.x() ? false + : this->y() < rhs.y() ? true : this->y() > rhs.y() ? false + : this->z() <=rhs.z() ? true : false; + } + /// Lexicographic greater than + bool operator>(const Coord& rhs) const { return !(*this <= rhs); } + /// Lexicographic greater than or equal to + bool operator>=(const Coord& rhs) const { return !(*this < rhs); } + + /// Perform a component-wise minimum with the other Coord. + void minComponent(const Coord& other) + { + mVec[0] = std::min(mVec[0], other.mVec[0]); + mVec[1] = std::min(mVec[1], other.mVec[1]); + mVec[2] = std::min(mVec[2], other.mVec[2]); + } + + /// Perform a component-wise maximum with the other Coord. + void maxComponent(const Coord& other) + { + mVec[0] = std::max(mVec[0], other.mVec[0]); + mVec[1] = std::max(mVec[1], other.mVec[1]); + mVec[2] = std::max(mVec[2], other.mVec[2]); + } + + /// Return the component-wise minimum of the two Coords. + static inline Coord minComponent(const Coord& lhs, const Coord& rhs) + { + return Coord(std::min(lhs.x(), rhs.x()), + std::min(lhs.y(), rhs.y()), + std::min(lhs.z(), rhs.z())); + } + + /// Return the component-wise maximum of the two Coords. + static inline Coord maxComponent(const Coord& lhs, const Coord& rhs) + { + return Coord(std::max(lhs.x(), rhs.x()), + std::max(lhs.y(), rhs.y()), + std::max(lhs.z(), rhs.z())); + } + + /// Return true if any of the components of @a a are smaller than the + /// corresponding components of @a b. + static inline bool lessThan(const Coord& a, const Coord& b) + { + return (a[0] < b[0] || a[1] < b[1] || a[2] < b[2]); + } + + /// @brief Return the index (0, 1 or 2) with the smallest value. + size_t minIndex() const { return MinIndex(mVec); } + + /// @brief Return the index (0, 1 or 2) with the largest value. + size_t maxIndex() const { return MaxIndex(mVec); } + + void read(std::istream& is) { is.read(reinterpret_cast(mVec.data()), sizeof(mVec)); } + void write(std::ostream& os) const + { + os.write(reinterpret_cast(mVec.data()), sizeof(mVec)); + } + + /// @brief Return a hash value for this coordinate + /// @note Log2N is the binary logarithm of the hash table size. + /// @details The hash function is taken from the SIGGRAPH paper: + /// "VDB: High-resolution sparse volumes with dynamic topology" + template + size_t hash() const + { + return ((1< mVec; +}; // class Coord + + +//////////////////////////////////////// + + +/// @brief Axis-aligned bounding box of signed integer coordinates +/// @note The range of the integer coordinates, [min, max], is inclusive. +/// Thus, a bounding box with min = max is not empty but rather encloses +/// a single coordinate. +class CoordBBox +{ +public: + using Index64 = uint64_t; + using ValueType = Coord::ValueType; + + /// @brief Iterator over the Coord domain covered by a CoordBBox + /// @note If ZYXOrder is @c true, @e z is the fastest-moving coordinate, + /// otherwise the traversal is in XYZ order (i.e., @e x is fastest-moving). + template + class Iterator + { + public: + /// @brief C-tor from a bounding box + Iterator(const CoordBBox& b): mPos(b.min()), mMin(b.min()), mMax(b.max()) {} + /// @brief Increment the iterator to point to the next coordinate. + /// @details Iteration stops one past the maximum coordinate + /// along the axis determined by the template parameter. + Iterator& operator++() { ZYXOrder ? next<2,1,0>() : next<0,1,2>(); return *this; } + /// @brief Return @c true if the iterator still points to a valid coordinate. + operator bool() const { return ZYXOrder ? (mPos[0] <= mMax[0]) : (mPos[2] <= mMax[2]); } + /// @brief Return a const reference to the coordinate currently pointed to. + const Coord& operator*() const { return mPos; } + /// Return @c true if this iterator and the given iterator point to the same coordinate. + bool operator==(const Iterator& other) const + { + return ((mPos == other.mPos) && (mMin == other.mMin) && (mMax == other.mMax)); + } + /// Return @c true if this iterator and the given iterator point to different coordinates. + bool operator!=(const Iterator& other) const { return !(*this == other); } + private: + template + void next() + { + if (mPos[a] < mMax[a]) { ++mPos[a]; } // this is the most common case + else if (mPos[b] < mMax[b]) { mPos[a] = mMin[a]; ++mPos[b]; } + else if (mPos[c] <= mMax[c]) { mPos[a] = mMin[a]; mPos[b] = mMin[b]; ++mPos[c]; } + } + Coord mPos, mMin, mMax; + friend class CoordBBox; // for CoordBBox::end() + };// CoordBBox::Iterator + + using ZYXIterator = Iterator; + using XYZIterator = Iterator; + + /// @brief The default constructor produces an empty bounding box. + CoordBBox(): mMin(Coord::max()), mMax(Coord::min()) {} + /// @brief Construct a bounding box with the given @a min and @a max bounds. + CoordBBox(const Coord& min, const Coord& max): mMin(min), mMax(max) {} + /// @brief Construct from individual components of the min and max bounds. + CoordBBox(ValueType xMin, ValueType yMin, ValueType zMin, + ValueType xMax, ValueType yMax, ValueType zMax) + : mMin(xMin, yMin, zMin), mMax(xMax, yMax, zMax) + { + } + /// @brief Splitting constructor for use in TBB ranges + /// @note The other bounding box is assumed to be divisible. + CoordBBox(CoordBBox& other, const tbb::split&): mMin(other.mMin), mMax(other.mMax) + { + assert(this->is_divisible()); + const size_t n = this->maxExtent(); + mMax[n] = (mMin[n] + mMax[n]) >> 1; + other.mMin[n] = mMax[n] + 1; + } + + static CoordBBox createCube(const Coord& min, ValueType dim) + { + return CoordBBox(min, min.offsetBy(dim - 1)); + } + + /// Return an "infinite" bounding box, as defined by the Coord value range. + static CoordBBox inf() { return CoordBBox(Coord::min(), Coord::max()); } + + const Coord& min() const { return mMin; } + const Coord& max() const { return mMax; } + + Coord& min() { return mMin; } + Coord& max() { return mMax; } + + void reset() { mMin = Coord::max(); mMax = Coord::min(); } + void reset(const Coord& min, const Coord& max) { mMin = min; mMax = max; } + void resetToCube(const Coord& min, ValueType dim) { mMin = min; mMax = min.offsetBy(dim - 1); } + + /// @brief Return the minimum coordinate. + /// @note The start coordinate is inclusive. + Coord getStart() const { return mMin; } + /// @brief Return the maximum coordinate plus one. + /// @note This end coordinate is exclusive. + Coord getEnd() const { return mMax.offsetBy(1); } + + /// @brief Return a ZYX-order iterator that points to the minimum coordinate. + ZYXIterator begin() const { return ZYXIterator{*this}; } + /// @brief Return a ZYX-order iterator that points to the minimum coordinate. + ZYXIterator beginZYX() const { return ZYXIterator{*this}; } + /// @brief Return an XYZ-order iterator that points to the minimum coordinate. + XYZIterator beginXYZ() const { return XYZIterator{*this}; } + + /// @brief Return a ZYX-order iterator that points past the maximum coordinate. + ZYXIterator end() const { ZYXIterator it{*this}; it.mPos[0] = mMax[0] + 1; return it; } + /// @brief Return a ZYX-order iterator that points past the maximum coordinate. + ZYXIterator endZYX() const { return end(); } + /// @brief Return an XYZ-order iterator that points past the maximum coordinate. + XYZIterator endXYZ() const { XYZIterator it{*this}; it.mPos[2] = mMax[2] + 1; return it; } + + bool operator==(const CoordBBox& rhs) const { return mMin == rhs.mMin && mMax == rhs.mMax; } + bool operator!=(const CoordBBox& rhs) const { return !(*this == rhs); } + + /// @brief Return @c true if this bounding box is empty (i.e., encloses no coordinates). + bool empty() const + { +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-overflow" +#endif + return (mMin[0] > mMax[0] || mMin[1] > mMax[1] || mMin[2] > mMax[2]); +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) + #pragma GCC diagnostic pop +#endif + } + /// @brief Return @c true if this bounding box is nonempty + /// (i.e., encloses at least one coordinate). + operator bool() const { return !this->empty(); } + /// @brief Return @c true if this bounding box is nonempty + /// (i.e., encloses at least one coordinate). + bool hasVolume() const { return !this->empty(); } + + /// @brief Return the floating-point position of the center of this bounding box. + Vec3d getCenter() const { return 0.5 * Vec3d((mMin + mMax).asPointer()); } + + /// @brief Return the dimensions of the coordinates spanned by this bounding box. + /// @note Since coordinates are inclusive, a bounding box with min = max + /// has dimensions of (1, 1, 1). + Coord dim() const { return empty() ? Coord(0) : (mMax.offsetBy(1) - mMin); } + /// @todo deprecate - use dim instead + Coord extents() const { return this->dim(); } + /// @brief Return the integer volume of coordinates spanned by this bounding box. + /// @note Since coordinates are inclusive, a bounding box with min = max has volume one. + Index64 volume() const + { + const Coord d = this->dim(); + return Index64(d[0]) * Index64(d[1]) * Index64(d[2]); + } + /// @brief Return @c true if this bounding box can be subdivided [mainly for use by TBB]. + bool is_divisible() const { return mMin[0]dim().minIndex(); } + + /// @brief Return the index (0, 1 or 2) of the longest axis. + size_t maxExtent() const { return this->dim().maxIndex(); } + + /// @brief Return @c true if point (x, y, z) is inside this bounding box. + bool isInside(const Coord& xyz) const + { + return !(Coord::lessThan(xyz,mMin) || Coord::lessThan(mMax,xyz)); + } + + /// @brief Return @c true if the given bounding box is inside this bounding box. + bool isInside(const CoordBBox& b) const + { + return !(Coord::lessThan(b.mMin,mMin) || Coord::lessThan(mMax,b.mMax)); + } + + /// @brief Return @c true if the given bounding box overlaps with this bounding box. + bool hasOverlap(const CoordBBox& b) const + { + return !(Coord::lessThan(mMax,b.mMin) || Coord::lessThan(b.mMax,mMin)); + } + + /// @brief Pad this bounding box with the specified padding. + void expand(ValueType padding) + { + mMin.offset(-padding); + mMax.offset( padding); + } + + /// @brief Return a new instance that is expanded by the specified padding. + CoordBBox expandBy(ValueType padding) const + { + return CoordBBox(mMin.offsetBy(-padding),mMax.offsetBy(padding)); + } + + /// @brief Expand this bounding box to enclose point (x, y, z). + void expand(const Coord& xyz) + { + mMin.minComponent(xyz); + mMax.maxComponent(xyz); + } + + /// @brief Union this bounding box with the given bounding box. + void expand(const CoordBBox& bbox) + { + mMin.minComponent(bbox.min()); + mMax.maxComponent(bbox.max()); + } + /// @brief Intersect this bounding box with the given bounding box. + void intersect(const CoordBBox& bbox) + { + mMin.maxComponent(bbox.min()); + mMax.minComponent(bbox.max()); + } + /// @brief Union this bounding box with the cubical bounding box + /// of the given size and with the given minimum coordinates. + void expand(const Coord& min, Coord::ValueType dim) + { + mMin.minComponent(min); + mMax.maxComponent(min.offsetBy(dim-1)); + } + /// @brief Translate this bounding box by + /// (tx, ty, tz). + void translate(const Coord& t) { mMin += t; mMax += t; } + + /// @brief Move this bounding box to the specified min + void moveMin(const Coord& min) { mMax += min - mMin; mMin = min; } + + /// @brief Move this bounding box to the specified max + void moveMax(const Coord& max) { mMin += max - mMax; mMax = max; } + + /// @brief Populates an array with the eight corner points of this bounding box. + /// @details The ordering of the corner points is lexicographic. + /// @warning It is assumed that the pointer can be incremented at + /// least seven times, i.e. has storage for eight Coord elements! + void getCornerPoints(Coord *p) const + { + assert(p != nullptr); + p->reset(mMin.x(), mMin.y(), mMin.z()); ++p; + p->reset(mMin.x(), mMin.y(), mMax.z()); ++p; + p->reset(mMin.x(), mMax.y(), mMin.z()); ++p; + p->reset(mMin.x(), mMax.y(), mMax.z()); ++p; + p->reset(mMax.x(), mMin.y(), mMin.z()); ++p; + p->reset(mMax.x(), mMin.y(), mMax.z()); ++p; + p->reset(mMax.x(), mMax.y(), mMin.z()); ++p; + p->reset(mMax.x(), mMax.y(), mMax.z()); + } + + //@{ + /// @brief Bit-wise operations performed on both the min and max members + CoordBBox operator>> (size_t n) const { return CoordBBox(mMin>>n, mMax>>n); } + CoordBBox operator<< (size_t n) const { return CoordBBox(mMin<>=(size_t n) { mMin >>= n; mMax >>= n; return *this; } + CoordBBox operator& (Coord::Int32 n) const { return CoordBBox(mMin & n, mMax & n); } + CoordBBox operator| (Coord::Int32 n) const { return CoordBBox(mMin | n, mMax | n); } + CoordBBox& operator&= (Coord::Int32 n) { mMin &= n; mMax &= n; return *this; } + CoordBBox& operator|= (Coord::Int32 n) { mMin |= n; mMax |= n; return *this; } + //@} + + /// @brief Unserialize this bounding box from the given stream. + void read(std::istream& is) { mMin.read(is); mMax.read(is); } + /// @brief Serialize this bounding box to the given stream. + void write(std::ostream& os) const { mMin.write(os); mMax.write(os); } + +private: + Coord mMin, mMax; +}; // class CoordBBox + + +//////////////////////////////////////// + + +inline std::ostream& operator<<(std::ostream& os, const Coord& xyz) +{ + os << xyz.asVec3i(); return os; +} + + +inline Coord +Abs(const Coord& xyz) +{ + return Coord(Abs(xyz[0]), Abs(xyz[1]), Abs(xyz[2])); +} + + +//@{ +/// Allow a Coord to be added to or subtracted from a Vec3. +template +inline Vec3::type> +operator+(const Vec3& v0, const Coord& v1) +{ + Vec3::type> result(v0); + result[0] += v1[0]; + result[1] += v1[1]; + result[2] += v1[2]; + return result; +} + +template +inline Vec3::type> +operator+(const Coord& v1, const Vec3& v0) +{ + Vec3::type> result(v0); + result[0] += v1[0]; + result[1] += v1[1]; + result[2] += v1[2]; + return result; +} +//@} + + +//@{ +/// Allow a Coord to be subtracted from a Vec3. +template +inline Vec3::type> +operator-(const Vec3& v0, const Coord& v1) +{ + Vec3::type> result(v0); + result[0] -= v1[0]; + result[1] -= v1[1]; + result[2] -= v1[2]; + return result; +} + +template +inline Vec3::type> +operator-(const Coord& v1, const Vec3& v0) +{ + Vec3::type> result(v0); + result[0] -= v1[0]; + result[1] -= v1[1]; + result[2] -= v1[2]; + return -result; +} +//@} + +inline std::ostream& +operator<<(std::ostream& os, const CoordBBox& b) +{ + os << b.min() << " -> " << b.max(); + return os; +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +//////////////////////////////////////// + +// template specialization of std::hash with Coord, which +// allows for Coord to be used as the key in std::unordered_map +namespace std {// injected in namespace std + +template<> +struct hash +{ + using Coord = openvdb::math::Coord; + using argument_type = Coord; + using result_type = std::size_t; + std::size_t operator()(const Coord& ijk) const noexcept { return ijk.Coord::hash<>(); } +};// std::hash + +}// namespace std + +#endif // OPENVDB_MATH_COORD_HAS_BEEN_INCLUDED diff --git a/openvdb/math/DDA.h b/openvdb/math/DDA.h new file mode 100644 index 00000000..8fb2b88f --- /dev/null +++ b/openvdb/math/DDA.h @@ -0,0 +1,344 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file DDA.h +/// +/// @author Ken Museth +/// +/// @brief Digital Differential Analyzers specialized for VDB. + +#ifndef OPENVDB_MATH_DDA_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_DDA_HAS_BEEN_INCLUDED + +#include "Coord.h" +#include "Math.h" +#include "Vec3.h" +#include +#include // for std::ostream +#include // for std::numeric_limits::max() +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief A Digital Differential Analyzer specialized for OpenVDB grids +/// @note Conceptually similar to Bresenham's line algorithm applied +/// to a 3D Ray intersecting OpenVDB nodes or voxels. Log2Dim = 0 +/// corresponds to a voxel and Log2Dim a tree node of size 2^Log2Dim. +/// +/// @note The Ray template class is expected to have the following +/// methods: test(time), t0(), t1(), invDir(), and operator()(time). +/// See the example Ray class above for their definition. +template +class DDA +{ +public: + using RealType = typename RayT::RealType; + using RealT = RealType; + using Vec3Type = typename RayT::Vec3Type; + using Vec3T = Vec3Type; + + /// @brief uninitialized constructor + DDA() {} + + DDA(const RayT& ray) { this->init(ray); } + + DDA(const RayT& ray, RealT startTime) { this->init(ray, startTime); } + + DDA(const RayT& ray, RealT startTime, RealT maxTime) { this->init(ray, startTime, maxTime); } + + inline void init(const RayT& ray, RealT startTime, RealT maxTime) + { + assert(startTime <= maxTime); + static const int DIM = 1 << Log2Dim; + mT0 = startTime; + mT1 = maxTime; + const Vec3T &pos = ray(mT0), &dir = ray.dir(), &inv = ray.invDir(); + mVoxel = Coord::floor(pos) & (~(DIM-1)); + for (int axis = 0; axis < 3; ++axis) { + if (math::isZero(dir[axis])) {//handles dir = +/- 0 + mStep[axis] = 0;//dummy value + mNext[axis] = std::numeric_limits::max();//i.e. disabled! + mDelta[axis] = std::numeric_limits::max();//dummy value + } else if (inv[axis] > 0) { + mStep[axis] = DIM; + mNext[axis] = mT0 + (mVoxel[axis] + DIM - pos[axis]) * inv[axis]; + mDelta[axis] = mStep[axis] * inv[axis]; + } else { + mStep[axis] = -DIM; + mNext[axis] = mT0 + (mVoxel[axis] - pos[axis]) * inv[axis]; + mDelta[axis] = mStep[axis] * inv[axis]; + } + } + } + + inline void init(const RayT& ray) { this->init(ray, ray.t0(), ray.t1()); } + + inline void init(const RayT& ray, RealT startTime) { this->init(ray, startTime, ray.t1()); } + + /// @brief Increment the voxel index to next intersected voxel or node + /// and returns true if the step in time does not exceed maxTime. + inline bool step() + { + const int stepAxis = static_cast(math::MinIndex(mNext)); + mT0 = mNext[stepAxis]; + mNext[stepAxis] += mDelta[stepAxis]; + mVoxel[stepAxis] += mStep[stepAxis]; + return mT0 <= mT1; + } + + /// @brief Return the index coordinates of the next node or voxel + /// intersected by the ray. If Log2Dim = 0 the return value is the + /// actual signed coordinate of the voxel, else it is the origin + /// of the corresponding VDB tree node or tile. + /// @note Incurs no computational overhead. + inline const Coord& voxel() const { return mVoxel; } + + /// @brief Return the time (parameterized along the Ray) of the + /// first hit of a tree node of size 2^Log2Dim. + /// @details This value is initialized to startTime or ray.t0() + /// depending on the constructor used. + /// @note Incurs no computational overhead. + inline RealType time() const { return mT0; } + + /// @brief Return the maximum time (parameterized along the Ray). + inline RealType maxTime() const { return mT1; } + + /// @brief Return the time (parameterized along the Ray) of the + /// second (i.e. next) hit of a tree node of size 2^Log2Dim. + /// @note Incurs a (small) computational overhead. + inline RealType next() const { return math::Min(mT1, mNext[0], mNext[1], mNext[2]); } + + /// @brief Print information about this DDA for debugging. + /// @param os a stream to which to write textual information. + void print(std::ostream& os = std::cout) const + { + os << "Dim=" << (1< +#include +#include + +#ifdef DWA_OPENVDB +#include +#endif + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + + +//////////////////////////////////////// + + +/// @brief Different discrete schemes used in the first derivatives. +// Add new items to the *end* of this list, and update NUM_DS_SCHEMES. +enum DScheme { + UNKNOWN_DS = -1, + CD_2NDT = 0, // center difference, 2nd order, but the result must be divided by 2 + CD_2ND, // center difference, 2nd order + CD_4TH, // center difference, 4th order + CD_6TH, // center difference, 6th order + FD_1ST, // forward difference, 1st order + FD_2ND, // forward difference, 2nd order + FD_3RD, // forward difference, 3rd order + BD_1ST, // backward difference, 1st order + BD_2ND, // backward difference, 2nd order + BD_3RD, // backward difference, 3rd order + FD_WENO5, // forward difference, weno5 + BD_WENO5, // backward difference, weno5 + FD_HJWENO5, // forward differene, HJ-weno5 + BD_HJWENO5 // backward difference, HJ-weno5 +}; + +enum { NUM_DS_SCHEMES = BD_HJWENO5 + 1 }; + + +inline std::string +dsSchemeToString(DScheme dss) +{ + std::string ret; + switch (dss) { + case UNKNOWN_DS: ret = "unknown_ds"; break; + case CD_2NDT: ret = "cd_2ndt"; break; + case CD_2ND: ret = "cd_2nd"; break; + case CD_4TH: ret = "cd_4th"; break; + case CD_6TH: ret = "cd_6th"; break; + case FD_1ST: ret = "fd_1st"; break; + case FD_2ND: ret = "fd_2nd"; break; + case FD_3RD: ret = "fd_3rd"; break; + case BD_1ST: ret = "bd_1st"; break; + case BD_2ND: ret = "bd_2nd"; break; + case BD_3RD: ret = "bd_3rd"; break; + case FD_WENO5: ret = "fd_weno5"; break; + case BD_WENO5: ret = "bd_weno5"; break; + case FD_HJWENO5: ret = "fd_hjweno5"; break; + case BD_HJWENO5: ret = "bd_hjweno5"; break; + } + return ret; +} + +inline DScheme +stringToDScheme(const std::string& s) +{ + DScheme ret = UNKNOWN_DS; + + std::string str = s; + boost::trim(str); + boost::to_lower(str); + + if (str == dsSchemeToString(CD_2NDT)) { + ret = CD_2NDT; + } else if (str == dsSchemeToString(CD_2ND)) { + ret = CD_2ND; + } else if (str == dsSchemeToString(CD_4TH)) { + ret = CD_4TH; + } else if (str == dsSchemeToString(CD_6TH)) { + ret = CD_6TH; + } else if (str == dsSchemeToString(FD_1ST)) { + ret = FD_1ST; + } else if (str == dsSchemeToString(FD_2ND)) { + ret = FD_2ND; + } else if (str == dsSchemeToString(FD_3RD)) { + ret = FD_3RD; + } else if (str == dsSchemeToString(BD_1ST)) { + ret = BD_1ST; + } else if (str == dsSchemeToString(BD_2ND)) { + ret = BD_2ND; + } else if (str == dsSchemeToString(BD_3RD)) { + ret = BD_3RD; + } else if (str == dsSchemeToString(FD_WENO5)) { + ret = FD_WENO5; + } else if (str == dsSchemeToString(BD_WENO5)) { + ret = BD_WENO5; + } else if (str == dsSchemeToString(FD_HJWENO5)) { + ret = FD_HJWENO5; + } else if (str == dsSchemeToString(BD_HJWENO5)) { + ret = BD_HJWENO5; + } + + return ret; +} + +inline std::string +dsSchemeToMenuName(DScheme dss) +{ + std::string ret; + switch (dss) { + case UNKNOWN_DS: ret = "Unknown DS scheme"; break; + case CD_2NDT: ret = "Twice 2nd-order center difference"; break; + case CD_2ND: ret = "2nd-order center difference"; break; + case CD_4TH: ret = "4th-order center difference"; break; + case CD_6TH: ret = "6th-order center difference"; break; + case FD_1ST: ret = "1st-order forward difference"; break; + case FD_2ND: ret = "2nd-order forward difference"; break; + case FD_3RD: ret = "3rd-order forward difference"; break; + case BD_1ST: ret = "1st-order backward difference"; break; + case BD_2ND: ret = "2nd-order backward difference"; break; + case BD_3RD: ret = "3rd-order backward difference"; break; + case FD_WENO5: ret = "5th-order WENO forward difference"; break; + case BD_WENO5: ret = "5th-order WENO backward difference"; break; + case FD_HJWENO5: ret = "5th-order HJ-WENO forward difference"; break; + case BD_HJWENO5: ret = "5th-order HJ-WENO backward difference"; break; + } + return ret; +} + + + +//////////////////////////////////////// + + +/// @brief Different discrete schemes used in the second derivatives. +// Add new items to the *end* of this list, and update NUM_DD_SCHEMES. +enum DDScheme { + UNKNOWN_DD = -1, + CD_SECOND = 0, // center difference, 2nd order + CD_FOURTH, // center difference, 4th order + CD_SIXTH // center difference, 6th order +}; + +enum { NUM_DD_SCHEMES = CD_SIXTH + 1 }; + + +//////////////////////////////////////// + + +/// @brief Biased Gradients are limited to non-centered differences +// Add new items to the *end* of this list, and update NUM_BIAS_SCHEMES. +enum BiasedGradientScheme { + UNKNOWN_BIAS = -1, + FIRST_BIAS = 0, // uses FD_1ST & BD_1ST + SECOND_BIAS, // uses FD_2ND & BD_2ND + THIRD_BIAS, // uses FD_3RD & BD_3RD + WENO5_BIAS, // uses WENO5 + HJWENO5_BIAS // uses HJWENO5 +}; + +enum { NUM_BIAS_SCHEMES = HJWENO5_BIAS + 1 }; + +inline std::string +biasedGradientSchemeToString(BiasedGradientScheme bgs) +{ + std::string ret; + switch (bgs) { + case UNKNOWN_BIAS: ret = "unknown_bias"; break; + case FIRST_BIAS: ret = "first_bias"; break; + case SECOND_BIAS: ret = "second_bias"; break; + case THIRD_BIAS: ret = "third_bias"; break; + case WENO5_BIAS: ret = "weno5_bias"; break; + case HJWENO5_BIAS: ret = "hjweno5_bias"; break; + } + return ret; +} + +inline BiasedGradientScheme +stringToBiasedGradientScheme(const std::string& s) +{ + BiasedGradientScheme ret = UNKNOWN_BIAS; + + std::string str = s; + boost::trim(str); + boost::to_lower(str); + + if (str == biasedGradientSchemeToString(FIRST_BIAS)) { + ret = FIRST_BIAS; + } else if (str == biasedGradientSchemeToString(SECOND_BIAS)) { + ret = SECOND_BIAS; + } else if (str == biasedGradientSchemeToString(THIRD_BIAS)) { + ret = THIRD_BIAS; + } else if (str == biasedGradientSchemeToString(WENO5_BIAS)) { + ret = WENO5_BIAS; + } else if (str == biasedGradientSchemeToString(HJWENO5_BIAS)) { + ret = HJWENO5_BIAS; + } + return ret; +} + +inline std::string +biasedGradientSchemeToMenuName(BiasedGradientScheme bgs) +{ + std::string ret; + switch (bgs) { + case UNKNOWN_BIAS: ret = "Unknown biased gradient"; break; + case FIRST_BIAS: ret = "1st-order biased gradient"; break; + case SECOND_BIAS: ret = "2nd-order biased gradient"; break; + case THIRD_BIAS: ret = "3rd-order biased gradient"; break; + case WENO5_BIAS: ret = "5th-order WENO biased gradient"; break; + case HJWENO5_BIAS: ret = "5th-order HJ-WENO biased gradient"; break; + } + return ret; +} + +//////////////////////////////////////// + + +/// @brief Temporal integration schemes +// Add new items to the *end* of this list, and update NUM_TEMPORAL_SCHEMES. +enum TemporalIntegrationScheme { + UNKNOWN_TIS = -1, + TVD_RK1,//same as explicit Euler integration + TVD_RK2, + TVD_RK3 +}; + +enum { NUM_TEMPORAL_SCHEMES = TVD_RK3 + 1 }; + +inline std::string +temporalIntegrationSchemeToString(TemporalIntegrationScheme tis) +{ + std::string ret; + switch (tis) { + case UNKNOWN_TIS: ret = "unknown_tis"; break; + case TVD_RK1: ret = "tvd_rk1"; break; + case TVD_RK2: ret = "tvd_rk2"; break; + case TVD_RK3: ret = "tvd_rk3"; break; + } + return ret; +} + +inline TemporalIntegrationScheme +stringToTemporalIntegrationScheme(const std::string& s) +{ + TemporalIntegrationScheme ret = UNKNOWN_TIS; + + std::string str = s; + boost::trim(str); + boost::to_lower(str); + + if (str == temporalIntegrationSchemeToString(TVD_RK1)) { + ret = TVD_RK1; + } else if (str == temporalIntegrationSchemeToString(TVD_RK2)) { + ret = TVD_RK2; + } else if (str == temporalIntegrationSchemeToString(TVD_RK3)) { + ret = TVD_RK3; + } + + return ret; +} + +inline std::string +temporalIntegrationSchemeToMenuName(TemporalIntegrationScheme tis) +{ + std::string ret; + switch (tis) { + case UNKNOWN_TIS: ret = "Unknown temporal integration"; break; + case TVD_RK1: ret = "Forward Euler"; break; + case TVD_RK2: ret = "2nd-order Runge-Kutta"; break; + case TVD_RK3: ret = "3rd-order Runge-Kutta"; break; + } + return ret; +} + + +//@} + + +/// @brief Implementation of nominally fifth-order finite-difference WENO +/// @details This function returns the numerical flux. See "High Order Finite Difference and +/// Finite Volume WENO Schemes and Discontinuous Galerkin Methods for CFD" - Chi-Wang Shu +/// ICASE Report No 2001-11 (page 6). Also see ICASE No 97-65 for a more complete reference +/// (Shu, 1997). +/// Given v1 = f(x-2dx), v2 = f(x-dx), v3 = f(x), v4 = f(x+dx) and v5 = f(x+2dx), +/// return an interpolated value f(x+dx/2) with the special property that +/// ( f(x+dx/2) - f(x-dx/2) ) / dx = df/dx (x) + error, +/// where the error is fifth-order in smooth regions: O(dx) <= error <=O(dx^5) +template +inline ValueType +WENO5(const ValueType& v1, const ValueType& v2, const ValueType& v3, + const ValueType& v4, const ValueType& v5, float scale2 = 0.01f) +{ + const double C = 13.0 / 12.0; + // WENO is formulated for non-dimensional equations, here the optional scale2 + // is a reference value (squared) for the function being interpolated. For + // example if 'v' is of order 1000, then scale2 = 10^6 is ok. But in practice + // leave scale2 = 1. + const double eps = 1.0e-6 * static_cast(scale2); + // {\tilde \omega_k} = \gamma_k / ( \beta_k + \epsilon)^2 in Shu's ICASE report) + const double A1=0.1/math::Pow2(C*math::Pow2(v1-2*v2+v3)+0.25*math::Pow2(v1-4*v2+3.0*v3)+eps), + A2=0.6/math::Pow2(C*math::Pow2(v2-2*v3+v4)+0.25*math::Pow2(v2-v4)+eps), + A3=0.3/math::Pow2(C*math::Pow2(v3-2*v4+v5)+0.25*math::Pow2(3.0*v3-4*v4+v5)+eps); + + return static_cast(static_cast( + A1*(2.0*v1 - 7.0*v2 + 11.0*v3) + + A2*(5.0*v3 - v2 + 2.0*v4) + + A3*(2.0*v3 + 5.0*v4 - v5))/(6.0*(A1+A2+A3))); +} + + +template +inline Real GodunovsNormSqrd(bool isOutside, + Real dP_xm, Real dP_xp, + Real dP_ym, Real dP_yp, + Real dP_zm, Real dP_zp) +{ + using math::Max; + using math::Min; + using math::Pow2; + + const Real zero(0); + Real dPLen2; + if (isOutside) { // outside + dPLen2 = Max(Pow2(Max(dP_xm, zero)), Pow2(Min(dP_xp,zero))); // (dP/dx)2 + dPLen2 += Max(Pow2(Max(dP_ym, zero)), Pow2(Min(dP_yp,zero))); // (dP/dy)2 + dPLen2 += Max(Pow2(Max(dP_zm, zero)), Pow2(Min(dP_zp,zero))); // (dP/dz)2 + } else { // inside + dPLen2 = Max(Pow2(Min(dP_xm, zero)), Pow2(Max(dP_xp,zero))); // (dP/dx)2 + dPLen2 += Max(Pow2(Min(dP_ym, zero)), Pow2(Max(dP_yp,zero))); // (dP/dy)2 + dPLen2 += Max(Pow2(Min(dP_zm, zero)), Pow2(Max(dP_zp,zero))); // (dP/dz)2 + } + return dPLen2; // |\nabla\phi|^2 +} + + +template +inline Real +GodunovsNormSqrd(bool isOutside, const Vec3& gradient_m, const Vec3& gradient_p) +{ + return GodunovsNormSqrd(isOutside, + gradient_m[0], gradient_p[0], + gradient_m[1], gradient_p[1], + gradient_m[2], gradient_p[2]); +} + + +#ifdef DWA_OPENVDB +inline simd::Float4 simdMin(const simd::Float4& a, const simd::Float4& b) { + return simd::Float4(_mm_min_ps(a.base(), b.base())); +} +inline simd::Float4 simdMax(const simd::Float4& a, const simd::Float4& b) { + return simd::Float4(_mm_max_ps(a.base(), b.base())); +} + +inline float simdSum(const simd::Float4& v); + +inline simd::Float4 Pow2(const simd::Float4& v) { return v * v; } + +template<> +inline simd::Float4 +WENO5(const simd::Float4& v1, const simd::Float4& v2, const simd::Float4& v3, + const simd::Float4& v4, const simd::Float4& v5, float scale2) +{ + using math::Pow2; + using F4 = simd::Float4; + const F4 + C(13.f / 12.f), + eps(1.0e-6f * scale2), + two(2.0), three(3.0), four(4.0), five(5.0), fourth(0.25), + A1 = F4(0.1f) / Pow2(C*Pow2(v1-two*v2+v3) + fourth*Pow2(v1-four*v2+three*v3) + eps), + A2 = F4(0.6f) / Pow2(C*Pow2(v2-two*v3+v4) + fourth*Pow2(v2-v4) + eps), + A3 = F4(0.3f) / Pow2(C*Pow2(v3-two*v4+v5) + fourth*Pow2(three*v3-four*v4+v5) + eps); + return (A1 * (two * v1 - F4(7.0) * v2 + F4(11.0) * v3) + + A2 * (five * v3 - v2 + two * v4) + + A3 * (two * v3 + five * v4 - v5)) / (F4(6.0) * (A1 + A2 + A3)); +} + + +inline float +simdSum(const simd::Float4& v) +{ + // temp = { v3+v3, v2+v2, v1+v3, v0+v2 } + __m128 temp = _mm_add_ps(v.base(), _mm_movehl_ps(v.base(), v.base())); + // temp = { v3+v3, v2+v2, v1+v3, (v0+v2)+(v1+v3) } + temp = _mm_add_ss(temp, _mm_shuffle_ps(temp, temp, 1)); + return _mm_cvtss_f32(temp); +} + +inline float +GodunovsNormSqrd(bool isOutside, const simd::Float4& dP_m, const simd::Float4& dP_p) +{ + const simd::Float4 zero(0.0); + simd::Float4 v = isOutside + ? simdMax(math::Pow2(simdMax(dP_m, zero)), math::Pow2(simdMin(dP_p, zero))) + : simdMax(math::Pow2(simdMin(dP_m, zero)), math::Pow2(simdMax(dP_p, zero))); + return simdSum(v);//should be v[0]+v[1]+v[2] +} +#endif + + +template +struct D1 +{ + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk); + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk); + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk); + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S); + + template + static typename Stencil::ValueType inY(const Stencil& S); + + template + static typename Stencil::ValueType inZ(const Stencil& S); +}; + +template<> +struct D1 +{ + // the difference opperator + template + static ValueType difference(const ValueType& xp1, const ValueType& xm1) { + return xp1 - xm1; + } + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(1, 0, 0)), + grid.getValue(ijk.offsetBy(-1, 0, 0))); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(0, 1, 0)), + grid.getValue(ijk.offsetBy( 0, -1, 0))); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(0, 0, 1)), + grid.getValue(ijk.offsetBy( 0, 0, -1))); + } + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference( S.template getValue< 1, 0, 0>(), S.template getValue<-1, 0, 0>()); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference( S.template getValue< 0, 1, 0>(), S.template getValue< 0,-1, 0>()); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference( S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0,-1>()); + } +}; + +template<> +struct D1 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp1, const ValueType& xm1) { + return (xp1 - xm1)*ValueType(0.5); + } + + + // random access + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(1, 0, 0)), + grid.getValue(ijk.offsetBy(-1, 0, 0))); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(0, 1, 0)), + grid.getValue(ijk.offsetBy( 0, -1, 0))); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(0, 0, 1)), + grid.getValue(ijk.offsetBy( 0, 0, -1))); + } + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference(S.template getValue< 1, 0, 0>(), S.template getValue<-1, 0, 0>()); + } + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference(S.template getValue< 0, 1, 0>(), S.template getValue< 0,-1, 0>()); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference(S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0,-1>()); + } + +}; + +template<> +struct D1 +{ + + // the difference opperator + template + static ValueType difference( const ValueType& xp2, const ValueType& xp1, + const ValueType& xm1, const ValueType& xm2 ) { + return ValueType(2./3.)*(xp1 - xm1) + ValueType(1./12.)*(xm2 - xp2) ; + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy( 2,0,0)), grid.getValue(ijk.offsetBy( 1,0,0)), + grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk.offsetBy(-2,0,0)) ); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + + return difference( + grid.getValue(ijk.offsetBy( 0, 2, 0)), grid.getValue(ijk.offsetBy( 0, 1, 0)), + grid.getValue(ijk.offsetBy( 0,-1, 0)), grid.getValue(ijk.offsetBy( 0,-2, 0)) ); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + + return difference( + grid.getValue(ijk.offsetBy( 0, 0, 2)), grid.getValue(ijk.offsetBy( 0, 0, 1)), + grid.getValue(ijk.offsetBy( 0, 0,-1)), grid.getValue(ijk.offsetBy( 0, 0,-2)) ); + } + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference( S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), + S.template getValue<-1, 0, 0>(), + S.template getValue<-2, 0, 0>() ); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference( S.template getValue< 0, 2, 0>(), + S.template getValue< 0, 1, 0>(), + S.template getValue< 0,-1, 0>(), + S.template getValue< 0,-2, 0>() ); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference( S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0,-1>(), + S.template getValue< 0, 0,-2>() ); + } +}; + +template<> +struct D1 +{ + + // the difference opperator + template + static ValueType difference( const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, + const ValueType& xm1, const ValueType& xm2, const ValueType& xm3 ) + { + return ValueType(3./4.)*(xp1 - xm1) - ValueType(0.15)*(xp2 - xm2) + + ValueType(1./60.)*(xp3-xm3); + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy( 3,0,0)), grid.getValue(ijk.offsetBy( 2,0,0)), + grid.getValue(ijk.offsetBy( 1,0,0)), grid.getValue(ijk.offsetBy(-1,0,0)), + grid.getValue(ijk.offsetBy(-2,0,0)), grid.getValue(ijk.offsetBy(-3,0,0))); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy( 0, 3, 0)), grid.getValue(ijk.offsetBy( 0, 2, 0)), + grid.getValue(ijk.offsetBy( 0, 1, 0)), grid.getValue(ijk.offsetBy( 0,-1, 0)), + grid.getValue(ijk.offsetBy( 0,-2, 0)), grid.getValue(ijk.offsetBy( 0,-3, 0))); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy( 0, 0, 3)), grid.getValue(ijk.offsetBy( 0, 0, 2)), + grid.getValue(ijk.offsetBy( 0, 0, 1)), grid.getValue(ijk.offsetBy( 0, 0,-1)), + grid.getValue(ijk.offsetBy( 0, 0,-2)), grid.getValue(ijk.offsetBy( 0, 0,-3))); + } + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference(S.template getValue< 3, 0, 0>(), + S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), + S.template getValue<-1, 0, 0>(), + S.template getValue<-2, 0, 0>(), + S.template getValue<-3, 0, 0>()); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + + return difference( S.template getValue< 0, 3, 0>(), + S.template getValue< 0, 2, 0>(), + S.template getValue< 0, 1, 0>(), + S.template getValue< 0,-1, 0>(), + S.template getValue< 0,-2, 0>(), + S.template getValue< 0,-3, 0>()); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + + return difference( S.template getValue< 0, 0, 3>(), + S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0,-1>(), + S.template getValue< 0, 0,-2>(), + S.template getValue< 0, 0,-3>()); + } +}; + + +template<> +struct D1 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp1, const ValueType& xp0) { + return xp1 - xp0; + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(1, 0, 0)), grid.getValue(ijk)); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(0, 1, 0)), grid.getValue(ijk)); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(0, 0, 1)), grid.getValue(ijk)); + } + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference(S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>()); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference(S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>()); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference(S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0, 0>()); + } +}; + + +template<> +struct D1 +{ + // the difference opperator + template + static ValueType difference(const ValueType& xp2, const ValueType& xp1, const ValueType& xp0) + { + return ValueType(2)*xp1 -(ValueType(0.5)*xp2 + ValueType(3./2.)*xp0); + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(2,0,0)), + grid.getValue(ijk.offsetBy(1,0,0)), + grid.getValue(ijk)); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(0,2,0)), + grid.getValue(ijk.offsetBy(0,1,0)), + grid.getValue(ijk)); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(0,0,2)), + grid.getValue(ijk.offsetBy(0,0,1)), + grid.getValue(ijk)); + } + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference( S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), + S.template getValue< 0, 0, 0>() ); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference( S.template getValue< 0, 2, 0>(), + S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 0, 0>() ); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference( S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 0>() ); + } + +}; + + +template<> +struct D1 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp3, const ValueType& xp2, + const ValueType& xp1, const ValueType& xp0) + { + return static_cast(xp3/3.0 - 1.5*xp2 + 3.0*xp1 - 11.0*xp0/6.0); + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy(3,0,0)), + grid.getValue(ijk.offsetBy(2,0,0)), + grid.getValue(ijk.offsetBy(1,0,0)), + grid.getValue(ijk) ); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy(0,3,0)), + grid.getValue(ijk.offsetBy(0,2,0)), + grid.getValue(ijk.offsetBy(0,1,0)), + grid.getValue(ijk) ); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy(0,0,3)), + grid.getValue(ijk.offsetBy(0,0,2)), + grid.getValue(ijk.offsetBy(0,0,1)), + grid.getValue(ijk) ); + } + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference(S.template getValue< 3, 0, 0>(), + S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), + S.template getValue< 0, 0, 0>() ); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference(S.template getValue< 0, 3, 0>(), + S.template getValue< 0, 2, 0>(), + S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 0, 0>() ); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference( S.template getValue< 0, 0, 3>(), + S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 0>() ); + } +}; + + +template<> +struct D1 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xm1, const ValueType& xm0) { + return -D1::difference(xm1, xm0); + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk)); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(0,-1,0)), grid.getValue(ijk)); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(0, 0,-1)), grid.getValue(ijk)); + } + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference(S.template getValue<-1, 0, 0>(), S.template getValue< 0, 0, 0>()); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference(S.template getValue< 0,-1, 0>(), S.template getValue< 0, 0, 0>()); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference(S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0, 0>()); + } +}; + + +template<> +struct D1 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xm2, const ValueType& xm1, const ValueType& xm0) + { + return -D1::difference(xm2, xm1, xm0); + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy(-2,0,0)), + grid.getValue(ijk.offsetBy(-1,0,0)), + grid.getValue(ijk) ); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy(0,-2,0)), + grid.getValue(ijk.offsetBy(0,-1,0)), + grid.getValue(ijk) ); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy(0,0,-2)), + grid.getValue(ijk.offsetBy(0,0,-1)), + grid.getValue(ijk) ); + } + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference( S.template getValue<-2, 0, 0>(), + S.template getValue<-1, 0, 0>(), + S.template getValue< 0, 0, 0>() ); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference( S.template getValue< 0,-2, 0>(), + S.template getValue< 0,-1, 0>(), + S.template getValue< 0, 0, 0>() ); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference( S.template getValue< 0, 0,-2>(), + S.template getValue< 0, 0,-1>(), + S.template getValue< 0, 0, 0>() ); + } +}; + + +template<> +struct D1 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xm3, const ValueType& xm2, + const ValueType& xm1, const ValueType& xm0) + { + return -D1::difference(xm3, xm2, xm1, xm0); + } + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy(-3,0,0)), + grid.getValue(ijk.offsetBy(-2,0,0)), + grid.getValue(ijk.offsetBy(-1,0,0)), + grid.getValue(ijk) ); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy( 0,-3,0)), + grid.getValue(ijk.offsetBy( 0,-2,0)), + grid.getValue(ijk.offsetBy( 0,-1,0)), + grid.getValue(ijk) ); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy( 0, 0,-3)), + grid.getValue(ijk.offsetBy( 0, 0,-2)), + grid.getValue(ijk.offsetBy( 0, 0,-1)), + grid.getValue(ijk) ); + } + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference( S.template getValue<-3, 0, 0>(), + S.template getValue<-2, 0, 0>(), + S.template getValue<-1, 0, 0>(), + S.template getValue< 0, 0, 0>() ); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference( S.template getValue< 0,-3, 0>(), + S.template getValue< 0,-2, 0>(), + S.template getValue< 0,-1, 0>(), + S.template getValue< 0, 0, 0>() ); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference( S.template getValue< 0, 0,-3>(), + S.template getValue< 0, 0,-2>(), + S.template getValue< 0, 0,-1>(), + S.template getValue< 0, 0, 0>() ); + } + +}; + +template<> +struct D1 +{ + // the difference operator + template + static ValueType difference(const ValueType& xp3, const ValueType& xp2, + const ValueType& xp1, const ValueType& xp0, + const ValueType& xm1, const ValueType& xm2) { + return WENO5(xp3, xp2, xp1, xp0, xm1) + - WENO5(xp2, xp1, xp0, xm1, xm2); + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(3,0,0)); + V[1] = grid.getValue(ijk.offsetBy(2,0,0)); + V[2] = grid.getValue(ijk.offsetBy(1,0,0)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(-1,0,0)); + V[5] = grid.getValue(ijk.offsetBy(-2,0,0)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(0,3,0)); + V[1] = grid.getValue(ijk.offsetBy(0,2,0)); + V[2] = grid.getValue(ijk.offsetBy(0,1,0)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(0,-1,0)); + V[5] = grid.getValue(ijk.offsetBy(0,-2,0)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(0,0,3)); + V[1] = grid.getValue(ijk.offsetBy(0,0,2)); + V[2] = grid.getValue(ijk.offsetBy(0,0,1)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(0,0,-1)); + V[5] = grid.getValue(ijk.offsetBy(0,0,-2)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + + return static_cast(difference( + S.template getValue< 3, 0, 0>(), + S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), + S.template getValue< 0, 0, 0>(), + S.template getValue<-1, 0, 0>(), + S.template getValue<-2, 0, 0>() )); + + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return static_cast(difference( + S.template getValue< 0, 3, 0>(), + S.template getValue< 0, 2, 0>(), + S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 0, 0>(), + S.template getValue< 0,-1, 0>(), + S.template getValue< 0,-2, 0>() )); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return static_cast(difference( + S.template getValue< 0, 0, 3>(), + S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 0>(), + S.template getValue< 0, 0,-1>(), + S.template getValue< 0, 0,-2>() )); + } +}; + +template<> +struct D1 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp3, const ValueType& xp2, + const ValueType& xp1, const ValueType& xp0, + const ValueType& xm1, const ValueType& xm2) { + return WENO5(xp3 - xp2, xp2 - xp1, xp1 - xp0, xp0-xm1, xm1-xm2); + } + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(3,0,0)); + V[1] = grid.getValue(ijk.offsetBy(2,0,0)); + V[2] = grid.getValue(ijk.offsetBy(1,0,0)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(-1,0,0)); + V[5] = grid.getValue(ijk.offsetBy(-2,0,0)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(0,3,0)); + V[1] = grid.getValue(ijk.offsetBy(0,2,0)); + V[2] = grid.getValue(ijk.offsetBy(0,1,0)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(0,-1,0)); + V[5] = grid.getValue(ijk.offsetBy(0,-2,0)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(0,0,3)); + V[1] = grid.getValue(ijk.offsetBy(0,0,2)); + V[2] = grid.getValue(ijk.offsetBy(0,0,1)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(0,0,-1)); + V[5] = grid.getValue(ijk.offsetBy(0,0,-2)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + + return difference( S.template getValue< 3, 0, 0>(), + S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), + S.template getValue< 0, 0, 0>(), + S.template getValue<-1, 0, 0>(), + S.template getValue<-2, 0, 0>() ); + + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference( S.template getValue< 0, 3, 0>(), + S.template getValue< 0, 2, 0>(), + S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 0, 0>(), + S.template getValue< 0,-1, 0>(), + S.template getValue< 0,-2, 0>() ); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + + return difference( S.template getValue< 0, 0, 3>(), + S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 0>(), + S.template getValue< 0, 0,-1>(), + S.template getValue< 0, 0,-2>() ); + } + +}; + +template<> +struct D1 +{ + + template + static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1, + const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) + { + return -D1::difference(xm3, xm2, xm1, xm0, xp1, xp2); + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(-3,0,0)); + V[1] = grid.getValue(ijk.offsetBy(-2,0,0)); + V[2] = grid.getValue(ijk.offsetBy(-1,0,0)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(1,0,0)); + V[5] = grid.getValue(ijk.offsetBy(2,0,0)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(0,-3,0)); + V[1] = grid.getValue(ijk.offsetBy(0,-2,0)); + V[2] = grid.getValue(ijk.offsetBy(0,-1,0)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(0,1,0)); + V[5] = grid.getValue(ijk.offsetBy(0,2,0)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(0,0,-3)); + V[1] = grid.getValue(ijk.offsetBy(0,0,-2)); + V[2] = grid.getValue(ijk.offsetBy(0,0,-1)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(0,0,1)); + V[5] = grid.getValue(ijk.offsetBy(0,0,2)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + using ValueType = typename Stencil::ValueType; + ValueType V[6]; + V[0] = S.template getValue<-3, 0, 0>(); + V[1] = S.template getValue<-2, 0, 0>(); + V[2] = S.template getValue<-1, 0, 0>(); + V[3] = S.template getValue< 0, 0, 0>(); + V[4] = S.template getValue< 1, 0, 0>(); + V[5] = S.template getValue< 2, 0, 0>(); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + using ValueType = typename Stencil::ValueType; + ValueType V[6]; + V[0] = S.template getValue< 0,-3, 0>(); + V[1] = S.template getValue< 0,-2, 0>(); + V[2] = S.template getValue< 0,-1, 0>(); + V[3] = S.template getValue< 0, 0, 0>(); + V[4] = S.template getValue< 0, 1, 0>(); + V[5] = S.template getValue< 0, 2, 0>(); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + using ValueType = typename Stencil::ValueType; + ValueType V[6]; + V[0] = S.template getValue< 0, 0,-3>(); + V[1] = S.template getValue< 0, 0,-2>(); + V[2] = S.template getValue< 0, 0,-1>(); + V[3] = S.template getValue< 0, 0, 0>(); + V[4] = S.template getValue< 0, 0, 1>(); + V[5] = S.template getValue< 0, 0, 2>(); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } +}; + + +template<> +struct D1 +{ + template + static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1, + const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) + { + return -D1::difference(xm3, xm2, xm1, xm0, xp1, xp2); + } + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(-3,0,0)); + V[1] = grid.getValue(ijk.offsetBy(-2,0,0)); + V[2] = grid.getValue(ijk.offsetBy(-1,0,0)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(1,0,0)); + V[5] = grid.getValue(ijk.offsetBy(2,0,0)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(0,-3,0)); + V[1] = grid.getValue(ijk.offsetBy(0,-2,0)); + V[2] = grid.getValue(ijk.offsetBy(0,-1,0)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(0,1,0)); + V[5] = grid.getValue(ijk.offsetBy(0,2,0)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType V[6]; + V[0] = grid.getValue(ijk.offsetBy(0,0,-3)); + V[1] = grid.getValue(ijk.offsetBy(0,0,-2)); + V[2] = grid.getValue(ijk.offsetBy(0,0,-1)); + V[3] = grid.getValue(ijk); + V[4] = grid.getValue(ijk.offsetBy(0,0,1)); + V[5] = grid.getValue(ijk.offsetBy(0,0,2)); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + using ValueType = typename Stencil::ValueType; + ValueType V[6]; + V[0] = S.template getValue<-3, 0, 0>(); + V[1] = S.template getValue<-2, 0, 0>(); + V[2] = S.template getValue<-1, 0, 0>(); + V[3] = S.template getValue< 0, 0, 0>(); + V[4] = S.template getValue< 1, 0, 0>(); + V[5] = S.template getValue< 2, 0, 0>(); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + using ValueType = typename Stencil::ValueType; + ValueType V[6]; + V[0] = S.template getValue< 0,-3, 0>(); + V[1] = S.template getValue< 0,-2, 0>(); + V[2] = S.template getValue< 0,-1, 0>(); + V[3] = S.template getValue< 0, 0, 0>(); + V[4] = S.template getValue< 0, 1, 0>(); + V[5] = S.template getValue< 0, 2, 0>(); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + using ValueType = typename Stencil::ValueType; + ValueType V[6]; + V[0] = S.template getValue< 0, 0,-3>(); + V[1] = S.template getValue< 0, 0,-2>(); + V[2] = S.template getValue< 0, 0,-1>(); + V[3] = S.template getValue< 0, 0, 0>(); + V[4] = S.template getValue< 0, 0, 1>(); + V[5] = S.template getValue< 0, 0, 2>(); + + return difference(V[0], V[1], V[2], V[3], V[4], V[5]); + } +}; + + +template +struct D1Vec +{ + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::inX(grid, ijk)[n]; + } + + template + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::inY(grid, ijk)[n]; + } + template + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::inZ(grid, ijk)[n]; + } + + + // stencil access version + template + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::inX(S)[n]; + } + + template + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::inY(S)[n]; + } + + template + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::inZ(S)[n]; + } +}; + + +template<> +struct D1Vec +{ + + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy( 1, 0, 0))[n], + grid.getValue(ijk.offsetBy(-1, 0, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy(0, 1, 0))[n], + grid.getValue(ijk.offsetBy(0,-1, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy(0, 0, 1))[n], + grid.getValue(ijk.offsetBy(0, 0,-1))[n] ); + } + + // stencil access version + template + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 1, 0, 0>()[n], + S.template getValue<-1, 0, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 0, 1, 0>()[n], + S.template getValue< 0,-1, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 0, 0, 1>()[n], + S.template getValue< 0, 0,-1>()[n] ); + } +}; + +template<> +struct D1Vec +{ + + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy( 1, 0, 0))[n] , + grid.getValue(ijk.offsetBy(-1, 0, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy(0, 1, 0))[n] , + grid.getValue(ijk.offsetBy(0,-1, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy(0, 0, 1))[n] , + grid.getValue(ijk.offsetBy(0, 0,-1))[n] ); + } + + + // stencil access version + template + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 1, 0, 0>()[n], + S.template getValue<-1, 0, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 0, 1, 0>()[n], + S.template getValue< 0,-1, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 0, 0, 1>()[n], + S.template getValue< 0, 0,-1>()[n] ); + } +}; + + +template<> +struct D1Vec { + // using value_type = typename Accessor::ValueType::value_type; + + + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( + grid.getValue(ijk.offsetBy(2, 0, 0))[n], grid.getValue(ijk.offsetBy( 1, 0, 0))[n], + grid.getValue(ijk.offsetBy(-1,0, 0))[n], grid.getValue(ijk.offsetBy(-2, 0, 0))[n]); + } + + template + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( + grid.getValue(ijk.offsetBy( 0, 2, 0))[n], grid.getValue(ijk.offsetBy( 0, 1, 0))[n], + grid.getValue(ijk.offsetBy( 0,-1, 0))[n], grid.getValue(ijk.offsetBy( 0,-2, 0))[n]); + } + + template + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( + grid.getValue(ijk.offsetBy(0,0, 2))[n], grid.getValue(ijk.offsetBy( 0, 0, 1))[n], + grid.getValue(ijk.offsetBy(0,0,-1))[n], grid.getValue(ijk.offsetBy( 0, 0,-2))[n]); + } + + // stencil access version + template + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::difference( + S.template getValue< 2, 0, 0>()[n], S.template getValue< 1, 0, 0>()[n], + S.template getValue<-1, 0, 0>()[n], S.template getValue<-2, 0, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::difference( + S.template getValue< 0, 2, 0>()[n], S.template getValue< 0, 1, 0>()[n], + S.template getValue< 0,-1, 0>()[n], S.template getValue< 0,-2, 0>()[n]); + } + + template + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::difference( + S.template getValue< 0, 0, 2>()[n], S.template getValue< 0, 0, 1>()[n], + S.template getValue< 0, 0,-1>()[n], S.template getValue< 0, 0,-2>()[n]); + } +}; + + +template<> +struct D1Vec +{ + //using ValueType = typename Accessor::ValueType::value_type::value_type; + + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( + grid.getValue(ijk.offsetBy( 3, 0, 0))[n], grid.getValue(ijk.offsetBy( 2, 0, 0))[n], + grid.getValue(ijk.offsetBy( 1, 0, 0))[n], grid.getValue(ijk.offsetBy(-1, 0, 0))[n], + grid.getValue(ijk.offsetBy(-2, 0, 0))[n], grid.getValue(ijk.offsetBy(-3, 0, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( + grid.getValue(ijk.offsetBy( 0, 3, 0))[n], grid.getValue(ijk.offsetBy( 0, 2, 0))[n], + grid.getValue(ijk.offsetBy( 0, 1, 0))[n], grid.getValue(ijk.offsetBy( 0,-1, 0))[n], + grid.getValue(ijk.offsetBy( 0,-2, 0))[n], grid.getValue(ijk.offsetBy( 0,-3, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( + grid.getValue(ijk.offsetBy( 0, 0, 3))[n], grid.getValue(ijk.offsetBy( 0, 0, 2))[n], + grid.getValue(ijk.offsetBy( 0, 0, 1))[n], grid.getValue(ijk.offsetBy( 0, 0,-1))[n], + grid.getValue(ijk.offsetBy( 0, 0,-2))[n], grid.getValue(ijk.offsetBy( 0, 0,-3))[n] ); + } + + + // stencil access version + template + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::difference( + S.template getValue< 3, 0, 0>()[n], S.template getValue< 2, 0, 0>()[n], + S.template getValue< 1, 0, 0>()[n], S.template getValue<-1, 0, 0>()[n], + S.template getValue<-2, 0, 0>()[n], S.template getValue<-3, 0, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::difference( + S.template getValue< 0, 3, 0>()[n], S.template getValue< 0, 2, 0>()[n], + S.template getValue< 0, 1, 0>()[n], S.template getValue< 0,-1, 0>()[n], + S.template getValue< 0,-2, 0>()[n], S.template getValue< 0,-3, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::difference( + S.template getValue< 0, 0, 3>()[n], S.template getValue< 0, 0, 2>()[n], + S.template getValue< 0, 0, 1>()[n], S.template getValue< 0, 0,-1>()[n], + S.template getValue< 0, 0,-2>()[n], S.template getValue< 0, 0,-3>()[n] ); + } +}; + +template +struct D2 +{ + + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk); + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk); + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk); + + // cross derivatives + template + static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk); + + template + static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk); + + template + static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk); + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S); + template + static typename Stencil::ValueType inY(const Stencil& S); + template + static typename Stencil::ValueType inZ(const Stencil& S); + + // cross derivatives + template + static typename Stencil::ValueType inXandY(const Stencil& S); + + template + static typename Stencil::ValueType inXandZ(const Stencil& S); + + template + static typename Stencil::ValueType inYandZ(const Stencil& S); +}; + +template<> +struct D2 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp1, const ValueType& xp0, const ValueType& xm1) + { + return xp1 + xm1 - ValueType(2)*xp0; + } + + template + static ValueType crossdifference(const ValueType& xpyp, const ValueType& xpym, + const ValueType& xmyp, const ValueType& xmym) + { + return ValueType(0.25)*(xpyp + xmym - xpym - xmyp); + } + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy( 1,0,0)), grid.getValue(ijk), + grid.getValue(ijk.offsetBy(-1,0,0)) ); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + + return difference( grid.getValue(ijk.offsetBy(0, 1,0)), grid.getValue(ijk), + grid.getValue(ijk.offsetBy(0,-1,0)) ); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference( grid.getValue(ijk.offsetBy( 0,0, 1)), grid.getValue(ijk), + grid.getValue(ijk.offsetBy( 0,0,-1)) ); + } + + // cross derivatives + template + static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk) + { + return crossdifference( + grid.getValue(ijk.offsetBy(1, 1,0)), grid.getValue(ijk.offsetBy( 1,-1,0)), + grid.getValue(ijk.offsetBy(-1,1,0)), grid.getValue(ijk.offsetBy(-1,-1,0))); + + } + + template + static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk) + { + return crossdifference( + grid.getValue(ijk.offsetBy(1,0, 1)), grid.getValue(ijk.offsetBy(1, 0,-1)), + grid.getValue(ijk.offsetBy(-1,0,1)), grid.getValue(ijk.offsetBy(-1,0,-1)) ); + } + + template + static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk) + { + return crossdifference( + grid.getValue(ijk.offsetBy(0, 1,1)), grid.getValue(ijk.offsetBy(0, 1,-1)), + grid.getValue(ijk.offsetBy(0,-1,1)), grid.getValue(ijk.offsetBy(0,-1,-1)) ); + } + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference( S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>(), + S.template getValue<-1, 0, 0>() ); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference( S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>(), + S.template getValue< 0,-1, 0>() ); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference( S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0, 0>(), + S.template getValue< 0, 0,-1>() ); + } + + // cross derivatives + template + static typename Stencil::ValueType inXandY(const Stencil& S) + { + return crossdifference(S.template getValue< 1, 1, 0>(), S.template getValue< 1,-1, 0>(), + S.template getValue<-1, 1, 0>(), S.template getValue<-1,-1, 0>() ); + } + + template + static typename Stencil::ValueType inXandZ(const Stencil& S) + { + return crossdifference(S.template getValue< 1, 0, 1>(), S.template getValue< 1, 0,-1>(), + S.template getValue<-1, 0, 1>(), S.template getValue<-1, 0,-1>() ); + } + + template + static typename Stencil::ValueType inYandZ(const Stencil& S) + { + return crossdifference(S.template getValue< 0, 1, 1>(), S.template getValue< 0, 1,-1>(), + S.template getValue< 0,-1, 1>(), S.template getValue< 0,-1,-1>() ); + } +}; + + +template<> +struct D2 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp2, const ValueType& xp1, const ValueType& xp0, + const ValueType& xm1, const ValueType& xm2) { + return ValueType(-1./12.)*(xp2 + xm2) + ValueType(4./3.)*(xp1 + xm1) -ValueType(2.5)*xp0; + } + + template + static ValueType crossdifference(const ValueType& xp2yp2, const ValueType& xp2yp1, + const ValueType& xp2ym1, const ValueType& xp2ym2, + const ValueType& xp1yp2, const ValueType& xp1yp1, + const ValueType& xp1ym1, const ValueType& xp1ym2, + const ValueType& xm2yp2, const ValueType& xm2yp1, + const ValueType& xm2ym1, const ValueType& xm2ym2, + const ValueType& xm1yp2, const ValueType& xm1yp1, + const ValueType& xm1ym1, const ValueType& xm1ym2 ) { + ValueType tmp1 = + ValueType(2./3.0)*(xp1yp1 - xm1yp1 - xp1ym1 + xm1ym1)- + ValueType(1./12.)*(xp2yp1 - xm2yp1 - xp2ym1 + xm2ym1); + ValueType tmp2 = + ValueType(2./3.0)*(xp1yp2 - xm1yp2 - xp1ym2 + xm1ym2)- + ValueType(1./12.)*(xp2yp2 - xm2yp2 - xp2ym2 + xm2ym2); + + return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; + } + + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(2,0,0)), grid.getValue(ijk.offsetBy( 1,0,0)), + grid.getValue(ijk), + grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk.offsetBy(-2, 0, 0))); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(0, 2,0)), grid.getValue(ijk.offsetBy(0, 1,0)), + grid.getValue(ijk), + grid.getValue(ijk.offsetBy(0,-1,0)), grid.getValue(ijk.offsetBy(0,-2, 0))); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy(0,0, 2)), grid.getValue(ijk.offsetBy(0, 0,1)), + grid.getValue(ijk), + grid.getValue(ijk.offsetBy(0,0,-1)), grid.getValue(ijk.offsetBy(0,0,-2))); + } + + // cross derivatives + template + static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + typename Accessor::ValueType tmp1 = + D1::inX(grid, ijk.offsetBy(0, 1, 0)) - + D1::inX(grid, ijk.offsetBy(0,-1, 0)); + typename Accessor::ValueType tmp2 = + D1::inX(grid, ijk.offsetBy(0, 2, 0)) - + D1::inX(grid, ijk.offsetBy(0,-2, 0)); + return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; + } + + template + static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + typename Accessor::ValueType tmp1 = + D1::inX(grid, ijk.offsetBy(0, 0, 1)) - + D1::inX(grid, ijk.offsetBy(0, 0,-1)); + typename Accessor::ValueType tmp2 = + D1::inX(grid, ijk.offsetBy(0, 0, 2)) - + D1::inX(grid, ijk.offsetBy(0, 0,-2)); + return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; + } + + template + static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + typename Accessor::ValueType tmp1 = + D1::inY(grid, ijk.offsetBy(0, 0, 1)) - + D1::inY(grid, ijk.offsetBy(0, 0,-1)); + typename Accessor::ValueType tmp2 = + D1::inY(grid, ijk.offsetBy(0, 0, 2)) - + D1::inY(grid, ijk.offsetBy(0, 0,-2)); + return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; + } + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference(S.template getValue< 2, 0, 0>(), S.template getValue< 1, 0, 0>(), + S.template getValue< 0, 0, 0>(), + S.template getValue<-1, 0, 0>(), S.template getValue<-2, 0, 0>() ); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference(S.template getValue< 0, 2, 0>(), S.template getValue< 0, 1, 0>(), + S.template getValue< 0, 0, 0>(), + S.template getValue< 0,-1, 0>(), S.template getValue< 0,-2, 0>() ); + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference(S.template getValue< 0, 0, 2>(), S.template getValue< 0, 0, 1>(), + S.template getValue< 0, 0, 0>(), + S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0,-2>() ); + } + + // cross derivatives + template + static typename Stencil::ValueType inXandY(const Stencil& S) + { + return crossdifference( + S.template getValue< 2, 2, 0>(), S.template getValue< 2, 1, 0>(), + S.template getValue< 2,-1, 0>(), S.template getValue< 2,-2, 0>(), + S.template getValue< 1, 2, 0>(), S.template getValue< 1, 1, 0>(), + S.template getValue< 1,-1, 0>(), S.template getValue< 1,-2, 0>(), + S.template getValue<-2, 2, 0>(), S.template getValue<-2, 1, 0>(), + S.template getValue<-2,-1, 0>(), S.template getValue<-2,-2, 0>(), + S.template getValue<-1, 2, 0>(), S.template getValue<-1, 1, 0>(), + S.template getValue<-1,-1, 0>(), S.template getValue<-1,-2, 0>() ); + } + + template + static typename Stencil::ValueType inXandZ(const Stencil& S) + { + return crossdifference( + S.template getValue< 2, 0, 2>(), S.template getValue< 2, 0, 1>(), + S.template getValue< 2, 0,-1>(), S.template getValue< 2, 0,-2>(), + S.template getValue< 1, 0, 2>(), S.template getValue< 1, 0, 1>(), + S.template getValue< 1, 0,-1>(), S.template getValue< 1, 0,-2>(), + S.template getValue<-2, 0, 2>(), S.template getValue<-2, 0, 1>(), + S.template getValue<-2, 0,-1>(), S.template getValue<-2, 0,-2>(), + S.template getValue<-1, 0, 2>(), S.template getValue<-1, 0, 1>(), + S.template getValue<-1, 0,-1>(), S.template getValue<-1, 0,-2>() ); + } + + template + static typename Stencil::ValueType inYandZ(const Stencil& S) + { + return crossdifference( + S.template getValue< 0, 2, 2>(), S.template getValue< 0, 2, 1>(), + S.template getValue< 0, 2,-1>(), S.template getValue< 0, 2,-2>(), + S.template getValue< 0, 1, 2>(), S.template getValue< 0, 1, 1>(), + S.template getValue< 0, 1,-1>(), S.template getValue< 0, 1,-2>(), + S.template getValue< 0,-2, 2>(), S.template getValue< 0,-2, 1>(), + S.template getValue< 0,-2,-1>(), S.template getValue< 0,-2,-2>(), + S.template getValue< 0,-1, 2>(), S.template getValue< 0,-1, 1>(), + S.template getValue< 0,-1,-1>(), S.template getValue< 0,-1,-2>() ); + } +}; + + +template<> +struct D2 +{ + // the difference opperator + template + static ValueType difference(const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, + const ValueType& xp0, + const ValueType& xm1, const ValueType& xm2, const ValueType& xm3) + { + return ValueType(1./90.)*(xp3 + xm3) - ValueType(3./20.)*(xp2 + xm2) + + ValueType(1.5)*(xp1 + xm1) - ValueType(49./18.)*xp0; + } + + template + static ValueType crossdifference( const ValueType& xp1yp1,const ValueType& xm1yp1, + const ValueType& xp1ym1,const ValueType& xm1ym1, + const ValueType& xp2yp1,const ValueType& xm2yp1, + const ValueType& xp2ym1,const ValueType& xm2ym1, + const ValueType& xp3yp1,const ValueType& xm3yp1, + const ValueType& xp3ym1,const ValueType& xm3ym1, + const ValueType& xp1yp2,const ValueType& xm1yp2, + const ValueType& xp1ym2,const ValueType& xm1ym2, + const ValueType& xp2yp2,const ValueType& xm2yp2, + const ValueType& xp2ym2,const ValueType& xm2ym2, + const ValueType& xp3yp2,const ValueType& xm3yp2, + const ValueType& xp3ym2,const ValueType& xm3ym2, + const ValueType& xp1yp3,const ValueType& xm1yp3, + const ValueType& xp1ym3,const ValueType& xm1ym3, + const ValueType& xp2yp3,const ValueType& xm2yp3, + const ValueType& xp2ym3,const ValueType& xm2ym3, + const ValueType& xp3yp3,const ValueType& xm3yp3, + const ValueType& xp3ym3,const ValueType& xm3ym3 ) + { + ValueType tmp1 = + ValueType(0.7500)*(xp1yp1 - xm1yp1 - xp1ym1 + xm1ym1) - + ValueType(0.1500)*(xp2yp1 - xm2yp1 - xp2ym1 + xm2ym1) + + ValueType(1./60.)*(xp3yp1 - xm3yp1 - xp3ym1 + xm3ym1); + + ValueType tmp2 = + ValueType(0.7500)*(xp1yp2 - xm1yp2 - xp1ym2 + xm1ym2) - + ValueType(0.1500)*(xp2yp2 - xm2yp2 - xp2ym2 + xm2ym2) + + ValueType(1./60.)*(xp3yp2 - xm3yp2 - xp3ym2 + xm3ym2); + + ValueType tmp3 = + ValueType(0.7500)*(xp1yp3 - xm1yp3 - xp1ym3 + xm1ym3) - + ValueType(0.1500)*(xp2yp3 - xm2yp3 - xp2ym3 + xm2ym3) + + ValueType(1./60.)*(xp3yp3 - xm3yp3 - xp3ym3 + xm3ym3); + + return ValueType(0.75)*tmp1 - ValueType(0.15)*tmp2 + ValueType(1./60)*tmp3; + } + + // random access version + + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy( 3, 0, 0)), grid.getValue(ijk.offsetBy( 2, 0, 0)), + grid.getValue(ijk.offsetBy( 1, 0, 0)), grid.getValue(ijk), + grid.getValue(ijk.offsetBy(-1, 0, 0)), grid.getValue(ijk.offsetBy(-2, 0, 0)), + grid.getValue(ijk.offsetBy(-3, 0, 0)) ); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference( + grid.getValue(ijk.offsetBy( 0, 3, 0)), grid.getValue(ijk.offsetBy( 0, 2, 0)), + grid.getValue(ijk.offsetBy( 0, 1, 0)), grid.getValue(ijk), + grid.getValue(ijk.offsetBy( 0,-1, 0)), grid.getValue(ijk.offsetBy( 0,-2, 0)), + grid.getValue(ijk.offsetBy( 0,-3, 0)) ); + } + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk) + { + + return difference( + grid.getValue(ijk.offsetBy( 0, 0, 3)), grid.getValue(ijk.offsetBy( 0, 0, 2)), + grid.getValue(ijk.offsetBy( 0, 0, 1)), grid.getValue(ijk), + grid.getValue(ijk.offsetBy( 0, 0,-1)), grid.getValue(ijk.offsetBy( 0, 0,-2)), + grid.getValue(ijk.offsetBy( 0, 0,-3)) ); + } + + template + static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk) + { + using ValueT = typename Accessor::ValueType; + ValueT tmp1 = + D1::inX(grid, ijk.offsetBy(0, 1, 0)) - + D1::inX(grid, ijk.offsetBy(0,-1, 0)); + ValueT tmp2 = + D1::inX(grid, ijk.offsetBy(0, 2, 0)) - + D1::inX(grid, ijk.offsetBy(0,-2, 0)); + ValueT tmp3 = + D1::inX(grid, ijk.offsetBy(0, 3, 0)) - + D1::inX(grid, ijk.offsetBy(0,-3, 0)); + return ValueT(0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3); + } + + template + static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk) + { + using ValueT = typename Accessor::ValueType; + ValueT tmp1 = + D1::inX(grid, ijk.offsetBy(0, 0, 1)) - + D1::inX(grid, ijk.offsetBy(0, 0,-1)); + ValueT tmp2 = + D1::inX(grid, ijk.offsetBy(0, 0, 2)) - + D1::inX(grid, ijk.offsetBy(0, 0,-2)); + ValueT tmp3 = + D1::inX(grid, ijk.offsetBy(0, 0, 3)) - + D1::inX(grid, ijk.offsetBy(0, 0,-3)); + return ValueT(0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3); + } + + template + static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk) + { + using ValueT = typename Accessor::ValueType; + ValueT tmp1 = + D1::inY(grid, ijk.offsetBy(0, 0, 1)) - + D1::inY(grid, ijk.offsetBy(0, 0,-1)); + ValueT tmp2 = + D1::inY(grid, ijk.offsetBy(0, 0, 2)) - + D1::inY(grid, ijk.offsetBy(0, 0,-2)); + ValueT tmp3 = + D1::inY(grid, ijk.offsetBy(0, 0, 3)) - + D1::inY(grid, ijk.offsetBy(0, 0,-3)); + return ValueT(0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3); + } + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference( S.template getValue< 3, 0, 0>(), S.template getValue< 2, 0, 0>(), + S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>(), + S.template getValue<-1, 0, 0>(), S.template getValue<-2, 0, 0>(), + S.template getValue<-3, 0, 0>() ); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference( S.template getValue< 0, 3, 0>(), S.template getValue< 0, 2, 0>(), + S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>(), + S.template getValue< 0,-1, 0>(), S.template getValue< 0,-2, 0>(), + S.template getValue< 0,-3, 0>() ); + + } + + template + static typename Stencil::ValueType inZ(const Stencil& S) + { + return difference( S.template getValue< 0, 0, 3>(), S.template getValue< 0, 0, 2>(), + S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0, 0>(), + S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0,-2>(), + S.template getValue< 0, 0,-3>() ); + } + + template + static typename Stencil::ValueType inXandY(const Stencil& S) + { + return crossdifference( S.template getValue< 1, 1, 0>(), S.template getValue<-1, 1, 0>(), + S.template getValue< 1,-1, 0>(), S.template getValue<-1,-1, 0>(), + S.template getValue< 2, 1, 0>(), S.template getValue<-2, 1, 0>(), + S.template getValue< 2,-1, 0>(), S.template getValue<-2,-1, 0>(), + S.template getValue< 3, 1, 0>(), S.template getValue<-3, 1, 0>(), + S.template getValue< 3,-1, 0>(), S.template getValue<-3,-1, 0>(), + S.template getValue< 1, 2, 0>(), S.template getValue<-1, 2, 0>(), + S.template getValue< 1,-2, 0>(), S.template getValue<-1,-2, 0>(), + S.template getValue< 2, 2, 0>(), S.template getValue<-2, 2, 0>(), + S.template getValue< 2,-2, 0>(), S.template getValue<-2,-2, 0>(), + S.template getValue< 3, 2, 0>(), S.template getValue<-3, 2, 0>(), + S.template getValue< 3,-2, 0>(), S.template getValue<-3,-2, 0>(), + S.template getValue< 1, 3, 0>(), S.template getValue<-1, 3, 0>(), + S.template getValue< 1,-3, 0>(), S.template getValue<-1,-3, 0>(), + S.template getValue< 2, 3, 0>(), S.template getValue<-2, 3, 0>(), + S.template getValue< 2,-3, 0>(), S.template getValue<-2,-3, 0>(), + S.template getValue< 3, 3, 0>(), S.template getValue<-3, 3, 0>(), + S.template getValue< 3,-3, 0>(), S.template getValue<-3,-3, 0>() ); + } + + template + static typename Stencil::ValueType inXandZ(const Stencil& S) + { + return crossdifference( S.template getValue< 1, 0, 1>(), S.template getValue<-1, 0, 1>(), + S.template getValue< 1, 0,-1>(), S.template getValue<-1, 0,-1>(), + S.template getValue< 2, 0, 1>(), S.template getValue<-2, 0, 1>(), + S.template getValue< 2, 0,-1>(), S.template getValue<-2, 0,-1>(), + S.template getValue< 3, 0, 1>(), S.template getValue<-3, 0, 1>(), + S.template getValue< 3, 0,-1>(), S.template getValue<-3, 0,-1>(), + S.template getValue< 1, 0, 2>(), S.template getValue<-1, 0, 2>(), + S.template getValue< 1, 0,-2>(), S.template getValue<-1, 0,-2>(), + S.template getValue< 2, 0, 2>(), S.template getValue<-2, 0, 2>(), + S.template getValue< 2, 0,-2>(), S.template getValue<-2, 0,-2>(), + S.template getValue< 3, 0, 2>(), S.template getValue<-3, 0, 2>(), + S.template getValue< 3, 0,-2>(), S.template getValue<-3, 0,-2>(), + S.template getValue< 1, 0, 3>(), S.template getValue<-1, 0, 3>(), + S.template getValue< 1, 0,-3>(), S.template getValue<-1, 0,-3>(), + S.template getValue< 2, 0, 3>(), S.template getValue<-2, 0, 3>(), + S.template getValue< 2, 0,-3>(), S.template getValue<-2, 0,-3>(), + S.template getValue< 3, 0, 3>(), S.template getValue<-3, 0, 3>(), + S.template getValue< 3, 0,-3>(), S.template getValue<-3, 0,-3>() ); + } + + template + static typename Stencil::ValueType inYandZ(const Stencil& S) + { + return crossdifference( S.template getValue< 0, 1, 1>(), S.template getValue< 0,-1, 1>(), + S.template getValue< 0, 1,-1>(), S.template getValue< 0,-1,-1>(), + S.template getValue< 0, 2, 1>(), S.template getValue< 0,-2, 1>(), + S.template getValue< 0, 2,-1>(), S.template getValue< 0,-2,-1>(), + S.template getValue< 0, 3, 1>(), S.template getValue< 0,-3, 1>(), + S.template getValue< 0, 3,-1>(), S.template getValue< 0,-3,-1>(), + S.template getValue< 0, 1, 2>(), S.template getValue< 0,-1, 2>(), + S.template getValue< 0, 1,-2>(), S.template getValue< 0,-1,-2>(), + S.template getValue< 0, 2, 2>(), S.template getValue< 0,-2, 2>(), + S.template getValue< 0, 2,-2>(), S.template getValue< 0,-2,-2>(), + S.template getValue< 0, 3, 2>(), S.template getValue< 0,-3, 2>(), + S.template getValue< 0, 3,-2>(), S.template getValue< 0,-3,-2>(), + S.template getValue< 0, 1, 3>(), S.template getValue< 0,-1, 3>(), + S.template getValue< 0, 1,-3>(), S.template getValue< 0,-1,-3>(), + S.template getValue< 0, 2, 3>(), S.template getValue< 0,-2, 3>(), + S.template getValue< 0, 2,-3>(), S.template getValue< 0,-2,-3>(), + S.template getValue< 0, 3, 3>(), S.template getValue< 0,-3, 3>(), + S.template getValue< 0, 3,-3>(), S.template getValue< 0,-3,-3>() ); + } + +}; + +} // end math namespace +} // namespace OPENVDB_VERSION_NAME +} // end openvdb namespace + +#endif // OPENVDB_MATH_FINITEDIFFERENCE_HAS_BEEN_INCLUDED diff --git a/openvdb/math/LegacyFrustum.h b/openvdb/math/LegacyFrustum.h new file mode 100644 index 00000000..d2632e8c --- /dev/null +++ b/openvdb/math/LegacyFrustum.h @@ -0,0 +1,165 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file math/LegacyFrustum.h + +#ifndef OPENVDB_MATH_LEGACYFRUSTUM_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_LEGACYFRUSTUM_HAS_BEEN_INCLUDED + +#include +#include // for Real typedef +#include "Coord.h" +#include "Mat4.h" +#include "Vec3.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { +namespace internal { + +/// @brief LegacyFrustum class used at DreamWorks for converting old vdb files. +class LegacyFrustum +{ +public: + LegacyFrustum(std::istream& is) + { + // First read in the old transform's base class. + // the "extents" + Vec3i tmpMin, tmpMax; + is.read(reinterpret_cast(&tmpMin), sizeof(Vec3i::ValueType) * 3); + is.read(reinterpret_cast(&tmpMax), sizeof(Vec3i::ValueType) * 3); + + Coord tmpMinCoord(tmpMin); + Coord tmpMaxCoord(tmpMax); + + // set the extents + mExtents = CoordBBox(tmpMinCoord, tmpMaxCoord); + + // read the old-frustum class member data + //Mat4d tmpW2C; + Mat4d tmpW2C, tmpC2S, tmpS2C, tmpWorldToLocal; + Mat4d tmpS2U, tmpXYLocalToUnit, tmpZLocalToUnit; + Real tmpWindow[6]; + Real tmpPadding; + + //Mat4d tmpXYUnitToLocal, tmpZUnitToLocal + + // read in each matrix. + is.read(reinterpret_cast(&tmpW2C), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&mC2W), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&tmpC2S), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&tmpS2C), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&tmpWorldToLocal), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&mLocalToWorld), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + + is.read(reinterpret_cast(&tmpWindow[0]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[1]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[2]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[3]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[4]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[5]), sizeof(Real)); + + is.read(reinterpret_cast(&tmpPadding), sizeof(Real)); + + is.read(reinterpret_cast(&tmpS2U), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&mXYUnitToLocal), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&tmpXYLocalToUnit), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&mZUnitToLocal), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&tmpZLocalToUnit), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + + + mNearPlane = tmpWindow[4]; + mFarPlane = tmpWindow[5]; + + // Look up the world space corners of the + // frustum grid. + mFrNearOrigin = unitToLocalFrustum(Vec3R(0,0,0)); + mFrFarOrigin = unitToLocalFrustum(Vec3R(0,0,1)); + + Vec3d frNearXTip = unitToLocalFrustum(Vec3R(1,0,0)); + Vec3d frNearYTip = unitToLocalFrustum(Vec3R(0,1,0)); + mFrNearXBasis = frNearXTip - mFrNearOrigin; + mFrNearYBasis = frNearYTip - mFrNearOrigin; + + Vec3R frFarXTip = unitToLocalFrustum(Vec3R(1,0,1)); + Vec3R frFarYTip = unitToLocalFrustum(Vec3R(0,1,1)); + mFrFarXBasis = frFarXTip - mFrFarOrigin; + mFrFarYBasis = frFarYTip - mFrFarOrigin; + } + + ~LegacyFrustum() {} + + const Mat4d& getCamXForm() const {return mC2W; } + + double getDepth() const {return (mFarPlane - mNearPlane); } + double getTaper() const { + + return getNearPlaneWidth() / getFarPlaneWidth(); + } + + double getNearPlaneWidth() const { + double nearPlaneWidth = (unitToWorld(Vec3d(0,0,0)) - unitToWorld(Vec3d(1,0,0))).length(); + return nearPlaneWidth; + } + + double getFarPlaneWidth() const { + double farPlaneWidth = (unitToWorld(Vec3d(0,0,1)) - unitToWorld(Vec3d(1,0,1))).length(); + return farPlaneWidth; + } + + double getNearPlaneDist() const { return mNearPlane; } + + const CoordBBox& getBBox() const {return mExtents; } + + Vec3d unitToWorld(const Vec3d& in) const {return mLocalToWorld.transform( unitToLocal(in) ); } + +private: + LegacyFrustum() {} + + Vec3d unitToLocal(const Vec3d& U) const { + + // We first find the local space coordinates + // of the unit point projected onto the near + // and far planes of the frustum by using a + // linear combination of the planes basis vectors + Vec3d nearLS = ( U[0] * mFrNearXBasis ) + ( U[1] * mFrNearYBasis ) + mFrNearOrigin; + Vec3d farLS = ( U[0] * mFrFarXBasis ) + ( U[1] * mFrFarYBasis ) + mFrFarOrigin; + + // then we lerp the two ws points in frustum z space + return U[2] * farLS + ( 1.0 - U[2] ) * nearLS; + } + + Vec3d unitToLocalFrustum(const Vec3d& u) const { + Vec3d fzu = mZUnitToLocal.transformH(u); + Vec3d fu = u; + fu[2] = fzu.z(); + return mXYUnitToLocal.transformH(fu); + } + +private: + Mat4d mC2W, mLocalToWorld, mXYUnitToLocal, mZUnitToLocal; + CoordBBox mExtents; + Vec3d mFrNearXBasis, mFrNearYBasis, mFrFarXBasis, mFrFarYBasis; + Vec3d mFrNearOrigin, mFrFarOrigin; + double mNearPlane, mFarPlane; +}; + +} // namespace internal +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_LEGACYFRUSTUM_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Maps.cc b/openvdb/math/Maps.cc new file mode 100644 index 00000000..15123059 --- /dev/null +++ b/openvdb/math/Maps.cc @@ -0,0 +1,262 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Maps.h" +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +namespace { + +using Mutex = tbb::mutex; +using Lock = Mutex::scoped_lock; + +// Declare this at file scope to ensure thread-safe initialization. +// NOTE: Do *NOT* move this into Maps.h or else we will need to pull in +// Windows.h with things like 'rad2' defined! +Mutex sInitMapRegistryMutex; + +} // unnamed namespace + + +//////////////////////////////////////// + + +// Caller is responsible for calling this function serially. +MapRegistry* +MapRegistry::staticInstance() +{ + static MapRegistry registry; + return ®istry; +} + + +MapRegistry* +MapRegistry::instance() +{ + Lock lock(sInitMapRegistryMutex); + return staticInstance(); +} + + +MapBase::Ptr +MapRegistry::createMap(const Name& name) +{ + Lock lock(sInitMapRegistryMutex); + MapDictionary::const_iterator iter = staticInstance()->mMap.find(name); + + if (iter == staticInstance()->mMap.end()) { + OPENVDB_THROW(LookupError, "Cannot create map of unregistered type " << name); + } + + return (iter->second)(); +} + + +bool +MapRegistry::isRegistered(const Name& name) +{ + Lock lock(sInitMapRegistryMutex); + return (staticInstance()->mMap.find(name) != staticInstance()->mMap.end()); +} + + +void +MapRegistry::registerMap(const Name& name, MapBase::MapFactory factory) +{ + Lock lock(sInitMapRegistryMutex); + + if (staticInstance()->mMap.find(name) != staticInstance()->mMap.end()) { + OPENVDB_THROW(KeyError, "Map type " << name << " is already registered"); + } + + staticInstance()->mMap[name] = factory; +} + + +void +MapRegistry::unregisterMap(const Name& name) +{ + Lock lock(sInitMapRegistryMutex); + staticInstance()->mMap.erase(name); +} + + +void +MapRegistry::clear() +{ + Lock lock(sInitMapRegistryMutex); + staticInstance()->mMap.clear(); +} + + +//////////////////////////////////////// + +// Utility methods for decomposition + + +SymmetricMap::Ptr +createSymmetricMap(const Mat3d& m) +{ + // test that the mat3 is a rotation || reflection + if (!isSymmetric(m)) { + OPENVDB_THROW(ArithmeticError, + "3x3 Matrix initializing symmetric map was not symmetric"); + } + Vec3d eigenValues; + Mat3d Umatrix; + + bool converged = math::diagonalizeSymmetricMatrix(m, Umatrix, eigenValues); + if (!converged) { + OPENVDB_THROW(ArithmeticError, "Diagonalization of the symmetric matrix failed"); + } + + UnitaryMap rotation(Umatrix); + ScaleMap diagonal(eigenValues); + CompoundMap first(rotation, diagonal); + + UnitaryMap rotationInv(Umatrix.transpose()); + return SymmetricMap::Ptr( new SymmetricMap(first, rotationInv)); +} + + +PolarDecomposedMap::Ptr +createPolarDecomposedMap(const Mat3d& m) +{ + // Because our internal libary left-multiplies vectors against matrices + // we are constructing M = Symmetric * Unitary instead of the more + // standard M = Unitary * Symmetric + Mat3d unitary, symmetric, mat3 = m.transpose(); + + // factor mat3 = U * S where U is unitary and S is symmetric + bool gotPolar = math::polarDecomposition(mat3, unitary, symmetric); + if (!gotPolar) { + OPENVDB_THROW(ArithmeticError, "Polar decomposition of transform failed"); + } + // put the result in a polar map and then copy it into the output polar + UnitaryMap unitary_map(unitary.transpose()); + SymmetricMap::Ptr symmetric_map = createSymmetricMap(symmetric); + + return PolarDecomposedMap::Ptr(new PolarDecomposedMap(*symmetric_map, unitary_map)); +} + + +FullyDecomposedMap::Ptr +createFullyDecomposedMap(const Mat4d& m) +{ + if (!isAffine(m)) { + OPENVDB_THROW(ArithmeticError, + "4x4 Matrix initializing Decomposition map was not affine"); + } + + TranslationMap translate(m.getTranslation()); + PolarDecomposedMap::Ptr polar = createPolarDecomposedMap(m.getMat3()); + + UnitaryAndTranslationMap rotationAndTranslate(polar->secondMap(), translate); + + return FullyDecomposedMap::Ptr(new FullyDecomposedMap(polar->firstMap(), rotationAndTranslate)); +} + + +MapBase::Ptr +simplify(AffineMap::Ptr affine) +{ + if (affine->isScale()) { // can be simplified into a ScaleMap + + Vec3d scale = affine->applyMap(Vec3d(1,1,1)); + if (isApproxEqual(scale[0], scale[1]) && isApproxEqual(scale[0], scale[2])) { + return MapBase::Ptr(new UniformScaleMap(scale[0])); + } else { + return MapBase::Ptr(new ScaleMap(scale)); + } + + } else if (affine->isScaleTranslate()) { // can be simplified into a ScaleTranslateMap + + Vec3d translate = affine->applyMap(Vec3d(0,0,0)); + Vec3d scale = affine->applyMap(Vec3d(1,1,1)) - translate; + + if (isApproxEqual(scale[0], scale[1]) && isApproxEqual(scale[0], scale[2])) { + return MapBase::Ptr(new UniformScaleTranslateMap(scale[0], translate)); + } else { + return MapBase::Ptr(new ScaleTranslateMap(scale, translate)); + } + } + + // could not simplify the general Affine map. + return StaticPtrCast(affine); +} + + +Mat4d +approxInverse(const Mat4d& mat4d) +{ + if (std::abs(mat4d.det()) >= 3 * math::Tolerance::value()) { + try { + return mat4d.inverse(); + } catch (ArithmeticError& ) { + // Mat4 code couldn't invert. + } + } + + const Mat3d mat3 = mat4d.getMat3(); + const Mat3d mat3T = mat3.transpose(); + const Vec3d trans = mat4d.getTranslation(); + + // absolute tolerance used for the symmetric test. + const double tol = 1.e-6; + + // only create the pseudoInverse for symmetric + bool symmetric = true; + for (int i = 0; i < 3; ++i ) { + for (int j = 0; j < 3; ++j ) { + if (!isApproxEqual(mat3[i][j], mat3T[i][j], tol)) { + symmetric = false; + } + } + } + + if (!symmetric) { + + // not symmetric, so just zero out the mat3 inverse and reverse the translation + + Mat4d result = Mat4d::zero(); + result.setTranslation(-trans); + result[3][3] = 1.f; + return result; + + } else { + + // compute the pseudo inverse + + Mat3d eigenVectors; + Vec3d eigenValues; + + diagonalizeSymmetricMatrix(mat3, eigenVectors, eigenValues); + + Mat3d d = Mat3d::identity(); + for (int i = 0; i < 3; ++i ) { + if (std::abs(eigenValues[i]) < 10.0 * math::Tolerance::value()) { + d[i][i] = 0.f; + } else { + d[i][i] = 1.f/eigenValues[i]; + } + } + // assemble the pseudo inverse + + Mat3d pseudoInv = eigenVectors * d * eigenVectors.transpose(); + Vec3d invTrans = -trans * pseudoInv; + + Mat4d result = Mat4d::identity(); + result.setMat3(pseudoInv); + result.setTranslation(invTrans); + + return result; + } +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/math/Maps.h b/openvdb/math/Maps.h new file mode 100644 index 00000000..47a153aa --- /dev/null +++ b/openvdb/math/Maps.h @@ -0,0 +1,2692 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file math/Maps.h + +#ifndef OPENVDB_MATH_MAPS_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_MAPS_HAS_BEEN_INCLUDED + +#include "Math.h" +#include "Mat4.h" +#include "Vec3.h" +#include "BBox.h" +#include "Coord.h" +#include // for io::getFormatVersion() +#include +#include +#include // for std::abs() +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + + +//////////////////////////////////////// + +/// Forward declarations of the different map types + +class MapBase; +class ScaleMap; +class TranslationMap; +class ScaleTranslateMap; +class UniformScaleMap; +class UniformScaleTranslateMap; +class AffineMap; +class UnitaryMap; +class NonlinearFrustumMap; + +template class CompoundMap; + +using UnitaryAndTranslationMap = CompoundMap; +using SpectralDecomposedMap = CompoundMap, UnitaryMap>; +using SymmetricMap = SpectralDecomposedMap; +using FullyDecomposedMap = CompoundMap; +using PolarDecomposedMap = CompoundMap; + + +//////////////////////////////////////// + +/// Map traits + +template struct is_linear { static const bool value = false; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; + +template struct is_linear > { + static const bool value = is_linear::value && is_linear::value; +}; + + +template struct is_uniform_scale { static const bool value = false; }; +template<> struct is_uniform_scale { static const bool value = true; }; + +template struct is_uniform_scale_translate { static const bool value = false; }; +template<> struct is_uniform_scale_translate { static const bool value = true; }; +template<> struct is_uniform_scale_translate { + static const bool value = true; +}; + + +template struct is_scale { static const bool value = false; }; +template<> struct is_scale { static const bool value = true; }; + +template struct is_scale_translate { static const bool value = false; }; +template<> struct is_scale_translate { static const bool value = true; }; + + +template struct is_uniform_diagonal_jacobian { + static const bool value = is_uniform_scale::value || is_uniform_scale_translate::value; +}; + +template struct is_diagonal_jacobian { + static const bool value = is_scale::value || is_scale_translate::value; +}; + + +//////////////////////////////////////// + +/// Utility methods + +/// @brief Create a SymmetricMap from a symmetric matrix. +/// Decomposes the map into Rotation Diagonal Rotation^T +OPENVDB_API SharedPtr createSymmetricMap(const Mat3d& m); + + +/// @brief General decomposition of a Matrix into a Unitary (e.g. rotation) +/// following a Symmetric (e.g. stretch & shear) +OPENVDB_API SharedPtr createFullyDecomposedMap(const Mat4d& m); + + +/// @brief Decomposes a general linear into translation following polar decomposition. +/// +/// T U S where: +/// +/// T: Translation +/// U: Unitary (rotation or reflection) +/// S: Symmetric +/// +/// @note: the Symmetric is automatically decomposed into Q D Q^T, where +/// Q is rotation and D is diagonal. +OPENVDB_API SharedPtr createPolarDecomposedMap(const Mat3d& m); + + +/// @brief reduces an AffineMap to a ScaleMap or a ScaleTranslateMap when it can +OPENVDB_API SharedPtr simplify(SharedPtr affine); + +/// @brief Returns the left pseudoInverse of the input matrix when the 3x3 part is symmetric +/// otherwise it zeros the 3x3 and reverses the translation. +OPENVDB_API Mat4d approxInverse(const Mat4d& mat); + + +//////////////////////////////////////// + + +/// @brief Abstract base class for maps +class OPENVDB_API MapBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + using MapFactory = Ptr (*)(); + + MapBase(const MapBase&) = default; + virtual ~MapBase() = default; + + virtual SharedPtr getAffineMap() const = 0; + + /// Return the name of this map's concrete type (e.g., @c "AffineMap"). + virtual Name type() const = 0; + + /// Return @c true if this map is of concrete type @c MapT (e.g., AffineMap). + template bool isType() const { return this->type() == MapT::mapType(); } + + /// Return @c true if this map is equal to the given map. + virtual bool isEqual(const MapBase& other) const = 0; + + /// Return @c true if this map is linear. + virtual bool isLinear() const = 0; + /// Return @c true if the spacing between the image of latice is uniform in all directions + virtual bool hasUniformScale() const = 0; + + virtual Vec3d applyMap(const Vec3d& in) const = 0; + virtual Vec3d applyInverseMap(const Vec3d& in) const = 0; + + //@{ + /// @brief Apply the Inverse Jacobian Transpose of this map to a vector. + /// For a linear map this is equivalent to applying the transpose of + /// inverse map excluding translation. + virtual Vec3d applyIJT(const Vec3d& in) const = 0; + virtual Vec3d applyIJT(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + virtual Mat3d applyIJC(const Mat3d& m) const = 0; + virtual Mat3d applyIJC(const Mat3d& m, const Vec3d& v, const Vec3d& domainPos) const = 0; + + + virtual double determinant() const = 0; + virtual double determinant(const Vec3d&) const = 0; + + + //@{ + /// @brief Method to return the local size of a voxel. + /// When a location is specified as an argument, it is understood to be + /// be in the domain of the map (i.e. index space) + virtual Vec3d voxelSize() const = 0; + virtual Vec3d voxelSize(const Vec3d&) const = 0; + //@} + + virtual void read(std::istream&) = 0; + virtual void write(std::ostream&) const = 0; + + virtual std::string str() const = 0; + + virtual MapBase::Ptr copy() const = 0; + + //@{ + /// @brief Methods to update the map + virtual MapBase::Ptr preRotate(double radians, Axis axis = X_AXIS) const = 0; + virtual MapBase::Ptr preTranslate(const Vec3d&) const = 0; + virtual MapBase::Ptr preScale(const Vec3d&) const = 0; + virtual MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const = 0; + + virtual MapBase::Ptr postRotate(double radians, Axis axis = X_AXIS) const = 0; + virtual MapBase::Ptr postTranslate(const Vec3d&) const = 0; + virtual MapBase::Ptr postScale(const Vec3d&) const = 0; + virtual MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const = 0; + //@} + + //@{ + /// @brief Apply the Jacobian of this map to a vector. + /// For a linear map this is equivalent to applying the map excluding translation. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual Vec3d applyJacobian(const Vec3d& in) const = 0; + virtual Vec3d applyJacobian(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + //@{ + /// @brief Apply the InverseJacobian of this map to a vector. + /// For a linear map this is equivalent to applying the map inverse excluding translation. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual Vec3d applyInverseJacobian(const Vec3d& in) const = 0; + virtual Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + + //@{ + /// @brief Apply the Jacobian transpose of this map to a vector. + /// For a linear map this is equivalent to applying the transpose of the map + /// excluding translation. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual Vec3d applyJT(const Vec3d& in) const = 0; + virtual Vec3d applyJT(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + /// @brief Return a new map representing the inverse of this map. + /// @throw NotImplementedError if the map is a NonlinearFrustumMap. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual MapBase::Ptr inverseMap() const = 0; + +protected: + MapBase() {} + + template + static bool isEqualBase(const MapT& self, const MapBase& other) + { + return other.isType() && (self == *static_cast(&other)); + } +}; + + +//////////////////////////////////////// + + +/// @brief Threadsafe singleton object for accessing the map type-name dictionary. +/// Associates a map type-name with a factory function. +class OPENVDB_API MapRegistry +{ +public: + using MapDictionary = std::map; + + static MapRegistry* instance(); + + /// Create a new map of the given (registered) type name. + static MapBase::Ptr createMap(const Name&); + + /// Return @c true if the given map type name is registered. + static bool isRegistered(const Name&); + + /// Register a map type along with a factory function. + static void registerMap(const Name&, MapBase::MapFactory); + + /// Remove a map type from the registry. + static void unregisterMap(const Name&); + + /// Clear the map type registry. + static void clear(); + +private: + MapRegistry() {} + + static MapRegistry* staticInstance(); + + MapDictionary mMap; +}; + + +//////////////////////////////////////// + + +/// @brief A general linear transform using homogeneous coordinates to perform +/// rotation, scaling, shear and translation +class OPENVDB_API AffineMap: public MapBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + AffineMap(): + mMatrix(Mat4d::identity()), + mMatrixInv(Mat4d::identity()), + mJacobianInv(Mat3d::identity()), + mDeterminant(1), + mVoxelSize(Vec3d(1,1,1)), + mIsDiagonal(true), + mIsIdentity(true) + // the default constructor for translation is zero + { + } + + AffineMap(const Mat3d& m) + { + Mat4d mat4(Mat4d::identity()); + mat4.setMat3(m); + mMatrix = mat4; + updateAcceleration(); + } + + AffineMap(const Mat4d& m): mMatrix(m) + { + if (!isAffine(m)) { + OPENVDB_THROW(ArithmeticError, + "Tried to initialize an affine transform from a non-affine 4x4 matrix"); + } + updateAcceleration(); + } + + AffineMap(const AffineMap& other): + MapBase(other), + mMatrix(other.mMatrix), + mMatrixInv(other.mMatrixInv), + mJacobianInv(other.mJacobianInv), + mDeterminant(other.mDeterminant), + mVoxelSize(other.mVoxelSize), + mIsDiagonal(other.mIsDiagonal), + mIsIdentity(other.mIsIdentity) + { + } + + /// @brief constructor that merges the matrixes for two affine maps + AffineMap(const AffineMap& first, const AffineMap& second): + mMatrix(first.mMatrix * second.mMatrix) + { + updateAcceleration(); + } + + ~AffineMap() override = default; + + /// Return a MapBase::Ptr to a new AffineMap + static MapBase::Ptr create() { return MapBase::Ptr(new AffineMap()); } + /// Return a MapBase::Ptr to a deep copy of this map + MapBase::Ptr copy() const override { return MapBase::Ptr(new AffineMap(*this)); } + + MapBase::Ptr inverseMap() const override { return MapBase::Ptr(new AffineMap(mMatrixInv)); } + + static bool isRegistered() { return MapRegistry::isRegistered(AffineMap::mapType()); } + + static void registerMap() + { + MapRegistry::registerMap( + AffineMap::mapType(), + AffineMap::create); + } + + Name type() const override { return mapType(); } + static Name mapType() { return Name("AffineMap"); } + + /// Return @c true (an AffineMap is always linear). + bool isLinear() const override { return true; } + + /// Return @c false ( test if this is unitary with translation ) + bool hasUniformScale() const override + { + Mat3d mat = mMatrix.getMat3(); + const double det = mat.det(); + if (isApproxEqual(det, double(0))) { + return false; + } else { + mat *= (1.0 / pow(std::abs(det), 1.0/3.0)); + return isUnitary(mat); + } + } + + bool isEqual(const MapBase& other) const override { return isEqualBase(*this, other); } + + bool operator==(const AffineMap& other) const + { + // the Mat.eq() is approximate + if (!mMatrix.eq(other.mMatrix)) { return false; } + if (!mMatrixInv.eq(other.mMatrixInv)) { return false; } + return true; + } + + bool operator!=(const AffineMap& other) const { return !(*this == other); } + + AffineMap& operator=(const AffineMap& other) + { + mMatrix = other.mMatrix; + mMatrixInv = other.mMatrixInv; + + mJacobianInv = other.mJacobianInv; + mDeterminant = other.mDeterminant; + mVoxelSize = other.mVoxelSize; + mIsDiagonal = other.mIsDiagonal; + mIsIdentity = other.mIsIdentity; + return *this; + } + /// Return the image of @c in under the map + Vec3d applyMap(const Vec3d& in) const override { return in * mMatrix; } + /// Return the pre-image of @c in under the map + Vec3d applyInverseMap(const Vec3d& in) const override {return in * mMatrixInv; } + + /// Return the Jacobian of the map applied to @a in. + Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const override { return applyJacobian(in); } + /// Return the Jacobian of the map applied to @a in. + Vec3d applyJacobian(const Vec3d& in) const override { return mMatrix.transform3x3(in); } + + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const override { + return applyInverseJacobian(in); + } + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const override { + return mMatrixInv.transform3x3(in); + } + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const override { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const override { + const double* m = mMatrix.asPointer(); + return Vec3d( m[ 0] * in[0] + m[ 1] * in[1] + m[ 2] * in[2], + m[ 4] * in[0] + m[ 5] * in[1] + m[ 6] * in[2], + m[ 8] * in[0] + m[ 9] * in[1] + m[10] * in[2] ); + } + + /// Return the transpose of the inverse Jacobian of the map applied to @a in. + Vec3d applyIJT(const Vec3d& in, const Vec3d&) const override { return applyIJT(in); } + /// Return the transpose of the inverse Jacobian of the map applied to @c in + Vec3d applyIJT(const Vec3d& in) const override { return in * mJacobianInv; } + /// Return the Jacobian Curvature: zero for a linear map + Mat3d applyIJC(const Mat3d& m) const override { + return mJacobianInv.transpose()* m * mJacobianInv; + } + Mat3d applyIJC(const Mat3d& in, const Vec3d& , const Vec3d& ) const override { + return applyIJC(in); + } + /// Return the determinant of the Jacobian, ignores argument + double determinant(const Vec3d& ) const override { return determinant(); } + /// Return the determinant of the Jacobian + double determinant() const override { return mDeterminant; } + + //@{ + /// @brief Return the lengths of the images of the segments + /// (0,0,0)-(1,0,0), (0,0,0)-(0,1,0) and (0,0,0)-(0,0,1). + Vec3d voxelSize() const override { return mVoxelSize; } + Vec3d voxelSize(const Vec3d&) const override { return voxelSize(); } + //@} + + /// Return @c true if the underlying matrix is approximately an identity + bool isIdentity() const { return mIsIdentity; } + /// Return @c true if the underylying matrix is diagonal + bool isDiagonal() const { return mIsDiagonal; } + /// Return @c true if the map is equivalent to a ScaleMap + bool isScale() const { return isDiagonal(); } + /// Return @c true if the map is equivalent to a ScaleTranslateMap + bool isScaleTranslate() const { return math::isDiagonal(mMatrix.getMat3()); } + + + // Methods that modify the existing affine map + + //@{ + /// @brief Modify the existing affine map by pre-applying the given operation. + void accumPreRotation(Axis axis, double radians) + { + mMatrix.preRotate(axis, radians); + updateAcceleration(); + } + void accumPreScale(const Vec3d& v) + { + mMatrix.preScale(v); + updateAcceleration(); + } + void accumPreTranslation(const Vec3d& v) + { + mMatrix.preTranslate(v); + updateAcceleration(); + } + void accumPreShear(Axis axis0, Axis axis1, double shear) + { + mMatrix.preShear(axis0, axis1, shear); + updateAcceleration(); + } + //@} + + + //@{ + /// @brief Modify the existing affine map by post-applying the given operation. + void accumPostRotation(Axis axis, double radians) + { + mMatrix.postRotate(axis, radians); + updateAcceleration(); + } + void accumPostScale(const Vec3d& v) + { + mMatrix.postScale(v); + updateAcceleration(); + } + void accumPostTranslation(const Vec3d& v) + { + mMatrix.postTranslate(v); + updateAcceleration(); + } + void accumPostShear(Axis axis0, Axis axis1, double shear) + { + mMatrix.postShear(axis0, axis1, shear); + updateAcceleration(); + } + //@} + + + /// read serialization + void read(std::istream& is) override { mMatrix.read(is); updateAcceleration(); } + /// write serialization + void write(std::ostream& os) const override { mMatrix.write(os); } + /// string serialization, useful for debugging + std::string str() const override + { + std::ostringstream buffer; + buffer << " - mat4:\n" << mMatrix.str() << std::endl; + buffer << " - voxel dimensions: " << mVoxelSize << std::endl; + return buffer.str(); + } + + /// on-demand decomposition of the affine map + SharedPtr createDecomposedMap() + { + return createFullyDecomposedMap(mMatrix); + } + + /// Return AffineMap::Ptr to a deep copy of the current AffineMap + AffineMap::Ptr getAffineMap() const override { return AffineMap::Ptr(new AffineMap(*this)); } + + /// Return AffineMap::Ptr to the inverse of this map + AffineMap::Ptr inverse() const { return AffineMap::Ptr(new AffineMap(mMatrixInv)); } + + + //@{ + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the appropraite operation. + MapBase::Ptr preRotate(double radians, Axis axis = X_AXIS) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreRotation(axis, radians); + return simplify(affineMap); + } + MapBase::Ptr preTranslate(const Vec3d& t) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreTranslation(t); + return StaticPtrCast(affineMap); + } + MapBase::Ptr preScale(const Vec3d& s) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreScale(s); + return StaticPtrCast(affineMap); + } + MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreShear(axis0, axis1, shear); + return simplify(affineMap); + } + //@} + + + //@{ + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of postfixing the appropraite operation. + MapBase::Ptr postRotate(double radians, Axis axis = X_AXIS) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostRotation(axis, radians); + return simplify(affineMap); + } + MapBase::Ptr postTranslate(const Vec3d& t) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostTranslation(t); + return StaticPtrCast(affineMap); + } + MapBase::Ptr postScale(const Vec3d& s) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostScale(s); + return StaticPtrCast(affineMap); + } + MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostShear(axis0, axis1, shear); + return simplify(affineMap); + } + //@} + + /// Return the matrix representation of this AffineMap + Mat4d getMat4() const { return mMatrix;} + const Mat4d& getConstMat4() const {return mMatrix;} + const Mat3d& getConstJacobianInv() const {return mJacobianInv;} + +private: + void updateAcceleration() { + Mat3d mat3 = mMatrix.getMat3(); + mDeterminant = mat3.det(); + + if (std::abs(mDeterminant) < (3.0 * math::Tolerance::value())) { + OPENVDB_THROW(ArithmeticError, + "Tried to initialize an affine transform from a nearly singular matrix"); + } + mMatrixInv = mMatrix.inverse(); + mJacobianInv = mat3.inverse().transpose(); + mIsDiagonal = math::isDiagonal(mMatrix); + mIsIdentity = math::isIdentity(mMatrix); + Vec3d pos = applyMap(Vec3d(0,0,0)); + mVoxelSize(0) = (applyMap(Vec3d(1,0,0)) - pos).length(); + mVoxelSize(1) = (applyMap(Vec3d(0,1,0)) - pos).length(); + mVoxelSize(2) = (applyMap(Vec3d(0,0,1)) - pos).length(); + } + + // the underlying matrix + Mat4d mMatrix; + + // stored for acceleration + Mat4d mMatrixInv; + Mat3d mJacobianInv; + double mDeterminant; + Vec3d mVoxelSize; + bool mIsDiagonal, mIsIdentity; +}; // class AffineMap + + +//////////////////////////////////////// + + +/// @brief A specialized Affine transform that scales along the principal axis +/// the scaling need not be uniform in the three-directions +class OPENVDB_API ScaleMap: public MapBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + ScaleMap(): MapBase(), mScaleValues(Vec3d(1,1,1)), mVoxelSize(Vec3d(1,1,1)), + mScaleValuesInverse(Vec3d(1,1,1)), + mInvScaleSqr(1,1,1), mInvTwiceScale(0.5,0.5,0.5){} + + ScaleMap(const Vec3d& scale): + MapBase(), + mScaleValues(scale), + mVoxelSize(Vec3d(std::abs(scale(0)),std::abs(scale(1)), std::abs(scale(2)))) + { + double determinant = scale[0]* scale[1] * scale[2]; + if (std::abs(determinant) < 3.0 * math::Tolerance::value()) { + OPENVDB_THROW(ArithmeticError, "Non-zero scale values required"); + } + mScaleValuesInverse = 1.0 / mScaleValues; + mInvScaleSqr = mScaleValuesInverse * mScaleValuesInverse; + mInvTwiceScale = mScaleValuesInverse / 2; + } + + ScaleMap(const ScaleMap& other): + MapBase(), + mScaleValues(other.mScaleValues), + mVoxelSize(other.mVoxelSize), + mScaleValuesInverse(other.mScaleValuesInverse), + mInvScaleSqr(other.mInvScaleSqr), + mInvTwiceScale(other.mInvTwiceScale) + { + } + + ~ScaleMap() override = default; + + /// Return a MapBase::Ptr to a new ScaleMap + static MapBase::Ptr create() { return MapBase::Ptr(new ScaleMap()); } + /// Return a MapBase::Ptr to a deep copy of this map + MapBase::Ptr copy() const override { return MapBase::Ptr(new ScaleMap(*this)); } + + MapBase::Ptr inverseMap() const override { + return MapBase::Ptr(new ScaleMap(mScaleValuesInverse)); + } + + static bool isRegistered() { return MapRegistry::isRegistered(ScaleMap::mapType()); } + + static void registerMap() + { + MapRegistry::registerMap( + ScaleMap::mapType(), + ScaleMap::create); + } + + Name type() const override { return mapType(); } + static Name mapType() { return Name("ScaleMap"); } + + /// Return @c true (a ScaleMap is always linear). + bool isLinear() const override { return true; } + + /// Return @c true if the values have the same magitude (eg. -1, 1, -1 would be a rotation). + bool hasUniformScale() const override + { + bool value = isApproxEqual( + std::abs(mScaleValues.x()), std::abs(mScaleValues.y()), double(5e-7)); + value = value && isApproxEqual( + std::abs(mScaleValues.x()), std::abs(mScaleValues.z()), double(5e-7)); + return value; + } + + /// Return the image of @c in under the map + Vec3d applyMap(const Vec3d& in) const override + { + return Vec3d( + in.x() * mScaleValues.x(), + in.y() * mScaleValues.y(), + in.z() * mScaleValues.z()); + } + /// Return the pre-image of @c in under the map + Vec3d applyInverseMap(const Vec3d& in) const override + { + return Vec3d( + in.x() * mScaleValuesInverse.x(), + in.y() * mScaleValuesInverse.y(), + in.z() * mScaleValuesInverse.z()); + } + /// Return the Jacobian of the map applied to @a in. + Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const override { return applyJacobian(in); } + /// Return the Jacobian of the map applied to @a in. + Vec3d applyJacobian(const Vec3d& in) const override { return applyMap(in); } + + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const override { + return applyInverseJacobian(in); + } + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const override { return applyInverseMap(in); } + + /// @brief Return the Jacobian Transpose of the map applied to @a in. + /// @details This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const override { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const override { return applyMap(in); } + + /// @brief Return the transpose of the inverse Jacobian of the map applied to @a in. + /// @details Ignores second argument + Vec3d applyIJT(const Vec3d& in, const Vec3d&) const override { return applyIJT(in);} + /// Return the transpose of the inverse Jacobian of the map applied to @c in + Vec3d applyIJT(const Vec3d& in) const override { return applyInverseMap(in); } + /// Return the Jacobian Curvature: zero for a linear map + Mat3d applyIJC(const Mat3d& in) const override + { + Mat3d tmp; + for (int i = 0; i < 3; i++) { + tmp.setRow(i, in.row(i) * mScaleValuesInverse(i)); + } + for (int i = 0; i < 3; i++) { + tmp.setCol(i, tmp.col(i) * mScaleValuesInverse(i)); + } + return tmp; + } + Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d&) const override { + return applyIJC(in); + } + /// Return the product of the scale values, ignores argument + double determinant(const Vec3d&) const override { return determinant(); } + /// Return the product of the scale values + double determinant() const override { + return mScaleValues.x() * mScaleValues.y() * mScaleValues.z(); + } + + /// Return the scale values that define the map + const Vec3d& getScale() const {return mScaleValues;} + + /// Return the square of the scale. Used to optimize some finite difference calculations + const Vec3d& getInvScaleSqr() const { return mInvScaleSqr; } + /// Return 1/(2 scale). Used to optimize some finite difference calculations + const Vec3d& getInvTwiceScale() const { return mInvTwiceScale; } + /// Return 1/(scale) + const Vec3d& getInvScale() const { return mScaleValuesInverse; } + + //@{ + /// @brief Return the lengths of the images of the segments + /// (0,0,0) − 1,0,0), (0,0,0) − (0,1,0) and (0,0,0) − (0,0,1). + /// @details This is equivalent to the absolute values of the scale values + Vec3d voxelSize() const override { return mVoxelSize; } + Vec3d voxelSize(const Vec3d&) const override { return voxelSize(); } + //@} + + /// read serialization + void read(std::istream& is) override + { + mScaleValues.read(is); + mVoxelSize.read(is); + mScaleValuesInverse.read(is); + mInvScaleSqr.read(is); + mInvTwiceScale.read(is); + } + /// write serialization + void write(std::ostream& os) const override + { + mScaleValues.write(os); + mVoxelSize.write(os); + mScaleValuesInverse.write(os); + mInvScaleSqr.write(os); + mInvTwiceScale.write(os); + } + /// string serialization, useful for debuging + std::string str() const override + { + std::ostringstream buffer; + buffer << " - scale: " << mScaleValues << std::endl; + buffer << " - voxel dimensions: " << mVoxelSize << std::endl; + return buffer.str(); + } + + bool isEqual(const MapBase& other) const override { return isEqualBase(*this, other); } + + bool operator==(const ScaleMap& other) const + { + // ::eq() uses a tolerance + if (!mScaleValues.eq(other.mScaleValues)) { return false; } + return true; + } + + bool operator!=(const ScaleMap& other) const { return !(*this == other); } + + /// Return a AffineMap equivalent to this map + AffineMap::Ptr getAffineMap() const override + { + return AffineMap::Ptr(new AffineMap(math::scale(mScaleValues))); + } + + + + //@{ + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the appropraite operation to the existing map + MapBase::Ptr preRotate(double radians, Axis axis) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreRotation(axis, radians); + return simplify(affineMap); + } + + MapBase::Ptr preTranslate(const Vec3d&) const override; + MapBase::Ptr preScale(const Vec3d&) const override; + MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreShear(axis0, axis1, shear); + return simplify(affineMap); + } + //@} + + + //@{ + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the appropraite operation to the existing map. + MapBase::Ptr postRotate(double radians, Axis axis) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostRotation(axis, radians); + return simplify(affineMap); + } + MapBase::Ptr postTranslate(const Vec3d&) const override; + MapBase::Ptr postScale(const Vec3d&) const override; + MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostShear(axis0, axis1, shear); + return simplify(affineMap); + } + //@} + +private: + Vec3d mScaleValues, mVoxelSize, mScaleValuesInverse, mInvScaleSqr, mInvTwiceScale; +}; // class ScaleMap + + +/// @brief A specialized Affine transform that scales along the principal axis +/// the scaling is uniform in the three-directions +class OPENVDB_API UniformScaleMap: public ScaleMap +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + UniformScaleMap(): ScaleMap(Vec3d(1,1,1)) {} + UniformScaleMap(double scale): ScaleMap(Vec3d(scale, scale, scale)) {} + UniformScaleMap(const UniformScaleMap& other): ScaleMap(other) {} + ~UniformScaleMap() override = default; + + /// Return a MapBase::Ptr to a new UniformScaleMap + static MapBase::Ptr create() { return MapBase::Ptr(new UniformScaleMap()); } + /// Return a MapBase::Ptr to a deep copy of this map + MapBase::Ptr copy() const override { return MapBase::Ptr(new UniformScaleMap(*this)); } + + MapBase::Ptr inverseMap() const override + { + const Vec3d& invScale = getInvScale(); + return MapBase::Ptr(new UniformScaleMap( invScale[0])); + } + + static bool isRegistered() { return MapRegistry::isRegistered(UniformScaleMap::mapType()); } + static void registerMap() + { + MapRegistry::registerMap( + UniformScaleMap::mapType(), + UniformScaleMap::create); + } + + Name type() const override { return mapType(); } + static Name mapType() { return Name("UniformScaleMap"); } + + bool isEqual(const MapBase& other) const override { return isEqualBase(*this, other); } + + bool operator==(const UniformScaleMap& other) const { return ScaleMap::operator==(other); } + bool operator!=(const UniformScaleMap& other) const { return !(*this == other); } + + /// @brief Return a MapBase::Ptr to a UniformScaleTraslateMap that is the result of + /// pre-translation on this map + MapBase::Ptr preTranslate(const Vec3d&) const override; + + /// @brief Return a MapBase::Ptr to a UniformScaleTraslateMap that is the result of + /// post-translation on this map + MapBase::Ptr postTranslate(const Vec3d&) const override; + +}; // class UniformScaleMap + + +//////////////////////////////////////// + + +inline MapBase::Ptr +ScaleMap::preScale(const Vec3d& v) const +{ + const Vec3d new_scale(v * mScaleValues); + if (isApproxEqual(new_scale[0],new_scale[1]) && isApproxEqual(new_scale[0],new_scale[2])) { + return MapBase::Ptr(new UniformScaleMap(new_scale[0])); + } else { + return MapBase::Ptr(new ScaleMap(new_scale)); + } +} + + +inline MapBase::Ptr +ScaleMap::postScale(const Vec3d& v) const +{ // pre-post Scale are the same for a scale map + return preScale(v); +} + + +/// @brief A specialized linear transform that performs a translation +class OPENVDB_API TranslationMap: public MapBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + // default constructor is a translation by zero. + TranslationMap(): MapBase(), mTranslation(Vec3d(0,0,0)) {} + TranslationMap(const Vec3d& t): MapBase(), mTranslation(t) {} + TranslationMap(const TranslationMap& other): MapBase(), mTranslation(other.mTranslation) {} + + ~TranslationMap() override = default; + + /// Return a MapBase::Ptr to a new TranslationMap + static MapBase::Ptr create() { return MapBase::Ptr(new TranslationMap()); } + /// Return a MapBase::Ptr to a deep copy of this map + MapBase::Ptr copy() const override { return MapBase::Ptr(new TranslationMap(*this)); } + + MapBase::Ptr inverseMap() const override { + return MapBase::Ptr(new TranslationMap(-mTranslation)); + } + + static bool isRegistered() { return MapRegistry::isRegistered(TranslationMap::mapType()); } + + static void registerMap() + { + MapRegistry::registerMap( + TranslationMap::mapType(), + TranslationMap::create); + } + + Name type() const override { return mapType(); } + static Name mapType() { return Name("TranslationMap"); } + + /// Return @c true (a TranslationMap is always linear). + bool isLinear() const override { return true; } + + /// Return @c false (by convention true) + bool hasUniformScale() const override { return true; } + + /// Return the image of @c in under the map + Vec3d applyMap(const Vec3d& in) const override { return in + mTranslation; } + /// Return the pre-image of @c in under the map + Vec3d applyInverseMap(const Vec3d& in) const override { return in - mTranslation; } + /// Return the Jacobian of the map applied to @a in. + Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const override { return applyJacobian(in); } + /// Return the Jacobian of the map applied to @a in. + Vec3d applyJacobian(const Vec3d& in) const override { return in; } + + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const override { + return applyInverseJacobian(in); + } + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const override { return in; } + + + /// @brief Return the Jacobian Transpose of the map applied to @a in. + /// @details This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const override { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const override { return in; } + + /// @brief Return the transpose of the inverse Jacobian (Identity for TranslationMap) + /// of the map applied to @c in, ignores second argument + Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const override { return applyIJT(in);} + /// @brief Return the transpose of the inverse Jacobian (Identity for TranslationMap) + /// of the map applied to @c in + Vec3d applyIJT(const Vec3d& in) const override {return in;} + /// Return the Jacobian Curvature: zero for a linear map + Mat3d applyIJC(const Mat3d& mat) const override {return mat;} + Mat3d applyIJC(const Mat3d& mat, const Vec3d&, const Vec3d&) const override { + return applyIJC(mat); + } + + /// Return @c 1 + double determinant(const Vec3d& ) const override { return determinant(); } + /// Return @c 1 + double determinant() const override { return 1.0; } + + /// Return (1,1,1). + Vec3d voxelSize() const override { return Vec3d(1,1,1);} + /// Return (1,1,1). + Vec3d voxelSize(const Vec3d&) const override { return voxelSize();} + + /// Return the translation vector + const Vec3d& getTranslation() const { return mTranslation; } + + /// read serialization + void read(std::istream& is) override { mTranslation.read(is); } + /// write serialization + void write(std::ostream& os) const override { mTranslation.write(os); } + /// string serialization, useful for debuging + std::string str() const override + { + std::ostringstream buffer; + buffer << " - translation: " << mTranslation << std::endl; + return buffer.str(); + } + + bool isEqual(const MapBase& other) const override { return isEqualBase(*this, other); } + + bool operator==(const TranslationMap& other) const + { + // ::eq() uses a tolerance + return mTranslation.eq(other.mTranslation); + } + + bool operator!=(const TranslationMap& other) const { return !(*this == other); } + + /// Return AffineMap::Ptr to an AffineMap equivalent to *this + AffineMap::Ptr getAffineMap() const override + { + Mat4d matrix(Mat4d::identity()); + matrix.setTranslation(mTranslation); + + AffineMap::Ptr affineMap(new AffineMap(matrix)); + return affineMap; + } + + //@{ + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the appropriate operation. + MapBase::Ptr preRotate(double radians, Axis axis) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreRotation(axis, radians); + return simplify(affineMap); + + } + MapBase::Ptr preTranslate(const Vec3d& t) const override + { + return MapBase::Ptr(new TranslationMap(t + mTranslation)); + } + + MapBase::Ptr preScale(const Vec3d& v) const override; + + MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreShear(axis0, axis1, shear); + return simplify(affineMap); + } + //@} + + //@{ + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of postfixing the appropriate operation. + MapBase::Ptr postRotate(double radians, Axis axis) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostRotation(axis, radians); + return simplify(affineMap); + + } + MapBase::Ptr postTranslate(const Vec3d& t) const override + { // post and pre are the same for this + return MapBase::Ptr(new TranslationMap(t + mTranslation)); + } + + MapBase::Ptr postScale(const Vec3d& v) const override; + + MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostShear(axis0, axis1, shear); + return simplify(affineMap); + } + //@} + +private: + Vec3d mTranslation; +}; // class TranslationMap + + +//////////////////////////////////////// + + +/// @brief A specialized Affine transform that scales along the principal axis +/// the scaling need not be uniform in the three-directions, and then +/// translates the result. +class OPENVDB_API ScaleTranslateMap: public MapBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + ScaleTranslateMap(): + MapBase(), + mTranslation(Vec3d(0,0,0)), + mScaleValues(Vec3d(1,1,1)), + mVoxelSize(Vec3d(1,1,1)), + mScaleValuesInverse(Vec3d(1,1,1)), + mInvScaleSqr(1,1,1), + mInvTwiceScale(0.5,0.5,0.5) + { + } + + ScaleTranslateMap(const Vec3d& scale, const Vec3d& translate): + MapBase(), + mTranslation(translate), + mScaleValues(scale), + mVoxelSize(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2))) + { + const double determinant = scale[0]* scale[1] * scale[2]; + if (std::abs(determinant) < 3.0 * math::Tolerance::value()) { + OPENVDB_THROW(ArithmeticError, "Non-zero scale values required"); + } + mScaleValuesInverse = 1.0 / mScaleValues; + mInvScaleSqr = mScaleValuesInverse * mScaleValuesInverse; + mInvTwiceScale = mScaleValuesInverse / 2; + } + + ScaleTranslateMap(const ScaleMap& scale, const TranslationMap& translate): + MapBase(), + mTranslation(translate.getTranslation()), + mScaleValues(scale.getScale()), + mVoxelSize(std::abs(mScaleValues(0)), + std::abs(mScaleValues(1)), + std::abs(mScaleValues(2))), + mScaleValuesInverse(1.0 / scale.getScale()) + { + mInvScaleSqr = mScaleValuesInverse * mScaleValuesInverse; + mInvTwiceScale = mScaleValuesInverse / 2; + } + + ScaleTranslateMap(const ScaleTranslateMap& other): + MapBase(), + mTranslation(other.mTranslation), + mScaleValues(other.mScaleValues), + mVoxelSize(other.mVoxelSize), + mScaleValuesInverse(other.mScaleValuesInverse), + mInvScaleSqr(other.mInvScaleSqr), + mInvTwiceScale(other.mInvTwiceScale) + {} + + ~ScaleTranslateMap() override = default; + + /// Return a MapBase::Ptr to a new ScaleTranslateMap + static MapBase::Ptr create() { return MapBase::Ptr(new ScaleTranslateMap()); } + /// Return a MapBase::Ptr to a deep copy of this map + MapBase::Ptr copy() const override { return MapBase::Ptr(new ScaleTranslateMap(*this)); } + + MapBase::Ptr inverseMap() const override + { + return MapBase::Ptr(new ScaleTranslateMap( + mScaleValuesInverse, -mScaleValuesInverse * mTranslation)); + } + + static bool isRegistered() { return MapRegistry::isRegistered(ScaleTranslateMap::mapType()); } + + static void registerMap() + { + MapRegistry::registerMap( + ScaleTranslateMap::mapType(), + ScaleTranslateMap::create); + } + + Name type() const override { return mapType(); } + static Name mapType() { return Name("ScaleTranslateMap"); } + + /// Return @c true (a ScaleTranslateMap is always linear). + bool isLinear() const override { return true; } + + /// @brief Return @c true if the scale values have the same magnitude + /// (eg. -1, 1, -1 would be a rotation). + bool hasUniformScale() const override + { + bool value = isApproxEqual( + std::abs(mScaleValues.x()), std::abs(mScaleValues.y()), double(5e-7)); + value = value && isApproxEqual( + std::abs(mScaleValues.x()), std::abs(mScaleValues.z()), double(5e-7)); + return value; + } + + /// Return the image of @c under the map + Vec3d applyMap(const Vec3d& in) const override + { + return Vec3d( + in.x() * mScaleValues.x() + mTranslation.x(), + in.y() * mScaleValues.y() + mTranslation.y(), + in.z() * mScaleValues.z() + mTranslation.z()); + } + /// Return the pre-image of @c under the map + Vec3d applyInverseMap(const Vec3d& in) const override + { + return Vec3d( + (in.x() - mTranslation.x() ) * mScaleValuesInverse.x(), + (in.y() - mTranslation.y() ) * mScaleValuesInverse.y(), + (in.z() - mTranslation.z() ) * mScaleValuesInverse.z()); + } + + /// Return the Jacobian of the map applied to @a in. + Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const override { return applyJacobian(in); } + /// Return the Jacobian of the map applied to @a in. + Vec3d applyJacobian(const Vec3d& in) const override { return in * mScaleValues; } + + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const override { return applyInverseJacobian(in); } + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const override { return in * mScaleValuesInverse; } + + /// @brief Return the Jacobian Transpose of the map applied to @a in. + /// @details This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const override { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const override { return applyJacobian(in); } + + /// @brief Return the transpose of the inverse Jacobian of the map applied to @a in + /// @details Ignores second argument + Vec3d applyIJT(const Vec3d& in, const Vec3d&) const override { return applyIJT(in);} + /// Return the transpose of the inverse Jacobian of the map applied to @c in + Vec3d applyIJT(const Vec3d& in) const override + { + return Vec3d( + in.x() * mScaleValuesInverse.x(), + in.y() * mScaleValuesInverse.y(), + in.z() * mScaleValuesInverse.z()); + } + /// Return the Jacobian Curvature: zero for a linear map + Mat3d applyIJC(const Mat3d& in) const override + { + Mat3d tmp; + for (int i=0; i<3; i++){ + tmp.setRow(i, in.row(i)*mScaleValuesInverse(i)); + } + for (int i=0; i<3; i++){ + tmp.setCol(i, tmp.col(i)*mScaleValuesInverse(i)); + } + return tmp; + } + Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const override { + return applyIJC(in); + } + + /// Return the product of the scale values, ignores argument + double determinant(const Vec3d&) const override { return determinant(); } + /// Return the product of the scale values + double determinant() const override { + return mScaleValues.x() * mScaleValues.y() * mScaleValues.z(); + } + /// Return the absolute values of the scale values + Vec3d voxelSize() const override { return mVoxelSize;} + /// Return the absolute values of the scale values, ignores argument + Vec3d voxelSize(const Vec3d&) const override { return voxelSize();} + + /// Returns the scale values + const Vec3d& getScale() const { return mScaleValues; } + /// Returns the translation + const Vec3d& getTranslation() const { return mTranslation; } + + /// Return the square of the scale. Used to optimize some finite difference calculations + const Vec3d& getInvScaleSqr() const {return mInvScaleSqr;} + /// Return 1/(2 scale). Used to optimize some finite difference calculations + const Vec3d& getInvTwiceScale() const {return mInvTwiceScale;} + /// Return 1/(scale) + const Vec3d& getInvScale() const {return mScaleValuesInverse; } + + /// read serialization + void read(std::istream& is) override + { + mTranslation.read(is); + mScaleValues.read(is); + mVoxelSize.read(is); + mScaleValuesInverse.read(is); + mInvScaleSqr.read(is); + mInvTwiceScale.read(is); + } + /// write serialization + void write(std::ostream& os) const override + { + mTranslation.write(os); + mScaleValues.write(os); + mVoxelSize.write(os); + mScaleValuesInverse.write(os); + mInvScaleSqr.write(os); + mInvTwiceScale.write(os); + } + /// string serialization, useful for debuging + std::string str() const override + { + std::ostringstream buffer; + buffer << " - translation: " << mTranslation << std::endl; + buffer << " - scale: " << mScaleValues << std::endl; + buffer << " - voxel dimensions: " << mVoxelSize << std::endl; + return buffer.str(); + } + + bool isEqual(const MapBase& other) const override { return isEqualBase(*this, other); } + + bool operator==(const ScaleTranslateMap& other) const + { + // ::eq() uses a tolerance + if (!mScaleValues.eq(other.mScaleValues)) { return false; } + if (!mTranslation.eq(other.mTranslation)) { return false; } + return true; + } + + bool operator!=(const ScaleTranslateMap& other) const { return !(*this == other); } + + /// Return AffineMap::Ptr to an AffineMap equivalent to *this + AffineMap::Ptr getAffineMap() const override + { + AffineMap::Ptr affineMap(new AffineMap(math::scale(mScaleValues))); + affineMap->accumPostTranslation(mTranslation); + return affineMap; + } + + //@{ + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the appropraite operation. + MapBase::Ptr preRotate(double radians, Axis axis) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreRotation(axis, radians); + return simplify(affineMap); + } + MapBase::Ptr preTranslate(const Vec3d& t) const override + { + const Vec3d& s = mScaleValues; + const Vec3d scaled_trans( t.x() * s.x(), + t.y() * s.y(), + t.z() * s.z() ); + return MapBase::Ptr( new ScaleTranslateMap(mScaleValues, mTranslation + scaled_trans)); + } + + MapBase::Ptr preScale(const Vec3d& v) const override; + + MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreShear(axis0, axis1, shear); + return simplify(affineMap); + } + //@} + + //@{ + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of postfixing the appropraite operation. + MapBase::Ptr postRotate(double radians, Axis axis) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostRotation(axis, radians); + return simplify(affineMap); + } + MapBase::Ptr postTranslate(const Vec3d& t) const override + { + return MapBase::Ptr( new ScaleTranslateMap(mScaleValues, mTranslation + t)); + } + + MapBase::Ptr postScale(const Vec3d& v) const override; + + MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostShear(axis0, axis1, shear); + return simplify(affineMap); + } + //@} + +private: + Vec3d mTranslation, mScaleValues, mVoxelSize, mScaleValuesInverse, + mInvScaleSqr, mInvTwiceScale; +}; // class ScaleTanslateMap + + +inline MapBase::Ptr +ScaleMap::postTranslate(const Vec3d& t) const +{ + return MapBase::Ptr(new ScaleTranslateMap(mScaleValues, t)); +} + + +inline MapBase::Ptr +ScaleMap::preTranslate(const Vec3d& t) const +{ + + const Vec3d& s = mScaleValues; + const Vec3d scaled_trans( t.x() * s.x(), + t.y() * s.y(), + t.z() * s.z() ); + return MapBase::Ptr(new ScaleTranslateMap(mScaleValues, scaled_trans)); +} + + +/// @brief A specialized Affine transform that uniformaly scales along the principal axis +/// and then translates the result. +class OPENVDB_API UniformScaleTranslateMap: public ScaleTranslateMap +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + UniformScaleTranslateMap():ScaleTranslateMap(Vec3d(1,1,1), Vec3d(0,0,0)) {} + UniformScaleTranslateMap(double scale, const Vec3d& translate): + ScaleTranslateMap(Vec3d(scale,scale,scale), translate) {} + UniformScaleTranslateMap(const UniformScaleMap& scale, const TranslationMap& translate): + ScaleTranslateMap(scale.getScale(), translate.getTranslation()) {} + + UniformScaleTranslateMap(const UniformScaleTranslateMap& other):ScaleTranslateMap(other) {} + ~UniformScaleTranslateMap() override = default; + + /// Return a MapBase::Ptr to a new UniformScaleTranslateMap + static MapBase::Ptr create() { return MapBase::Ptr(new UniformScaleTranslateMap()); } + /// Return a MapBase::Ptr to a deep copy of this map + MapBase::Ptr copy() const override { return MapBase::Ptr(new UniformScaleTranslateMap(*this)); } + + MapBase::Ptr inverseMap() const override + { + const Vec3d& scaleInv = getInvScale(); + const Vec3d& trans = getTranslation(); + return MapBase::Ptr(new UniformScaleTranslateMap(scaleInv[0], -scaleInv[0] * trans)); + } + + static bool isRegistered() + { + return MapRegistry::isRegistered(UniformScaleTranslateMap::mapType()); + } + + static void registerMap() + { + MapRegistry::registerMap( + UniformScaleTranslateMap::mapType(), UniformScaleTranslateMap::create); + } + + Name type() const override { return mapType(); } + static Name mapType() { return Name("UniformScaleTranslateMap"); } + + bool isEqual(const MapBase& other) const override { return isEqualBase(*this, other); } + + bool operator==(const UniformScaleTranslateMap& other) const + { + return ScaleTranslateMap::operator==(other); + } + bool operator!=(const UniformScaleTranslateMap& other) const { return !(*this == other); } + + /// @brief Return a MapBase::Ptr to a UniformScaleTranslateMap that is + /// the result of prepending translation on this map. + MapBase::Ptr preTranslate(const Vec3d& t) const override + { + const double scale = this->getScale().x(); + const Vec3d new_trans = this->getTranslation() + scale * t; + return MapBase::Ptr( new UniformScaleTranslateMap(scale, new_trans)); + } + + /// @brief Return a MapBase::Ptr to a UniformScaleTranslateMap that is + /// the result of postfixing translation on this map. + MapBase::Ptr postTranslate(const Vec3d& t) const override + { + const double scale = this->getScale().x(); + return MapBase::Ptr( new UniformScaleTranslateMap(scale, this->getTranslation() + t)); + } +}; // class UniformScaleTanslateMap + + +inline MapBase::Ptr +UniformScaleMap::postTranslate(const Vec3d& t) const +{ + const double scale = this->getScale().x(); + return MapBase::Ptr(new UniformScaleTranslateMap(scale, t)); +} + + +inline MapBase::Ptr +UniformScaleMap::preTranslate(const Vec3d& t) const +{ + const double scale = this->getScale().x(); + return MapBase::Ptr(new UniformScaleTranslateMap(scale, scale*t)); +} + + +inline MapBase::Ptr +TranslationMap::preScale(const Vec3d& v) const +{ + if (isApproxEqual(v[0],v[1]) && isApproxEqual(v[0],v[2])) { + return MapBase::Ptr(new UniformScaleTranslateMap(v[0], mTranslation)); + } else { + return MapBase::Ptr(new ScaleTranslateMap(v, mTranslation)); + } +} + + +inline MapBase::Ptr +TranslationMap::postScale(const Vec3d& v) const +{ + if (isApproxEqual(v[0],v[1]) && isApproxEqual(v[0],v[2])) { + return MapBase::Ptr(new UniformScaleTranslateMap(v[0], v[0]*mTranslation)); + } else { + const Vec3d trans(mTranslation.x()*v.x(), + mTranslation.y()*v.y(), + mTranslation.z()*v.z()); + return MapBase::Ptr(new ScaleTranslateMap(v, trans)); + } +} + + +inline MapBase::Ptr +ScaleTranslateMap::preScale(const Vec3d& v) const +{ + const Vec3d new_scale( v * mScaleValues ); + if (isApproxEqual(new_scale[0],new_scale[1]) && isApproxEqual(new_scale[0],new_scale[2])) { + return MapBase::Ptr( new UniformScaleTranslateMap(new_scale[0], mTranslation)); + } else { + return MapBase::Ptr( new ScaleTranslateMap(new_scale, mTranslation)); + } +} + + +inline MapBase::Ptr +ScaleTranslateMap::postScale(const Vec3d& v) const +{ + const Vec3d new_scale( v * mScaleValues ); + const Vec3d new_trans( mTranslation.x()*v.x(), + mTranslation.y()*v.y(), + mTranslation.z()*v.z() ); + + if (isApproxEqual(new_scale[0],new_scale[1]) && isApproxEqual(new_scale[0],new_scale[2])) { + return MapBase::Ptr( new UniformScaleTranslateMap(new_scale[0], new_trans)); + } else { + return MapBase::Ptr( new ScaleTranslateMap(new_scale, new_trans)); + } +} + + +//////////////////////////////////////// + + +/// @brief A specialized linear transform that performs a unitary maping +/// i.e. rotation and or reflection. +class OPENVDB_API UnitaryMap: public MapBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + /// default constructor makes an Idenity. + UnitaryMap(): mAffineMap(Mat4d::identity()) + { + } + + UnitaryMap(const Vec3d& axis, double radians) + { + Mat3d matrix; + matrix.setToRotation(axis, radians); + mAffineMap = AffineMap(matrix); + } + + UnitaryMap(Axis axis, double radians) + { + Mat4d matrix; + matrix.setToRotation(axis, radians); + mAffineMap = AffineMap(matrix); + } + + UnitaryMap(const Mat3d& m) + { + // test that the mat3 is a rotation || reflection + if (!isUnitary(m)) { + OPENVDB_THROW(ArithmeticError, "Matrix initializing unitary map was not unitary"); + } + + Mat4d matrix(Mat4d::identity()); + matrix.setMat3(m); + mAffineMap = AffineMap(matrix); + } + + UnitaryMap(const Mat4d& m) + { + if (!isInvertible(m)) { + OPENVDB_THROW(ArithmeticError, + "4x4 Matrix initializing unitary map was not unitary: not invertible"); + } + + if (!isAffine(m)) { + OPENVDB_THROW(ArithmeticError, + "4x4 Matrix initializing unitary map was not unitary: not affine"); + } + + if (hasTranslation(m)) { + OPENVDB_THROW(ArithmeticError, + "4x4 Matrix initializing unitary map was not unitary: had translation"); + } + + if (!isUnitary(m.getMat3())) { + OPENVDB_THROW(ArithmeticError, + "4x4 Matrix initializing unitary map was not unitary"); + } + + mAffineMap = AffineMap(m); + } + + UnitaryMap(const UnitaryMap& other): + MapBase(other), + mAffineMap(other.mAffineMap) + { + } + + UnitaryMap(const UnitaryMap& first, const UnitaryMap& second): + mAffineMap(*(first.getAffineMap()), *(second.getAffineMap())) + { + } + + ~UnitaryMap() override = default; + + /// Return a MapBase::Ptr to a new UnitaryMap + static MapBase::Ptr create() { return MapBase::Ptr(new UnitaryMap()); } + /// Returns a MapBase::Ptr to a deep copy of *this + MapBase::Ptr copy() const override { return MapBase::Ptr(new UnitaryMap(*this)); } + + MapBase::Ptr inverseMap() const override + { + return MapBase::Ptr(new UnitaryMap(mAffineMap.getMat4().inverse())); + } + + static bool isRegistered() { return MapRegistry::isRegistered(UnitaryMap::mapType()); } + + static void registerMap() + { + MapRegistry::registerMap( + UnitaryMap::mapType(), + UnitaryMap::create); + } + + /// Return @c UnitaryMap + Name type() const override { return mapType(); } + /// Return @c UnitaryMap + static Name mapType() { return Name("UnitaryMap"); } + + /// Return @c true (a UnitaryMap is always linear). + bool isLinear() const override { return true; } + + /// Return @c false (by convention true) + bool hasUniformScale() const override { return true; } + + bool isEqual(const MapBase& other) const override { return isEqualBase(*this, other); } + + bool operator==(const UnitaryMap& other) const + { + // compare underlying linear map. + if (mAffineMap!=other.mAffineMap) return false; + return true; + } + + bool operator!=(const UnitaryMap& other) const { return !(*this == other); } + /// Return the image of @c in under the map + Vec3d applyMap(const Vec3d& in) const override { return mAffineMap.applyMap(in); } + /// Return the pre-image of @c in under the map + Vec3d applyInverseMap(const Vec3d& in) const override { return mAffineMap.applyInverseMap(in); } + + Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const override { return applyJacobian(in); } + /// Return the Jacobian of the map applied to @a in. + Vec3d applyJacobian(const Vec3d& in) const override { return mAffineMap.applyJacobian(in); } + + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const override { + return applyInverseJacobian(in); + } + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const override { + return mAffineMap.applyInverseJacobian(in); + } + + /// @brief Return the Jacobian Transpose of the map applied to @a in. + /// @details This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const override { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const override { + return applyInverseMap(in); // the transpose of the unitary map is its inverse + } + + + /// @brief Return the transpose of the inverse Jacobian of the map applied to @a in + /// @details Ignores second argument + Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const override { return applyIJT(in);} + /// Return the transpose of the inverse Jacobian of the map applied to @c in + Vec3d applyIJT(const Vec3d& in) const override { return mAffineMap.applyIJT(in); } + /// Return the Jacobian Curvature: zero for a linear map + Mat3d applyIJC(const Mat3d& in) const override { return mAffineMap.applyIJC(in); } + Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const override { + return applyIJC(in); + } + + /// Return the determinant of the Jacobian, ignores argument + double determinant(const Vec3d&) const override { return determinant(); } + /// Return the determinant of the Jacobian + double determinant() const override { return mAffineMap.determinant(); } + + + /// @{ + /// @brief Returns the lengths of the images of the segments + /// (0,0,0) − (1,0,0), (0,0,0) − (0,1,0) and (0,0,0) − (0,0,1). + Vec3d voxelSize() const override { return mAffineMap.voxelSize();} + Vec3d voxelSize(const Vec3d&) const override { return voxelSize();} + /// @} + + /// read serialization + void read(std::istream& is) override + { + mAffineMap.read(is); + } + + /// write serialization + void write(std::ostream& os) const override + { + mAffineMap.write(os); + } + /// string serialization, useful for debuging + std::string str() const override + { + std::ostringstream buffer; + buffer << mAffineMap.str(); + return buffer.str(); + } + /// Return AffineMap::Ptr to an AffineMap equivalent to *this + AffineMap::Ptr getAffineMap() const override { + return AffineMap::Ptr(new AffineMap(mAffineMap)); + } + + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the given rotation. + MapBase::Ptr preRotate(double radians, Axis axis) const override + { + UnitaryMap first(axis, radians); + UnitaryMap::Ptr unitaryMap(new UnitaryMap(first, *this)); + return StaticPtrCast(unitaryMap); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the given translation. + MapBase::Ptr preTranslate(const Vec3d& t) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreTranslation(t); + return simplify(affineMap); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the given scale. + MapBase::Ptr preScale(const Vec3d& v) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreScale(v); + return simplify(affineMap); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the given shear. + MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreShear(axis0, axis1, shear); + return simplify(affineMap); + } + + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of appending the given rotation. + MapBase::Ptr postRotate(double radians, Axis axis) const override + { + UnitaryMap second(axis, radians); + UnitaryMap::Ptr unitaryMap(new UnitaryMap(*this, second)); + return StaticPtrCast(unitaryMap); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of appending the given translation. + MapBase::Ptr postTranslate(const Vec3d& t) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostTranslation(t); + return simplify(affineMap); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of appending the given scale. + MapBase::Ptr postScale(const Vec3d& v) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostScale(v); + return simplify(affineMap); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of appending the given shear. + MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const override + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostShear(axis0, axis1, shear); + return simplify(affineMap); + } + +private: + AffineMap mAffineMap; +}; // class UnitaryMap + + +//////////////////////////////////////// + + +/// @brief This map is composed of three steps. +/// First it will take a box of size (Lx X Ly X Lz) defined by a member data bounding box +/// and map it into a frustum with near plane (1 X Ly/Lx) and prescribed depth +/// Then this frustum is transformed by an internal second map: most often a uniform scale, +/// but other effects can be achieved by accumulating translation, shear and rotation: these +/// are all applied to the second map +class OPENVDB_API NonlinearFrustumMap: public MapBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + NonlinearFrustumMap(): + MapBase(), + mBBox(Vec3d(0), Vec3d(1)), + mTaper(1), + mDepth(1) + { + init(); + } + + /// @brief Constructor that takes an index-space bounding box + /// to be mapped into a frustum with a given @a depth and @a taper + /// (defined as ratio of nearplane/farplane). + NonlinearFrustumMap(const BBoxd& bb, double taper, double depth): + MapBase(),mBBox(bb), mTaper(taper), mDepth(depth) + { + init(); + } + + /// @brief Constructor that takes an index-space bounding box + /// to be mapped into a frustum with a given @a depth and @a taper + /// (defined as ratio of nearplane/farplane). + /// @details This frustum is further modifed by the @a secondMap, + /// intended to be a simple translation and rotation and uniform scale + NonlinearFrustumMap(const BBoxd& bb, double taper, double depth, + const MapBase::Ptr& secondMap): + mBBox(bb), mTaper(taper), mDepth(depth) + { + if (!secondMap->isLinear() ) { + OPENVDB_THROW(ArithmeticError, + "The second map in the Frustum transfrom must be linear"); + } + mSecondMap = *( secondMap->getAffineMap() ); + init(); + } + + NonlinearFrustumMap(const NonlinearFrustumMap& other): + MapBase(), + mBBox(other.mBBox), + mTaper(other.mTaper), + mDepth(other.mDepth), + mSecondMap(other.mSecondMap), + mHasSimpleAffine(other.mHasSimpleAffine) + { + init(); + } + + /// @brief Constructor from a camera frustum + /// + /// @param position the tip of the frustum (i.e., the camera's position). + /// @param direction a vector pointing from @a position toward the near plane. + /// @param up a non-unit vector describing the direction and extent of + /// the frustum's intersection on the near plane. Together, + /// @a up must be orthogonal to @a direction. + /// @param aspect the aspect ratio of the frustum intersection with near plane + /// defined as width / height + /// @param z_near,depth the distance from @a position along @a direction to the + /// near and far planes of the frustum. + /// @param x_count the number of voxels, aligned with @a left, + /// across the face of the frustum + /// @param z_count the number of voxels, aligned with @a direction, + /// between the near and far planes + NonlinearFrustumMap(const Vec3d& position, + const Vec3d& direction, + const Vec3d& up, + double aspect /* width / height */, + double z_near, double depth, + Coord::ValueType x_count, Coord::ValueType z_count) { + + /// @todo check that depth > 0 + /// @todo check up.length > 0 + /// @todo check that direction dot up = 0 + if (!(depth > 0)) { + OPENVDB_THROW(ArithmeticError, + "The frustum depth must be non-zero and positive"); + } + if (!(up.length() > 0)) { + OPENVDB_THROW(ArithmeticError, + "The frustum height must be non-zero and positive"); + } + if (!(aspect > 0)) { + OPENVDB_THROW(ArithmeticError, + "The frustum aspect ratio must be non-zero and positive"); + } + if (!(isApproxEqual(up.dot(direction), 0.))) { + OPENVDB_THROW(ArithmeticError, + "The frustum up orientation must be perpendicular to into-frustum direction"); + } + + double near_plane_height = 2 * up.length(); + double near_plane_width = aspect * near_plane_height; + + Coord::ValueType y_count = static_cast(Round(x_count / aspect)); + + mBBox = BBoxd(Vec3d(0,0,0), Vec3d(x_count, y_count, z_count)); + mDepth = depth / near_plane_width; // depth non-dimensionalized on width + double gamma = near_plane_width / z_near; + mTaper = 1./(mDepth*gamma + 1.); + + Vec3d direction_unit = direction; + direction_unit.normalize(); + + Mat4d r1(Mat4d::identity()); + r1.setToRotation(/*from*/Vec3d(0,0,1), /*to */direction_unit); + Mat4d r2(Mat4d::identity()); + Vec3d temp = r1.inverse().transform(up); + r2.setToRotation(/*from*/Vec3d(0,1,0), /*to*/temp ); + Mat4d scale = math::scale( + Vec3d(near_plane_width, near_plane_width, near_plane_width)); + + // move the near plane to origin, rotate to align with axis, and scale down + // T_inv * R1_inv * R2_inv * scale_inv + Mat4d mat = scale * r2 * r1; + mat.setTranslation(position + z_near*direction_unit); + + mSecondMap = AffineMap(mat); + + init(); + } + + ~NonlinearFrustumMap() override = default; + + /// Return a MapBase::Ptr to a new NonlinearFrustumMap + static MapBase::Ptr create() { return MapBase::Ptr(new NonlinearFrustumMap()); } + /// Return a MapBase::Ptr to a deep copy of this map + MapBase::Ptr copy() const override { return MapBase::Ptr(new NonlinearFrustumMap(*this)); } + + /// @brief Not implemented, since there is currently no map type that can + /// represent the inverse of a frustum + /// @throw NotImplementedError + MapBase::Ptr inverseMap() const override + { + OPENVDB_THROW(NotImplementedError, + "inverseMap() is not implemented for NonlinearFrustumMap"); + } + static bool isRegistered() { return MapRegistry::isRegistered(NonlinearFrustumMap::mapType()); } + + static void registerMap() + { + MapRegistry::registerMap( + NonlinearFrustumMap::mapType(), + NonlinearFrustumMap::create); + } + /// Return @c NonlinearFrustumMap + Name type() const override { return mapType(); } + /// Return @c NonlinearFrustumMap + static Name mapType() { return Name("NonlinearFrustumMap"); } + + /// Return @c false (a NonlinearFrustumMap is never linear). + bool isLinear() const override { return false; } + + /// Return @c false (by convention false) + bool hasUniformScale() const override { return false; } + + /// Return @c true if the map is equivalent to an identity + bool isIdentity() const + { + // The frustum can only be consistent with a linear map if the taper value is 1 + if (!isApproxEqual(mTaper, double(1)) ) return false; + + // There are various ways an identity can decomposed between the two parts of the + // map. Best to just check that the principle vectors are stationary. + const Vec3d e1(1,0,0); + if (!applyMap(e1).eq(e1)) return false; + + const Vec3d e2(0,1,0); + if (!applyMap(e2).eq(e2)) return false; + + const Vec3d e3(0,0,1); + if (!applyMap(e3).eq(e3)) return false; + + return true; + } + + bool isEqual(const MapBase& other) const override { return isEqualBase(*this, other); } + + bool operator==(const NonlinearFrustumMap& other) const + { + if (mBBox!=other.mBBox) return false; + if (!isApproxEqual(mTaper, other.mTaper)) return false; + if (!isApproxEqual(mDepth, other.mDepth)) return false; + + // Two linear transforms are equivalent iff they have the same translation + // and have the same affects on orthongal spanning basis check translation + Vec3d e(0,0,0); + if (!mSecondMap.applyMap(e).eq(other.mSecondMap.applyMap(e))) return false; + /// check spanning vectors + e(0) = 1; + if (!mSecondMap.applyMap(e).eq(other.mSecondMap.applyMap(e))) return false; + e(0) = 0; + e(1) = 1; + if (!mSecondMap.applyMap(e).eq(other.mSecondMap.applyMap(e))) return false; + e(1) = 0; + e(2) = 1; + if (!mSecondMap.applyMap(e).eq(other.mSecondMap.applyMap(e))) return false; + return true; + } + + bool operator!=(const NonlinearFrustumMap& other) const { return !(*this == other); } + + /// Return the image of @c in under the map + Vec3d applyMap(const Vec3d& in) const override + { + return mSecondMap.applyMap(applyFrustumMap(in)); + } + + /// Return the pre-image of @c in under the map + Vec3d applyInverseMap(const Vec3d& in) const override + { + return applyFrustumInverseMap(mSecondMap.applyInverseMap(in)); + } + /// Return the Jacobian of the linear second map applied to @c in + Vec3d applyJacobian(const Vec3d& in) const override { return mSecondMap.applyJacobian(in); } + /// Return the Jacobian defined at @c isloc applied to @c in + Vec3d applyJacobian(const Vec3d& in, const Vec3d& isloc) const override + { + // Move the center of the x-face of the bbox + // to the origin in index space. + Vec3d centered(isloc); + centered = centered - mBBox.min(); + centered.x() -= mXo; + centered.y() -= mYo; + + // scale the z-direction on depth / K count + const double zprime = centered.z()*mDepthOnLz; + + const double scale = (mGamma * zprime + 1.) / mLx; + const double scale2 = mGamma * mDepthOnLz / mLx; + + const Vec3d tmp(scale * in.x() + scale2 * centered.x()* in.z(), + scale * in.y() + scale2 * centered.y()* in.z(), + mDepthOnLz * in.z()); + + return mSecondMap.applyJacobian(tmp); + } + + + /// @brief Return the Inverse Jacobian of the map applied to @a in + /// (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const override { + return mSecondMap.applyInverseJacobian(in); + } + /// Return the Inverse Jacobian defined at @c isloc of the map applied to @a in. + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d& isloc) const override { + + // Move the center of the x-face of the bbox + // to the origin in index space. + Vec3d centered(isloc); + centered = centered - mBBox.min(); + centered.x() -= mXo; + centered.y() -= mYo; + + // scale the z-direction on depth / K count + const double zprime = centered.z()*mDepthOnLz; + + const double scale = (mGamma * zprime + 1.) / mLx; + const double scale2 = mGamma * mDepthOnLz / mLx; + + + Vec3d out = mSecondMap.applyInverseJacobian(in); + + out.x() = (out.x() - scale2 * centered.x() * out.z() / mDepthOnLz) / scale; + out.y() = (out.y() - scale2 * centered.y() * out.z() / mDepthOnLz) / scale; + out.z() = out.z() / mDepthOnLz; + + return out; + } + + /// @brief Return the Jacobian Transpose of the map applied to vector @c in at @c indexloc. + /// @details This tranforms range-space gradients to domain-space gradients. + Vec3d applyJT(const Vec3d& in, const Vec3d& isloc) const override { + const Vec3d tmp = mSecondMap.applyJT(in); + // Move the center of the x-face of the bbox + // to the origin in index space. + Vec3d centered(isloc); + centered = centered - mBBox.min(); + centered.x() -= mXo; + centered.y() -= mYo; + + // scale the z-direction on depth / K count + const double zprime = centered.z()*mDepthOnLz; + + const double scale = (mGamma * zprime + 1.) / mLx; + const double scale2 = mGamma * mDepthOnLz / mLx; + + return Vec3d(scale * tmp.x(), + scale * tmp.y(), + scale2 * centered.x()* tmp.x() + + scale2 * centered.y()* tmp.y() + + mDepthOnLz * tmp.z()); + } + /// Return the Jacobian Transpose of the second map applied to @c in. + Vec3d applyJT(const Vec3d& in) const override { + return mSecondMap.applyJT(in); + } + + /// Return the transpose of the inverse Jacobian of the linear second map applied to @c in + Vec3d applyIJT(const Vec3d& in) const override { return mSecondMap.applyIJT(in); } + + // the Jacobian of the nonlinear part of the transform is a sparse matrix + // Jacobian^(-T) = + // + // (Lx)( 1/s 0 0 ) + // ( 0 1/s 0 ) + // ( -(x-xo)g/(sLx) -(y-yo)g/(sLx) Lz/(Depth Lx) ) + /// Return the transpose of the inverse Jacobain (at @c locW applied to @c in. + /// @c ijk is the location in the pre-image space (e.g. index space) + Vec3d applyIJT(const Vec3d& d1_is, const Vec3d& ijk) const override + { + const Vec3d loc = applyFrustumMap(ijk); + const double s = mGamma * loc.z() + 1.; + + // verify that we aren't at the singularity + if (isApproxEqual(s, 0.)) { + OPENVDB_THROW(ArithmeticError, "Tried to evaluate the frustum transform" + " at the singular focal point (e.g. camera)"); + } + + const double sinv = 1.0/s; // 1/(z*gamma + 1) + const double pt0 = mLx * sinv; // Lx / (z*gamma +1) + const double pt1 = mGamma * pt0; // gamma * Lx / ( z*gamma +1) + const double pt2 = pt1 * sinv; // gamma * Lx / ( z*gamma +1)**2 + + const Mat3d& jacinv = mSecondMap.getConstJacobianInv(); + + // compute \frac{\partial E_i}{\partial x_j} + Mat3d gradE(Mat3d::zero()); + for (int j = 0; j < 3; ++j ) { + gradE(0,j) = pt0 * jacinv(0,j) - pt2 * loc.x()*jacinv(2,j); + gradE(1,j) = pt0 * jacinv(1,j) - pt2 * loc.y()*jacinv(2,j); + gradE(2,j) = (1./mDepthOnLz) * jacinv(2,j); + } + + Vec3d result; + for (int i = 0; i < 3; ++i) { + result(i) = d1_is(0) * gradE(0,i) + d1_is(1) * gradE(1,i) + d1_is(2) * gradE(2,i); + } + + return result; + + } + + /// Return the Jacobian Curvature for the linear second map + Mat3d applyIJC(const Mat3d& in) const override { return mSecondMap.applyIJC(in); } + /// Return the Jacobian Curvature: all the second derivatives in range space + /// @param d2_is second derivative matrix computed in index space + /// @param d1_is gradient computed in index space + /// @param ijk the index space location where the result is computed + Mat3d applyIJC(const Mat3d& d2_is, const Vec3d& d1_is, const Vec3d& ijk) const override + { + const Vec3d loc = applyFrustumMap(ijk); + + const double s = mGamma * loc.z() + 1.; + + // verify that we aren't at the singularity + if (isApproxEqual(s, 0.)) { + OPENVDB_THROW(ArithmeticError, "Tried to evaluate the frustum transform" + " at the singular focal point (e.g. camera)"); + } + + // precompute + const double sinv = 1.0/s; // 1/(z*gamma + 1) + const double pt0 = mLx * sinv; // Lx / (z*gamma +1) + const double pt1 = mGamma * pt0; // gamma * Lx / ( z*gamma +1) + const double pt2 = pt1 * sinv; // gamma * Lx / ( z*gamma +1)**2 + const double pt3 = pt2 * sinv; // gamma * Lx / ( z*gamma +1)**3 + + const Mat3d& jacinv = mSecondMap.getConstJacobianInv(); + + // compute \frac{\partial^2 E_i}{\partial x_j \partial x_k} + + Mat3d matE0(Mat3d::zero()); + Mat3d matE1(Mat3d::zero()); // matE2 = 0 + for(int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + + const double pt4 = 2. * jacinv(2,j) * jacinv(2,k) * pt3; + + matE0(j,k) = -(jacinv(0,j) * jacinv(2,k) + jacinv(2,j) * jacinv(0,k)) * pt2 + + pt4 * loc.x(); + + matE1(j,k) = -(jacinv(1,j) * jacinv(2,k) + jacinv(2,j) * jacinv(1,k)) * pt2 + + pt4 * loc.y(); + } + } + + // compute \frac{\partial E_i}{\partial x_j} + Mat3d gradE(Mat3d::zero()); + for (int j = 0; j < 3; ++j ) { + gradE(0,j) = pt0 * jacinv(0,j) - pt2 * loc.x()*jacinv(2,j); + gradE(1,j) = pt0 * jacinv(1,j) - pt2 * loc.y()*jacinv(2,j); + gradE(2,j) = (1./mDepthOnLz) * jacinv(2,j); + } + + Mat3d result(Mat3d::zero()); + // compute \fac{\partial E_j}{\partial x_m} \fac{\partial E_i}{\partial x_n} + // \frac{\partial^2 input}{\partial E_i \partial E_j} + for (int m = 0; m < 3; ++m ) { + for ( int n = 0; n < 3; ++n) { + for (int i = 0; i < 3; ++i ) { + for (int j = 0; j < 3; ++j) { + result(m, n) += gradE(j, m) * gradE(i, n) * d2_is(i, j); + } + } + } + } + + for (int m = 0; m < 3; ++m ) { + for ( int n = 0; n < 3; ++n) { + result(m, n) += + matE0(m, n) * d1_is(0) + matE1(m, n) * d1_is(1);// + matE2(m, n) * d1_is(2); + } + } + + return result; + } + + /// Return the determinant of the Jacobian of linear second map + double determinant() const override {return mSecondMap.determinant();} // no implementation + + /// Return the determinate of the Jacobian evaluated at @c loc + /// @c loc is a location in the pre-image space (e.g., index space) + double determinant(const Vec3d& loc) const override + { + double s = mGamma * loc.z() + 1.0; + double frustum_determinant = s * s * mDepthOnLzLxLx; + return mSecondMap.determinant() * frustum_determinant; + } + + /// Return the size of a voxel at the center of the near plane + Vec3d voxelSize() const override + { + const Vec3d loc( 0.5*(mBBox.min().x() + mBBox.max().x()), + 0.5*(mBBox.min().y() + mBBox.max().y()), + mBBox.min().z()); + + return voxelSize(loc); + + } + + /// @brief Returns the lengths of the images of the three segments + /// from @a loc to @a loc + (1,0,0), from @a loc to @a loc + (0,1,0) + /// and from @a loc to @a loc + (0,0,1) + /// @param loc a location in the pre-image space (e.g., index space) + Vec3d voxelSize(const Vec3d& loc) const override + { + Vec3d out, pos = applyMap(loc); + out(0) = (applyMap(loc + Vec3d(1,0,0)) - pos).length(); + out(1) = (applyMap(loc + Vec3d(0,1,0)) - pos).length(); + out(2) = (applyMap(loc + Vec3d(0,0,1)) - pos).length(); + return out; + } + + AffineMap::Ptr getAffineMap() const override { return mSecondMap.getAffineMap(); } + + /// set the taper value, the ratio of nearplane width / far plane width + void setTaper(double t) { mTaper = t; init();} + /// Return the taper value. + double getTaper() const { return mTaper; } + /// set the frustum depth: distance between near and far plane = frustm depth * frustm x-width + void setDepth(double d) { mDepth = d; init();} + /// Return the unscaled frustm depth + double getDepth() const { return mDepth; } + // gamma a non-dimensional number: nearplane x-width / camera to near plane distance + double getGamma() const { return mGamma; } + + /// Return the bounding box that defines the frustum in pre-image space + const BBoxd& getBBox() const { return mBBox; } + + /// Return MapBase::Ptr& to the second map + const AffineMap& secondMap() const { return mSecondMap; } + /// Return @c true if the the bounding box in index space that defines the region that + /// is maped into the frustum is non-zero, otherwise @c false + bool isValid() const { return !mBBox.empty();} + + /// Return @c true if the second map is a uniform scale, Rotation and translation + bool hasSimpleAffine() const { return mHasSimpleAffine; } + + /// read serialization + void read(std::istream& is) override + { + // for backward compatibility with earlier version + if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_FLOAT_FRUSTUM_BBOX ) { + CoordBBox bb; + bb.read(is); + mBBox = BBoxd(bb.min().asVec3d(), bb.max().asVec3d()); + } else { + mBBox.read(is); + } + + is.read(reinterpret_cast(&mTaper), sizeof(double)); + is.read(reinterpret_cast(&mDepth), sizeof(double)); + + // Read the second maps type. + Name type = readString(is); + + // Check if the map has been registered. + if(!MapRegistry::isRegistered(type)) { + OPENVDB_THROW(KeyError, "Map " << type << " is not registered"); + } + + // Create the second map of the type and then read it in. + MapBase::Ptr proxy = math::MapRegistry::createMap(type); + proxy->read(is); + mSecondMap = *(proxy->getAffineMap()); + init(); + } + + /// write serialization + void write(std::ostream& os) const override + { + mBBox.write(os); + os.write(reinterpret_cast(&mTaper), sizeof(double)); + os.write(reinterpret_cast(&mDepth), sizeof(double)); + + writeString(os, mSecondMap.type()); + mSecondMap.write(os); + } + + /// string serialization, useful for debuging + std::string str() const override + { + std::ostringstream buffer; + buffer << " - taper: " << mTaper << std::endl; + buffer << " - depth: " << mDepth << std::endl; + buffer << " SecondMap: "<< mSecondMap.type() << std::endl; + buffer << mSecondMap.str() << std::endl; + return buffer.str(); + } + + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the given rotation to the linear part of this map + MapBase::Ptr preRotate(double radians, Axis axis = X_AXIS) const override + { + return MapBase::Ptr( + new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.preRotate(radians, axis))); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the given translation to the linear part of this map + MapBase::Ptr preTranslate(const Vec3d& t) const override + { + return MapBase::Ptr( + new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.preTranslate(t))); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the given scale to the linear part of this map + MapBase::Ptr preScale(const Vec3d& s) const override + { + return MapBase::Ptr( + new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.preScale(s))); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of prepending the given shear to the linear part of this map + MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const override + { + return MapBase::Ptr(new NonlinearFrustumMap( + mBBox, mTaper, mDepth, mSecondMap.preShear(shear, axis0, axis1))); + } + + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of appending the given rotation to the linear part of this map. + MapBase::Ptr postRotate(double radians, Axis axis = X_AXIS) const override + { + return MapBase::Ptr( + new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.postRotate(radians, axis))); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of appending the given translation to the linear part of this map. + MapBase::Ptr postTranslate(const Vec3d& t) const override + { + return MapBase::Ptr( + new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.postTranslate(t))); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of appending the given scale to the linear part of this map. + MapBase::Ptr postScale(const Vec3d& s) const override + { + return MapBase::Ptr( + new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.postScale(s))); + } + /// @brief Return a MapBase::Ptr to a new map that is the result + /// of appending the given shear to the linear part of this map. + MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const override + { + return MapBase::Ptr(new NonlinearFrustumMap( + mBBox, mTaper, mDepth, mSecondMap.postShear(shear, axis0, axis1))); + } + +private: + void init() + { + // set up as a frustum + mLx = mBBox.extents().x(); + mLy = mBBox.extents().y(); + mLz = mBBox.extents().z(); + + if (isApproxEqual(mLx,0.) || isApproxEqual(mLy,0.) || isApproxEqual(mLz,0.) ) { + OPENVDB_THROW(ArithmeticError, "The index space bounding box" + " must have at least two index points in each direction."); + } + + mXo = 0.5* mLx; + mYo = 0.5* mLy; + + // mDepth is non-dimensionalized on near + mGamma = (1./mTaper - 1) / mDepth; + + mDepthOnLz = mDepth/mLz; + mDepthOnLzLxLx = mDepthOnLz/(mLx * mLx); + + /// test for shear and non-uniform scale + mHasSimpleAffine = true; + Vec3d tmp = mSecondMap.voxelSize(); + + /// false if there is non-uniform scale + if (!isApproxEqual(tmp(0), tmp(1))) { mHasSimpleAffine = false; return; } + if (!isApproxEqual(tmp(0), tmp(2))) { mHasSimpleAffine = false; return; } + + Vec3d trans = mSecondMap.applyMap(Vec3d(0,0,0)); + /// look for shear + Vec3d tmp1 = mSecondMap.applyMap(Vec3d(1,0,0)) - trans; + Vec3d tmp2 = mSecondMap.applyMap(Vec3d(0,1,0)) - trans; + Vec3d tmp3 = mSecondMap.applyMap(Vec3d(0,0,1)) - trans; + + /// false if there is shear + if (!isApproxEqual(tmp1.dot(tmp2), 0., 1.e-7)) { mHasSimpleAffine = false; return; } + if (!isApproxEqual(tmp2.dot(tmp3), 0., 1.e-7)) { mHasSimpleAffine = false; return; } + if (!isApproxEqual(tmp3.dot(tmp1), 0., 1.e-7)) { mHasSimpleAffine = false; return; } + } + + Vec3d applyFrustumMap(const Vec3d& in) const + { + + // Move the center of the x-face of the bbox + // to the origin in index space. + Vec3d out(in); + out = out - mBBox.min(); + out.x() -= mXo; + out.y() -= mYo; + + // scale the z-direction on depth / K count + out.z() *= mDepthOnLz; + + double scale = (mGamma * out.z() + 1.)/ mLx; + + // scale the x-y on the length I count and apply tapper + out.x() *= scale ; + out.y() *= scale ; + + return out; + } + + Vec3d applyFrustumInverseMap(const Vec3d& in) const + { + // invert taper and resize: scale = 1/( (z+1)/2 (mt-1) + 1) + Vec3d out(in); + double invScale = mLx / (mGamma * out.z() + 1.); + out.x() *= invScale; + out.y() *= invScale; + + out.x() += mXo; + out.y() += mYo; + + out.z() /= mDepthOnLz; + + // move back + out = out + mBBox.min(); + return out; + } + + // bounding box in index space used in Frustum transforms. + BBoxd mBBox; + + // taper value used in constructing Frustums. + double mTaper; + double mDepth; + + // defines the second map + AffineMap mSecondMap; + + // these are derived from the above. + double mLx, mLy, mLz; + double mXo, mYo, mGamma, mDepthOnLz, mDepthOnLzLxLx; + + // true: if the mSecondMap is linear and has no shear, and has no non-uniform scale + bool mHasSimpleAffine; +}; // class NonlinearFrustumMap + + +//////////////////////////////////////// + + +/// @brief Creates the composition of two maps, each of which could be a composition. +/// In the case that each component of the composition classified as linear an +/// acceleration AffineMap is stored. +template +class CompoundMap +{ +public: + using MyType = CompoundMap; + + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + + CompoundMap() { updateAffineMatrix(); } + + CompoundMap(const FirstMapType& f, const SecondMapType& s): mFirstMap(f), mSecondMap(s) + { + updateAffineMatrix(); + } + + CompoundMap(const MyType& other): + mFirstMap(other.mFirstMap), + mSecondMap(other.mSecondMap), + mAffineMap(other.mAffineMap) + {} + + Name type() const { return mapType(); } + static Name mapType() + { + return (FirstMapType::mapType() + Name(":") + SecondMapType::mapType()); + } + + bool operator==(const MyType& other) const + { + if (mFirstMap != other.mFirstMap) return false; + if (mSecondMap != other.mSecondMap) return false; + if (mAffineMap != other.mAffineMap) return false; + return true; + } + + bool operator!=(const MyType& other) const { return !(*this == other); } + + MyType& operator=(const MyType& other) + { + mFirstMap = other.mFirstMap; + mSecondMap = other.mSecondMap; + mAffineMap = other.mAffineMap; + return *this; + } + + bool isIdentity() const + { + if (is_linear::value) { + return mAffineMap.isIdentity(); + } else { + return mFirstMap.isIdentity()&&mSecondMap.isIdentity(); + } + } + + bool isDiagonal() const { + if (is_linear::value) { + return mAffineMap.isDiagonal(); + } else { + return mFirstMap.isDiagonal()&&mSecondMap.isDiagonal(); + } + } + + AffineMap::Ptr getAffineMap() const + { + if (is_linear::value) { + AffineMap::Ptr affine(new AffineMap(mAffineMap)); + return affine; + } else { + OPENVDB_THROW(ArithmeticError, + "Constant affine matrix representation not possible for this nonlinear map"); + } + } + + // direct decompotion + const FirstMapType& firstMap() const { return mFirstMap; } + const SecondMapType& secondMap() const {return mSecondMap; } + + void setFirstMap(const FirstMapType& first) { mFirstMap = first; updateAffineMatrix(); } + void setSecondMap(const SecondMapType& second) { mSecondMap = second; updateAffineMatrix(); } + + void read(std::istream& is) + { + mAffineMap.read(is); + mFirstMap.read(is); + mSecondMap.read(is); + } + void write(std::ostream& os) const + { + mAffineMap.write(os); + mFirstMap.write(os); + mSecondMap.write(os); + } + +private: + void updateAffineMatrix() + { + if (is_linear::value) { + // both maps need to be linear, these methods are only defined for linear maps + AffineMap::Ptr first = mFirstMap.getAffineMap(); + AffineMap::Ptr second= mSecondMap.getAffineMap(); + mAffineMap = AffineMap(*first, *second); + } + } + + FirstMapType mFirstMap; + SecondMapType mSecondMap; + // used for acceleration + AffineMap mAffineMap; +}; // class CompoundMap + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_MAPS_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Mat.h b/openvdb/math/Mat.h new file mode 100644 index 00000000..25e5fa09 --- /dev/null +++ b/openvdb/math/Mat.h @@ -0,0 +1,1019 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Mat.h +/// @author Joshua Schpok + +#ifndef OPENVDB_MATH_MAT_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_MAT_HAS_BEEN_INCLUDED + +#include "Math.h" +#include +#include // for std::max() +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @class Mat "Mat.h" +/// A base class for square matrices. +template +class Mat +{ +public: + using value_type = T; + using ValueType = T; + enum SIZE_ { size = SIZE }; + + // Number of cols, rows, elements + static unsigned numRows() { return SIZE; } + static unsigned numColumns() { return SIZE; } + static unsigned numElements() { return SIZE*SIZE; } + + /// Default ctor. Does nothing. Required because declaring a copy (or + /// other) constructor means the default constructor gets left out. + Mat() { } + + /// Copy constructor. Used when the class signature matches exactly. + Mat(Mat const &src) { + for (unsigned i(0); i < numElements(); ++i) { + mm[i] = src.mm[i]; + } + } + + Mat& operator=(Mat const& src) { + if (&src != this) { + for (unsigned i = 0; i < numElements(); ++i) { + mm[i] = src.mm[i]; + } + } + return *this; + } + + /// @return string representation of matrix + /// Since output is multiline, optional indentation argument prefixes + /// each newline with that much white space. It does not indent + /// the first line, since you might be calling this inline: + /// + /// cout << "matrix: " << mat.str(7) + /// + /// matrix: [[1 2] + /// [3 4]] + std::string + str(unsigned indentation = 0) const { + + std::string ret; + std::string indent; + + // We add +1 since we're indenting one for the first '[' + indent.append(indentation+1, ' '); + + ret.append("["); + + // For each row, + for (unsigned i(0); i < SIZE; i++) { + + ret.append("["); + + // For each column + for (unsigned j(0); j < SIZE; j++) { + + // Put a comma after everything except the last + if (j) ret.append(", "); + ret.append(std::to_string(mm[(i*SIZE)+j])); + } + + ret.append("]"); + + // At the end of every row (except the last)... + if (i < SIZE - 1) { + // ...suffix the row bracket with a comma, newline, and advance indentation. + ret.append(",\n"); + ret.append(indent); + } + } + + ret.append("]"); + + return ret; + } + + /// Write a Mat to an output stream + friend std::ostream& operator<<( + std::ostream& ostr, + const Mat& m) + { + ostr << m.str(); + return ostr; + } + + void write(std::ostream& os) const { + os.write(reinterpret_cast(&mm), sizeof(T)*SIZE*SIZE); + } + + void read(std::istream& is) { + is.read(reinterpret_cast(&mm), sizeof(T)*SIZE*SIZE); + } + + /// Return the maximum of the absolute of all elements in this matrix + T absMax() const { + T x = static_cast(std::fabs(mm[0])); + for (unsigned i = 1; i < numElements(); ++i) { + x = std::max(x, static_cast(std::fabs(mm[i]))); + } + return x; + } + + /// True if a Nan is present in this matrix + bool isNan() const { + for (unsigned i = 0; i < numElements(); ++i) { + if (math::isNan(mm[i])) return true; + } + return false; + } + + /// True if an Inf is present in this matrix + bool isInfinite() const { + for (unsigned i = 0; i < numElements(); ++i) { + if (math::isInfinite(mm[i])) return true; + } + return false; + } + + /// True if no Nan or Inf values are present + bool isFinite() const { + for (unsigned i = 0; i < numElements(); ++i) { + if (!math::isFinite(mm[i])) return false; + } + return true; + } + + /// True if all elements are exactly zero + bool isZero() const { + for (unsigned i = 0; i < numElements(); ++i) { + if (!math::isZero(mm[i])) return false; + } + return true; + } + +protected: + T mm[SIZE*SIZE]; +}; + + +template class Quat; +template class Vec3; + +/// @brief Return the rotation matrix specified by the given quaternion. +/// @details The quaternion is normalized and used to construct the matrix. +/// Note that the matrix is transposed to match post-multiplication semantics. +template +MatType +rotation(const Quat &q, + typename MatType::value_type eps = static_cast(1.0e-8)) +{ + using T = typename MatType::value_type; + + T qdot(q.dot(q)); + T s(0); + + if (!isApproxEqual(qdot, T(0.0),eps)) { + s = T(2.0 / qdot); + } + + T x = s*q.x(); + T y = s*q.y(); + T z = s*q.z(); + T wx = x*q.w(); + T wy = y*q.w(); + T wz = z*q.w(); + T xx = x*q.x(); + T xy = y*q.x(); + T xz = z*q.x(); + T yy = y*q.y(); + T yz = z*q.y(); + T zz = z*q.z(); + + MatType r; + r[0][0]=T(1) - (yy+zz); r[0][1]=xy + wz; r[0][2]=xz - wy; + r[1][0]=xy - wz; r[1][1]=T(1) - (xx+zz); r[1][2]=yz + wx; + r[2][0]=xz + wy; r[2][1]=yz - wx; r[2][2]=T(1) - (xx+yy); + + if(MatType::numColumns() == 4) padMat4(r); + return r; +} + + + +/// @brief Return a matrix for rotation by @a angle radians about the given @a axis. +/// @param axis The axis (one of X, Y, Z) to rotate about. +/// @param angle The rotation angle, in radians. +template +MatType +rotation(Axis axis, typename MatType::value_type angle) +{ + using T = typename MatType::value_type; + T c = static_cast(cos(angle)); + T s = static_cast(sin(angle)); + + MatType result; + result.setIdentity(); + + switch (axis) { + case X_AXIS: + result[1][1] = c; + result[1][2] = s; + result[2][1] = -s; + result[2][2] = c; + return result; + case Y_AXIS: + result[0][0] = c; + result[0][2] = -s; + result[2][0] = s; + result[2][2] = c; + return result; + case Z_AXIS: + result[0][0] = c; + result[0][1] = s; + result[1][0] = -s; + result[1][1] = c; + return result; + default: + throw ValueError("Unrecognized rotation axis"); + } +} + + +/// @brief Return a matrix for rotation by @a angle radians about the given @a axis. +/// @note The axis must be a unit vector. +template +MatType +rotation(const Vec3 &_axis, typename MatType::value_type angle) +{ + using T = typename MatType::value_type; + T txy, txz, tyz, sx, sy, sz; + + Vec3 axis(_axis.unit()); + + // compute trig properties of angle: + T c(cos(double(angle))); + T s(sin(double(angle))); + T t(1 - c); + + MatType result; + // handle diagonal elements + result[0][0] = axis[0]*axis[0] * t + c; + result[1][1] = axis[1]*axis[1] * t + c; + result[2][2] = axis[2]*axis[2] * t + c; + + txy = axis[0]*axis[1] * t; + sz = axis[2] * s; + + txz = axis[0]*axis[2] * t; + sy = axis[1] * s; + + tyz = axis[1]*axis[2] * t; + sx = axis[0] * s; + + // right handed space + // Contribution from rotation about 'z' + result[0][1] = txy + sz; + result[1][0] = txy - sz; + // Contribution from rotation about 'y' + result[0][2] = txz - sy; + result[2][0] = txz + sy; + // Contribution from rotation about 'x' + result[1][2] = tyz + sx; + result[2][1] = tyz - sx; + + if(MatType::numColumns() == 4) padMat4(result); + return MatType(result); +} + + +/// @brief Return the Euler angles composing the given rotation matrix. +/// @details Optional axes arguments describe in what order elementary rotations +/// are applied. Note that in our convention, XYZ means Rz * Ry * Rx. +/// Because we are using rows rather than columns to represent the +/// local axes of a coordinate frame, the interpretation from a local +/// reference point of view is to first rotate about the x axis, then +/// about the newly rotated y axis, and finally by the new local z axis. +/// From a fixed reference point of view, the interpretation is to +/// rotate about the stationary world z, y, and x axes respectively. +/// +/// Irrespective of the Euler angle convention, in the case of distinct +/// axes, eulerAngles() returns the x, y, and z angles in the corresponding +/// x, y, z components of the returned Vec3. For the XZX convention, the +/// left X value is returned in Vec3.x, and the right X value in Vec3.y. +/// For the ZXZ convention the left Z value is returned in Vec3.z and +/// the right Z value in Vec3.y +/// +/// Examples of reconstructing r from its Euler angle decomposition +/// +/// v = eulerAngles(r, ZYX_ROTATION); +/// rx.setToRotation(Vec3d(1,0,0), v[0]); +/// ry.setToRotation(Vec3d(0,1,0), v[1]); +/// rz.setToRotation(Vec3d(0,0,1), v[2]); +/// r = rx * ry * rz; +/// +/// v = eulerAngles(r, ZXZ_ROTATION); +/// rz1.setToRotation(Vec3d(0,0,1), v[2]); +/// rx.setToRotation (Vec3d(1,0,0), v[0]); +/// rz2.setToRotation(Vec3d(0,0,1), v[1]); +/// r = rz2 * rx * rz1; +/// +/// v = eulerAngles(r, XZX_ROTATION); +/// rx1.setToRotation (Vec3d(1,0,0), v[0]); +/// rx2.setToRotation (Vec3d(1,0,0), v[1]); +/// rz.setToRotation (Vec3d(0,0,1), v[2]); +/// r = rx2 * rz * rx1; +/// +template +Vec3 +eulerAngles( + const MatType& mat, + RotationOrder rotationOrder, + typename MatType::value_type eps = static_cast(1.0e-8)) +{ + using ValueType = typename MatType::value_type; + using V = Vec3; + ValueType phi, theta, psi; + + switch(rotationOrder) + { + case XYZ_ROTATION: + if (isApproxEqual(mat[2][0], ValueType(1.0), eps)) { + theta = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[1][2], mat[1][1])); + psi = phi; + } else if (isApproxEqual(mat[2][0], ValueType(-1.0), eps)) { + theta = ValueType(-M_PI_2); + phi = ValueType(0.5 * atan2(mat[1][2], mat[1][1])); + psi = -phi; + } else { + psi = ValueType(atan2(-mat[1][0],mat[0][0])); + phi = ValueType(atan2(-mat[2][1],mat[2][2])); + theta = ValueType(atan2(mat[2][0], + sqrt( mat[2][1]*mat[2][1] + + mat[2][2]*mat[2][2]))); + } + return V(phi, theta, psi); + case ZXY_ROTATION: + if (isApproxEqual(mat[1][2], ValueType(1.0), eps)) { + theta = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[0][1], mat[0][0])); + psi = phi; + } else if (isApproxEqual(mat[1][2], ValueType(-1.0), eps)) { + theta = ValueType(-M_PI/2); + phi = ValueType(0.5 * atan2(mat[0][1],mat[2][1])); + psi = -phi; + } else { + psi = ValueType(atan2(-mat[0][2], mat[2][2])); + phi = ValueType(atan2(-mat[1][0], mat[1][1])); + theta = ValueType(atan2(mat[1][2], + sqrt(mat[0][2] * mat[0][2] + + mat[2][2] * mat[2][2]))); + } + return V(theta, psi, phi); + + case YZX_ROTATION: + if (isApproxEqual(mat[0][1], ValueType(1.0), eps)) { + theta = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[2][0], mat[2][2])); + psi = phi; + } else if (isApproxEqual(mat[0][1], ValueType(-1.0), eps)) { + theta = ValueType(-M_PI/2); + phi = ValueType(0.5 * atan2(mat[2][0], mat[1][0])); + psi = -phi; + } else { + psi = ValueType(atan2(-mat[2][1], mat[1][1])); + phi = ValueType(atan2(-mat[0][2], mat[0][0])); + theta = ValueType(atan2(mat[0][1], + sqrt(mat[0][0] * mat[0][0] + + mat[0][2] * mat[0][2]))); + } + return V(psi, phi, theta); + + case XZX_ROTATION: + + if (isApproxEqual(mat[0][0], ValueType(1.0), eps)) { + theta = ValueType(0.0); + phi = ValueType(0.5 * atan2(mat[1][2], mat[1][1])); + psi = phi; + } else if (isApproxEqual(mat[0][0], ValueType(-1.0), eps)) { + theta = ValueType(M_PI); + psi = ValueType(0.5 * atan2(mat[2][1], -mat[1][1])); + phi = - psi; + } else { + psi = ValueType(atan2(mat[2][0], -mat[1][0])); + phi = ValueType(atan2(mat[0][2], mat[0][1])); + theta = ValueType(atan2(sqrt(mat[0][1] * mat[0][1] + + mat[0][2] * mat[0][2]), + mat[0][0])); + } + return V(phi, psi, theta); + + case ZXZ_ROTATION: + + if (isApproxEqual(mat[2][2], ValueType(1.0), eps)) { + theta = ValueType(0.0); + phi = ValueType(0.5 * atan2(mat[0][1], mat[0][0])); + psi = phi; + } else if (isApproxEqual(mat[2][2], ValueType(-1.0), eps)) { + theta = ValueType(M_PI); + phi = ValueType(0.5 * atan2(mat[0][1], mat[0][0])); + psi = -phi; + } else { + psi = ValueType(atan2(mat[0][2], mat[1][2])); + phi = ValueType(atan2(mat[2][0], -mat[2][1])); + theta = ValueType(atan2(sqrt(mat[0][2] * mat[0][2] + + mat[1][2] * mat[1][2]), + mat[2][2])); + } + return V(theta, psi, phi); + + case YXZ_ROTATION: + + if (isApproxEqual(mat[2][1], ValueType(1.0), eps)) { + theta = ValueType(-M_PI_2); + phi = ValueType(0.5 * atan2(-mat[1][0], mat[0][0])); + psi = phi; + } else if (isApproxEqual(mat[2][1], ValueType(-1.0), eps)) { + theta = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[1][0], mat[0][0])); + psi = -phi; + } else { + psi = ValueType(atan2(mat[0][1], mat[1][1])); + phi = ValueType(atan2(mat[2][0], mat[2][2])); + theta = ValueType(atan2(-mat[2][1], + sqrt(mat[0][1] * mat[0][1] + + mat[1][1] * mat[1][1]))); + } + return V(theta, phi, psi); + + case ZYX_ROTATION: + + if (isApproxEqual(mat[0][2], ValueType(1.0), eps)) { + theta = ValueType(-M_PI_2); + phi = ValueType(0.5 * atan2(-mat[1][0], mat[1][1])); + psi = phi; + } else if (isApproxEqual(mat[0][2], ValueType(-1.0), eps)) { + theta = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[2][1], mat[2][0])); + psi = -phi; + } else { + psi = ValueType(atan2(mat[1][2], mat[2][2])); + phi = ValueType(atan2(mat[0][1], mat[0][0])); + theta = ValueType(atan2(-mat[0][2], + sqrt(mat[0][1] * mat[0][1] + + mat[0][0] * mat[0][0]))); + } + return V(psi, theta, phi); + + case XZY_ROTATION: + + if (isApproxEqual(mat[1][0], ValueType(-1.0), eps)) { + theta = ValueType(M_PI_2); + psi = ValueType(0.5 * atan2(mat[2][1], mat[2][2])); + phi = -psi; + } else if (isApproxEqual(mat[1][0], ValueType(1.0), eps)) { + theta = ValueType(-M_PI_2); + psi = ValueType(0.5 * atan2(- mat[2][1], mat[2][2])); + phi = psi; + } else { + psi = ValueType(atan2(mat[2][0], mat[0][0])); + phi = ValueType(atan2(mat[1][2], mat[1][1])); + theta = ValueType(atan2(- mat[1][0], + sqrt(mat[1][1] * mat[1][1] + + mat[1][2] * mat[1][2]))); + } + return V(phi, psi, theta); + } + + OPENVDB_THROW(NotImplementedError, "Euler extraction sequence not implemented"); +} + + +/// @brief Return a rotation matrix that maps @a v1 onto @a v2 +/// about the cross product of @a v1 and @a v2. +/// +template +inline MatType +rotation( + const Vec3& _v1, + const Vec3& _v2, + typename MatType::value_type eps = static_cast(1.0e-8)) +{ + using T = typename MatType::value_type; + + Vec3 v1(_v1); + Vec3 v2(_v2); + + // Check if v1 and v2 are unit length + if (!isApproxEqual(T(1), v1.dot(v1), eps)) { + v1.normalize(); + } + if (!isApproxEqual(T(1), v2.dot(v2), eps)) { + v2.normalize(); + } + + Vec3 cross; + cross.cross(v1, v2); + + if (isApproxEqual(cross[0], zeroVal(), eps) && + isApproxEqual(cross[1], zeroVal(), eps) && + isApproxEqual(cross[2], zeroVal(), eps)) { + + + // Given two unit vectors v1 and v2 that are nearly parallel, build a + // rotation matrix that maps v1 onto v2. First find which principal axis + // p is closest to perpendicular to v1. Find a reflection that exchanges + // v1 and p, and find a reflection that exchanges p2 and v2. The desired + // rotation matrix is the composition of these two reflections. See the + // paper "Efficiently Building a Matrix to Rotate One Vector to + // Another" by Tomas Moller and John Hughes in Journal of Graphics + // Tools Vol 4, No 4 for details. + + Vec3 u, v, p(0.0, 0.0, 0.0); + + double x = Abs(v1[0]); + double y = Abs(v1[1]); + double z = Abs(v1[2]); + + if (x < y) { + if (z < x) { + p[2] = 1; + } else { + p[0] = 1; + } + } else { + if (z < y) { + p[2] = 1; + } else { + p[1] = 1; + } + } + u = p - v1; + v = p - v2; + + double udot = u.dot(u); + double vdot = v.dot(v); + + double a = -2 / udot; + double b = -2 / vdot; + double c = 4 * u.dot(v) / (udot * vdot); + + MatType result; + result.setIdentity(); + + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 3; i++) + result[i][j] = static_cast( + a * u[i] * u[j] + b * v[i] * v[j] + c * v[j] * u[i]); + } + result[0][0] += 1.0; + result[1][1] += 1.0; + result[2][2] += 1.0; + + if(MatType::numColumns() == 4) padMat4(result); + return result; + + } else { + double c = v1.dot(v2); + double a = (1.0 - c) / cross.dot(cross); + + double a0 = a * cross[0]; + double a1 = a * cross[1]; + double a2 = a * cross[2]; + + double a01 = a0 * cross[1]; + double a02 = a0 * cross[2]; + double a12 = a1 * cross[2]; + + MatType r; + + r[0][0] = static_cast(c + a0 * cross[0]); + r[0][1] = static_cast(a01 + cross[2]); + r[0][2] = static_cast(a02 - cross[1]); + r[1][0] = static_cast(a01 - cross[2]); + r[1][1] = static_cast(c + a1 * cross[1]); + r[1][2] = static_cast(a12 + cross[0]); + r[2][0] = static_cast(a02 + cross[1]); + r[2][1] = static_cast(a12 - cross[0]); + r[2][2] = static_cast(c + a2 * cross[2]); + + if(MatType::numColumns() == 4) padMat4(r); + return r; + + } +} + + +/// Return a matrix that scales by @a s. +template +MatType +scale(const Vec3& s) +{ + // Gets identity, then sets top 3 diagonal + // Inefficient by 3 sets. + + MatType result; + result.setIdentity(); + result[0][0] = s[0]; + result[1][1] = s[1]; + result[2][2] = s[2]; + + return result; +} + + +/// Return a Vec3 representing the lengths of the passed matrix's upper 3×3's rows. +template +Vec3 +getScale(const MatType &mat) +{ + using V = Vec3; + return V( + V(mat[0][0], mat[0][1], mat[0][2]).length(), + V(mat[1][0], mat[1][1], mat[1][2]).length(), + V(mat[2][0], mat[2][1], mat[2][2]).length()); +} + + +/// @brief Return a copy of the given matrix with its upper 3×3 rows normalized. +/// @details This can be geometrically interpreted as a matrix with no scaling +/// along its major axes. +template +MatType +unit(const MatType &mat, typename MatType::value_type eps = 1.0e-8) +{ + Vec3 dud; + return unit(mat, eps, dud); +} + + +/// @brief Return a copy of the given matrix with its upper 3×3 rows normalized, +/// and return the length of each of these rows in @a scaling. +/// @details This can be geometrically interpretted as a matrix with no scaling +/// along its major axes, and the scaling in the input vector +template +MatType +unit( + const MatType &in, + typename MatType::value_type eps, + Vec3& scaling) +{ + using T = typename MatType::value_type; + MatType result(in); + + for (int i(0); i < 3; i++) { + try { + const Vec3 u( + Vec3(in[i][0], in[i][1], in[i][2]).unit(eps, scaling[i])); + for (int j=0; j<3; j++) result[i][j] = u[j]; + } catch (ArithmeticError&) { + for (int j=0; j<3; j++) result[i][j] = 0; + } + } + return result; +} + + +/// @brief Set the matrix to a shear along @a axis0 by a fraction of @a axis1. +/// @param axis0 The fixed axis of the shear. +/// @param axis1 The shear axis. +/// @param shear The shear factor. +template +MatType +shear(Axis axis0, Axis axis1, typename MatType::value_type shear) +{ + int index0 = static_cast(axis0); + int index1 = static_cast(axis1); + + MatType result; + result.setIdentity(); + if (axis0 == axis1) { + result[index1][index0] = shear + 1; + } else { + result[index1][index0] = shear; + } + + return result; +} + + +/// Return a matrix as the cross product of the given vector. +template +MatType +skew(const Vec3 &skew) +{ + using T = typename MatType::value_type; + + MatType r; + r[0][0] = T(0); r[0][1] = skew.z(); r[0][2] = -skew.y(); + r[1][0] = -skew.z(); r[1][1] = T(0); r[2][1] = skew.x(); + r[2][0] = skew.y(); r[2][1] = -skew.x(); r[2][2] = T(0); + + if(MatType::numColumns() == 4) padMat4(r); + return r; +} + + +/// @brief Return an orientation matrix such that z points along @a direction, +/// and y is along the @a direction / @a vertical plane. +template +MatType +aim(const Vec3& direction, + const Vec3& vertical) +{ + using T = typename MatType::value_type; + Vec3 forward(direction.unit()); + Vec3 horizontal(vertical.unit().cross(forward).unit()); + Vec3 up(forward.cross(horizontal).unit()); + + MatType r; + + r[0][0]=horizontal.x(); r[0][1]=horizontal.y(); r[0][2]=horizontal.z(); + r[1][0]=up.x(); r[1][1]=up.y(); r[1][2]=up.z(); + r[2][0]=forward.x(); r[2][1]=forward.y(); r[2][2]=forward.z(); + + if(MatType::numColumns() == 4) padMat4(r); + return r; +} + +/// @brief This function snaps a specific axis to a specific direction, +/// preserving scaling. +/// @details It does this using minimum energy, thus posing a unique solution if +/// basis & direction aren't parallel. +/// @note @a direction need not be unit. +template +inline MatType +snapMatBasis(const MatType& source, Axis axis, const Vec3& direction) +{ + using T = typename MatType::value_type; + + Vec3 unitDir(direction.unit()); + Vec3 ourUnitAxis(source.row(axis).unit()); + + // Are the two parallel? + T parallel = unitDir.dot(ourUnitAxis); + + // Already snapped! + if (isApproxEqual(parallel, T(1.0))) return source; + + if (isApproxEqual(parallel, T(-1.0))) { + OPENVDB_THROW(ValueError, "Cannot snap to inverse axis"); + } + + // Find angle between our basis and the one specified + T angleBetween(angle(unitDir, ourUnitAxis)); + // Caclulate axis to rotate along + Vec3 rotationAxis = unitDir.cross(ourUnitAxis); + + MatType rotation; + rotation.setToRotation(rotationAxis, angleBetween); + + return source * rotation; +} + +/// @brief Write 0s along Mat4's last row and column, and a 1 on its diagonal. +/// @details Useful initialization when we're initializing just the 3×3 block. +template +inline MatType& +padMat4(MatType& dest) +{ + dest[0][3] = dest[1][3] = dest[2][3] = 0; + dest[3][2] = dest[3][1] = dest[3][0] = 0; + dest[3][3] = 1; + + return dest; +} + + +/// @brief Solve for A=B*B, given A. +/// @details Denman-Beavers square root iteration +template +inline void +sqrtSolve(const MatType& aA, MatType& aB, double aTol=0.01) +{ + unsigned int iterations = static_cast(log(aTol)/log(0.5)); + + MatType Y[2], Z[2]; + Y[0] = aA; + Z[0] = MatType::identity(); + + unsigned int current = 0; + for (unsigned int iteration=0; iteration < iterations; iteration++) { + unsigned int last = current; + current = !current; + + MatType invY = Y[last].inverse(); + MatType invZ = Z[last].inverse(); + + Y[current] = 0.5 * (Y[last] + invZ); + Z[current] = 0.5 * (Z[last] + invY); + } + aB = Y[current]; +} + + +template +inline void +powSolve(const MatType& aA, MatType& aB, double aPower, double aTol=0.01) +{ + unsigned int iterations = static_cast(log(aTol)/log(0.5)); + + const bool inverted = (aPower < 0.0); + if (inverted) { aPower = -aPower; } + + unsigned int whole = static_cast(aPower); + double fraction = aPower - whole; + + MatType R = MatType::identity(); + MatType partial = aA; + + double contribution = 1.0; + for (unsigned int iteration = 0; iteration < iterations; iteration++) { + sqrtSolve(partial, partial, aTol); + contribution *= 0.5; + if (fraction >= contribution) { + R *= partial; + fraction -= contribution; + } + } + + partial = aA; + while (whole) { + if (whole & 1) { R *= partial; } + whole >>= 1; + if (whole) { partial *= partial; } + } + + if (inverted) { aB = R.inverse(); } + else { aB = R; } +} + + +/// @brief Determine if a matrix is an identity matrix. +template +inline bool +isIdentity(const MatType& m) +{ + return m.eq(MatType::identity()); +} + + +/// @brief Determine if a matrix is invertible. +template +inline bool +isInvertible(const MatType& m) +{ + using ValueType = typename MatType::ValueType; + return !isApproxEqual(m.det(), ValueType(0)); +} + + +/// @brief Determine if a matrix is symmetric. +/// @details This implicitly uses math::isApproxEqual() to determine equality. +template +inline bool +isSymmetric(const MatType& m) +{ + return m.eq(m.transpose()); +} + + +/// Determine if a matrix is unitary (i.e., rotation or reflection). +template +inline bool +isUnitary(const MatType& m) +{ + using ValueType = typename MatType::ValueType; + if (!isApproxEqual(std::abs(m.det()), ValueType(1.0))) return false; + // check that the matrix transpose is the inverse + MatType temp = m * m.transpose(); + return temp.eq(MatType::identity()); +} + + +/// Determine if a matrix is diagonal. +template +inline bool +isDiagonal(const MatType& mat) +{ + int n = MatType::size; + typename MatType::ValueType temp(0); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + if (i != j) { + temp += std::abs(mat(i,j)); + } + } + } + return isApproxEqual(temp, typename MatType::ValueType(0.0)); +} + + +/// Return the L norm of an N×N matrix. +template +typename MatType::ValueType +lInfinityNorm(const MatType& matrix) +{ + int n = MatType::size; + typename MatType::ValueType norm = 0; + + for( int j = 0; jL1 norm of an N×N matrix. +template +typename MatType::ValueType +lOneNorm(const MatType& matrix) +{ + int n = MatType::size; + typename MatType::ValueType norm = 0; + + for( int i = 0; i +bool +polarDecomposition(const MatType& input, MatType& unitary, + MatType& positive_hermitian, unsigned int MAX_ITERATIONS=100) +{ + unitary = input; + MatType new_unitary(input); + MatType unitary_inv; + + if (fabs(unitary.det()) < math::Tolerance::value()) return false; + + unsigned int iteration(0); + + typename MatType::ValueType linf_of_u; + typename MatType::ValueType l1nm_of_u; + typename MatType::ValueType linf_of_u_inv; + typename MatType::ValueType l1nm_of_u_inv; + typename MatType::ValueType l1_error = 100; + double gamma; + + do { + unitary_inv = unitary.inverse(); + linf_of_u = lInfinityNorm(unitary); + l1nm_of_u = lOneNorm(unitary); + + linf_of_u_inv = lInfinityNorm(unitary_inv); + l1nm_of_u_inv = lOneNorm(unitary_inv); + + gamma = sqrt( sqrt( (l1nm_of_u_inv * linf_of_u_inv ) / (l1nm_of_u * linf_of_u) )); + + new_unitary = 0.5*(gamma * unitary + (1./gamma) * unitary_inv.transpose() ); + + l1_error = lInfinityNorm(unitary - new_unitary); + unitary = new_unitary; + + /// this generally converges in less than ten iterations + if (iteration > MAX_ITERATIONS) return false; + iteration++; + } while (l1_error > math::Tolerance::value()); + + positive_hermitian = unitary.transpose() * input; + return true; +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_MAT_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Mat3.h b/openvdb/math/Mat3.h new file mode 100644 index 00000000..fb87f0fa --- /dev/null +++ b/openvdb/math/Mat3.h @@ -0,0 +1,828 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_MAT3_H_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_MAT3_H_HAS_BEEN_INCLUDED + +#include +#include "Vec3.h" +#include "Mat.h" +#include // for std::copy() +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Vec3; +template class Mat4; +template class Quat; + +/// @class Mat3 Mat3.h +/// @brief 3x3 matrix class. +template +class Mat3: public Mat<3, T> +{ +public: + /// Data type held by the matrix. + using value_type = T; + using ValueType = T; + using MyBase = Mat<3, T>; + /// Trivial constructor, the matrix is NOT initialized + Mat3() {} + + /// Constructor given the quaternion rotation, e.g. Mat3f m(q); + /// The quaternion is normalized and used to construct the matrix + Mat3(const Quat &q) + { setToRotation(q); } + + + /// Constructor given array of elements, the ordering is in row major form: + /** @verbatim + a b c + d e f + g h i + @endverbatim */ + template + Mat3(Source a, Source b, Source c, + Source d, Source e, Source f, + Source g, Source h, Source i) + { + MyBase::mm[0] = static_cast(a); + MyBase::mm[1] = static_cast(b); + MyBase::mm[2] = static_cast(c); + MyBase::mm[3] = static_cast(d); + MyBase::mm[4] = static_cast(e); + MyBase::mm[5] = static_cast(f); + MyBase::mm[6] = static_cast(g); + MyBase::mm[7] = static_cast(h); + MyBase::mm[8] = static_cast(i); + } // constructor1Test + + /// Construct matrix from rows or columns vectors (defaults to rows + /// for historical reasons) + template + Mat3(const Vec3 &v1, const Vec3 &v2, const Vec3 &v3, bool rows = true) + { + if (rows) { + this->setRows(v1, v2, v3); + } else { + this->setColumns(v1, v2, v3); + } + } + + /// Constructor given array of elements, the ordering is in row major form:\n + /// a[0] a[1] a[2]\n + /// a[3] a[4] a[5]\n + /// a[6] a[7] a[8]\n + template + Mat3(Source *a) + { + MyBase::mm[0] = static_cast(a[0]); + MyBase::mm[1] = static_cast(a[1]); + MyBase::mm[2] = static_cast(a[2]); + MyBase::mm[3] = static_cast(a[3]); + MyBase::mm[4] = static_cast(a[4]); + MyBase::mm[5] = static_cast(a[5]); + MyBase::mm[6] = static_cast(a[6]); + MyBase::mm[7] = static_cast(a[7]); + MyBase::mm[8] = static_cast(a[8]); + } // constructor1Test + + /// Copy constructor + Mat3(const Mat<3, T> &m) + { + for (int i=0; i<3; ++i) { + for (int j=0; j<3; ++j) { + MyBase::mm[i*3 + j] = m[i][j]; + } + } + } + + /// Conversion constructor + template + explicit Mat3(const Mat3 &m) + { + for (int i=0; i<3; ++i) { + for (int j=0; j<3; ++j) { + MyBase::mm[i*3 + j] = static_cast(m[i][j]); + } + } + } + + /// Conversion from Mat4 (copies top left) + explicit Mat3(const Mat4 &m) + { + for (int i=0; i<3; ++i) { + for (int j=0; j<3; ++j) { + MyBase::mm[i*3 + j] = m[i][j]; + } + } + } + + /// Predefined constant for identity matrix + static const Mat3& identity() { + static const Mat3 sIdentity = Mat3( + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + ); + return sIdentity; + } + + /// Predefined constant for zero matrix + static const Mat3& zero() { + static const Mat3 sZero = Mat3( + 0, 0, 0, + 0, 0, 0, + 0, 0, 0 + ); + return sZero; + } + + /// Set ith row to vector v + void setRow(int i, const Vec3 &v) + { + // assert(i>=0 && i<3); + int i3 = i * 3; + + MyBase::mm[i3+0] = v[0]; + MyBase::mm[i3+1] = v[1]; + MyBase::mm[i3+2] = v[2]; + } // rowColumnTest + + /// Get ith row, e.g. Vec3d v = m.row(1); + Vec3 row(int i) const + { + // assert(i>=0 && i<3); + return Vec3((*this)(i,0), (*this)(i,1), (*this)(i,2)); + } // rowColumnTest + + /// Set jth column to vector v + void setCol(int j, const Vec3& v) + { + // assert(j>=0 && j<3); + MyBase::mm[0+j] = v[0]; + MyBase::mm[3+j] = v[1]; + MyBase::mm[6+j] = v[2]; + } // rowColumnTest + + /// Get jth column, e.g. Vec3d v = m.col(0); + Vec3 col(int j) const + { + // assert(j>=0 && j<3); + return Vec3((*this)(0,j), (*this)(1,j), (*this)(2,j)); + } // rowColumnTest + + // NB: The following two methods were changed to + // work around a gccWS5 compiler issue related to strict + // aliasing (see FX-475). + + //@{ + /// Array style reference to ith row + /// e.g. m[1][2] = 4; + T* operator[](int i) { return &(MyBase::mm[i*3]); } + const T* operator[](int i) const { return &(MyBase::mm[i*3]); } + //@} + + T* asPointer() {return MyBase::mm;} + const T* asPointer() const {return MyBase::mm;} + + /// Alternative indexed reference to the elements + /// Note that the indices are row first and column second. + /// e.g. m(0,0) = 1; + T& operator()(int i, int j) + { + // assert(i>=0 && i<3); + // assert(j>=0 && j<3); + return MyBase::mm[3*i+j]; + } // trivial + + /// Alternative indexed constant reference to the elements, + /// Note that the indices are row first and column second. + /// e.g. float f = m(1,0); + T operator()(int i, int j) const + { + // assert(i>=0 && i<3); + // assert(j>=0 && j<3); + return MyBase::mm[3*i+j]; + } // trivial + + /// Set the rows of this matrix to the vectors v1, v2, v3 + void setRows(const Vec3 &v1, const Vec3 &v2, const Vec3 &v3) + { + MyBase::mm[0] = v1[0]; + MyBase::mm[1] = v1[1]; + MyBase::mm[2] = v1[2]; + MyBase::mm[3] = v2[0]; + MyBase::mm[4] = v2[1]; + MyBase::mm[5] = v2[2]; + MyBase::mm[6] = v3[0]; + MyBase::mm[7] = v3[1]; + MyBase::mm[8] = v3[2]; + } // setRows + + /// Set the columns of this matrix to the vectors v1, v2, v3 + void setColumns(const Vec3 &v1, const Vec3 &v2, const Vec3 &v3) + { + MyBase::mm[0] = v1[0]; + MyBase::mm[1] = v2[0]; + MyBase::mm[2] = v3[0]; + MyBase::mm[3] = v1[1]; + MyBase::mm[4] = v2[1]; + MyBase::mm[5] = v3[1]; + MyBase::mm[6] = v1[2]; + MyBase::mm[7] = v2[2]; + MyBase::mm[8] = v3[2]; + } // setColumns + + /// Set diagonal and symmetric triangular components + void setSymmetric(const Vec3 &vdiag, const Vec3 &vtri) + { + MyBase::mm[0] = vdiag[0]; + MyBase::mm[1] = vtri[0]; + MyBase::mm[2] = vtri[1]; + MyBase::mm[3] = vtri[0]; + MyBase::mm[4] = vdiag[1]; + MyBase::mm[5] = vtri[2]; + MyBase::mm[6] = vtri[1]; + MyBase::mm[7] = vtri[2]; + MyBase::mm[8] = vdiag[2]; + } // setSymmetricTest + + /// Return a matrix with the prescribed diagonal and symmetric triangular components. + static Mat3 symmetric(const Vec3 &vdiag, const Vec3 &vtri) + { + return Mat3( + vdiag[0], vtri[0], vtri[1], + vtri[0], vdiag[1], vtri[2], + vtri[1], vtri[2], vdiag[2] + ); + } + + /// Set the matrix as cross product of the given vector + void setSkew(const Vec3 &v) + {*this = skew(v);} + + /// @brief Set this matrix to the rotation matrix specified by the quaternion + /// @details The quaternion is normalized and used to construct the matrix. + /// Note that the matrix is transposed to match post-multiplication semantics. + void setToRotation(const Quat &q) + {*this = rotation >(q);} + + /// @brief Set this matrix to the rotation specified by @a axis and @a angle + /// @details The axis must be unit vector + void setToRotation(const Vec3 &axis, T angle) + {*this = rotation >(axis, angle);} + + /// Set this matrix to zero + void setZero() + { + MyBase::mm[0] = 0; + MyBase::mm[1] = 0; + MyBase::mm[2] = 0; + MyBase::mm[3] = 0; + MyBase::mm[4] = 0; + MyBase::mm[5] = 0; + MyBase::mm[6] = 0; + MyBase::mm[7] = 0; + MyBase::mm[8] = 0; + } // trivial + + /// Set this matrix to identity + void setIdentity() + { + MyBase::mm[0] = 1; + MyBase::mm[1] = 0; + MyBase::mm[2] = 0; + MyBase::mm[3] = 0; + MyBase::mm[4] = 1; + MyBase::mm[5] = 0; + MyBase::mm[6] = 0; + MyBase::mm[7] = 0; + MyBase::mm[8] = 1; + } // trivial + + /// Assignment operator + template + const Mat3& operator=(const Mat3 &m) + { + const Source *src = m.asPointer(); + + // don't suppress type conversion warnings + std::copy(src, (src + this->numElements()), MyBase::mm); + return *this; + } // opEqualToTest + + /// Return @c true if this matrix is equivalent to @a m within a tolerance of @a eps. + bool eq(const Mat3 &m, T eps=1.0e-8) const + { + return (isApproxEqual(MyBase::mm[0],m.mm[0],eps) && + isApproxEqual(MyBase::mm[1],m.mm[1],eps) && + isApproxEqual(MyBase::mm[2],m.mm[2],eps) && + isApproxEqual(MyBase::mm[3],m.mm[3],eps) && + isApproxEqual(MyBase::mm[4],m.mm[4],eps) && + isApproxEqual(MyBase::mm[5],m.mm[5],eps) && + isApproxEqual(MyBase::mm[6],m.mm[6],eps) && + isApproxEqual(MyBase::mm[7],m.mm[7],eps) && + isApproxEqual(MyBase::mm[8],m.mm[8],eps)); + } // trivial + + /// Negation operator, for e.g. m1 = -m2; + Mat3 operator-() const + { + return Mat3( + -MyBase::mm[0], -MyBase::mm[1], -MyBase::mm[2], + -MyBase::mm[3], -MyBase::mm[4], -MyBase::mm[5], + -MyBase::mm[6], -MyBase::mm[7], -MyBase::mm[8] + ); + } // trivial + + /// Multiplication operator, e.g. M = scalar * M; + // friend Mat3 operator*(T scalar, const Mat3& m) { + // return m*scalar; + // } + + /// Multiply each element of this matrix by @a scalar. + template + const Mat3& operator*=(S scalar) + { + MyBase::mm[0] *= scalar; + MyBase::mm[1] *= scalar; + MyBase::mm[2] *= scalar; + MyBase::mm[3] *= scalar; + MyBase::mm[4] *= scalar; + MyBase::mm[5] *= scalar; + MyBase::mm[6] *= scalar; + MyBase::mm[7] *= scalar; + MyBase::mm[8] *= scalar; + return *this; + } + + /// Add each element of the given matrix to the corresponding element of this matrix. + template + const Mat3 &operator+=(const Mat3 &m1) + { + const S *s = m1.asPointer(); + + MyBase::mm[0] += s[0]; + MyBase::mm[1] += s[1]; + MyBase::mm[2] += s[2]; + MyBase::mm[3] += s[3]; + MyBase::mm[4] += s[4]; + MyBase::mm[5] += s[5]; + MyBase::mm[6] += s[6]; + MyBase::mm[7] += s[7]; + MyBase::mm[8] += s[8]; + return *this; + } + + /// Subtract each element of the given matrix from the corresponding element of this matrix. + template + const Mat3 &operator-=(const Mat3 &m1) + { + const S *s = m1.asPointer(); + + MyBase::mm[0] -= s[0]; + MyBase::mm[1] -= s[1]; + MyBase::mm[2] -= s[2]; + MyBase::mm[3] -= s[3]; + MyBase::mm[4] -= s[4]; + MyBase::mm[5] -= s[5]; + MyBase::mm[6] -= s[6]; + MyBase::mm[7] -= s[7]; + MyBase::mm[8] -= s[8]; + return *this; + } + + /// Multiply this matrix by the given matrix. + template + const Mat3 &operator*=(const Mat3 &m1) + { + Mat3 m0(*this); + const T* s0 = m0.asPointer(); + const S* s1 = m1.asPointer(); + + MyBase::mm[0] = static_cast(s0[0] * s1[0] + + s0[1] * s1[3] + + s0[2] * s1[6]); + MyBase::mm[1] = static_cast(s0[0] * s1[1] + + s0[1] * s1[4] + + s0[2] * s1[7]); + MyBase::mm[2] = static_cast(s0[0] * s1[2] + + s0[1] * s1[5] + + s0[2] * s1[8]); + + MyBase::mm[3] = static_cast(s0[3] * s1[0] + + s0[4] * s1[3] + + s0[5] * s1[6]); + MyBase::mm[4] = static_cast(s0[3] * s1[1] + + s0[4] * s1[4] + + s0[5] * s1[7]); + MyBase::mm[5] = static_cast(s0[3] * s1[2] + + s0[4] * s1[5] + + s0[5] * s1[8]); + + MyBase::mm[6] = static_cast(s0[6] * s1[0] + + s0[7] * s1[3] + + s0[8] * s1[6]); + MyBase::mm[7] = static_cast(s0[6] * s1[1] + + s0[7] * s1[4] + + s0[8] * s1[7]); + MyBase::mm[8] = static_cast(s0[6] * s1[2] + + s0[7] * s1[5] + + s0[8] * s1[8]); + + return *this; + } + + /// @brief Return the cofactor matrix of this matrix. + Mat3 cofactor() const + { + return Mat3( + MyBase::mm[4] * MyBase::mm[8] - MyBase::mm[5] * MyBase::mm[7], + MyBase::mm[5] * MyBase::mm[6] - MyBase::mm[3] * MyBase::mm[8], + MyBase::mm[3] * MyBase::mm[7] - MyBase::mm[4] * MyBase::mm[6], + MyBase::mm[2] * MyBase::mm[7] - MyBase::mm[1] * MyBase::mm[8], + MyBase::mm[0] * MyBase::mm[8] - MyBase::mm[2] * MyBase::mm[6], + MyBase::mm[1] * MyBase::mm[6] - MyBase::mm[0] * MyBase::mm[7], + MyBase::mm[1] * MyBase::mm[5] - MyBase::mm[2] * MyBase::mm[4], + MyBase::mm[2] * MyBase::mm[3] - MyBase::mm[0] * MyBase::mm[5], + MyBase::mm[0] * MyBase::mm[4] - MyBase::mm[1] * MyBase::mm[3]); + } + + /// Return the adjoint of this matrix, i.e., the transpose of its cofactor. + Mat3 adjoint() const + { + return Mat3( + MyBase::mm[4] * MyBase::mm[8] - MyBase::mm[5] * MyBase::mm[7], + MyBase::mm[2] * MyBase::mm[7] - MyBase::mm[1] * MyBase::mm[8], + MyBase::mm[1] * MyBase::mm[5] - MyBase::mm[2] * MyBase::mm[4], + MyBase::mm[5] * MyBase::mm[6] - MyBase::mm[3] * MyBase::mm[8], + MyBase::mm[0] * MyBase::mm[8] - MyBase::mm[2] * MyBase::mm[6], + MyBase::mm[2] * MyBase::mm[3] - MyBase::mm[0] * MyBase::mm[5], + MyBase::mm[3] * MyBase::mm[7] - MyBase::mm[4] * MyBase::mm[6], + MyBase::mm[1] * MyBase::mm[6] - MyBase::mm[0] * MyBase::mm[7], + MyBase::mm[0] * MyBase::mm[4] - MyBase::mm[1] * MyBase::mm[3]); + + } // adjointTest + + /// returns transpose of this + Mat3 transpose() const + { + return Mat3( + MyBase::mm[0], MyBase::mm[3], MyBase::mm[6], + MyBase::mm[1], MyBase::mm[4], MyBase::mm[7], + MyBase::mm[2], MyBase::mm[5], MyBase::mm[8]); + + } // transposeTest + + /// returns inverse of this + /// @throws ArithmeticError if singular + Mat3 inverse(T tolerance = 0) const + { + Mat3 inv(this->adjoint()); + + const T det = inv.mm[0]*MyBase::mm[0] + inv.mm[1]*MyBase::mm[3] + inv.mm[2]*MyBase::mm[6]; + + // If the determinant is 0, m was singular and the result will be invalid. + if (isApproxEqual(det,T(0.0),tolerance)) { + OPENVDB_THROW(ArithmeticError, "Inversion of singular 3x3 matrix"); + } + return inv * (T(1)/det); + } // invertTest + + /// Determinant of matrix + T det() const + { + const T co00 = MyBase::mm[4]*MyBase::mm[8] - MyBase::mm[5]*MyBase::mm[7]; + const T co10 = MyBase::mm[5]*MyBase::mm[6] - MyBase::mm[3]*MyBase::mm[8]; + const T co20 = MyBase::mm[3]*MyBase::mm[7] - MyBase::mm[4]*MyBase::mm[6]; + return MyBase::mm[0]*co00 + MyBase::mm[1]*co10 + MyBase::mm[2]*co20; + } // determinantTest + + /// Trace of matrix + T trace() const + { + return MyBase::mm[0]+MyBase::mm[4]+MyBase::mm[8]; + } + + /// This function snaps a specific axis to a specific direction, + /// preserving scaling. It does this using minimum energy, thus + /// posing a unique solution if basis & direction arent parralel. + /// Direction need not be unit. + Mat3 snapBasis(Axis axis, const Vec3 &direction) + { + return snapMatBasis(*this, axis, direction); + } + + /// Return the transformed vector by this matrix. + /// This function is equivalent to post-multiplying the matrix. + template + Vec3 transform(const Vec3 &v) const + { + return static_cast< Vec3 >(v * *this); + } // xformVectorTest + + /// Return the transformed vector by transpose of this matrix. + /// This function is equivalent to pre-multiplying the matrix. + template + Vec3 pretransform(const Vec3 &v) const + { + return static_cast< Vec3 >(*this * v); + } // xformTVectorTest + + + /// @brief Treat @a diag as a diagonal matrix and return the product + /// of this matrix with @a diag (from the right). + Mat3 timesDiagonal(const Vec3& diag) const + { + Mat3 ret(*this); + + ret.mm[0] *= diag(0); + ret.mm[1] *= diag(1); + ret.mm[2] *= diag(2); + ret.mm[3] *= diag(0); + ret.mm[4] *= diag(1); + ret.mm[5] *= diag(2); + ret.mm[6] *= diag(0); + ret.mm[7] *= diag(1); + ret.mm[8] *= diag(2); + return ret; + } +}; // class Mat3 + + +/// @relates Mat3 +/// @brief Equality operator, does exact floating point comparisons +template +bool operator==(const Mat3 &m0, const Mat3 &m1) +{ + const T0 *t0 = m0.asPointer(); + const T1 *t1 = m1.asPointer(); + + for (int i=0; i<9; ++i) { + if (!isExactlyEqual(t0[i], t1[i])) return false; + } + return true; +} + +/// @relates Mat3 +/// @brief Inequality operator, does exact floating point comparisons +template +bool operator!=(const Mat3 &m0, const Mat3 &m1) { return !(m0 == m1); } + +/// @relates Mat3 +/// @brief Multiply each element of the given matrix by @a scalar and return the result. +template +Mat3::type> operator*(S scalar, const Mat3 &m) +{ return m*scalar; } + +/// @relates Mat3 +/// @brief Multiply each element of the given matrix by @a scalar and return the result. +template +Mat3::type> operator*(const Mat3 &m, S scalar) +{ + Mat3::type> result(m); + result *= scalar; + return result; +} + +/// @relates Mat3 +/// @brief Add corresponding elements of @a m0 and @a m1 and return the result. +template +Mat3::type> operator+(const Mat3 &m0, const Mat3 &m1) +{ + Mat3::type> result(m0); + result += m1; + return result; +} + +/// @relates Mat3 +/// @brief Subtract corresponding elements of @a m0 and @a m1 and return the result. +template +Mat3::type> operator-(const Mat3 &m0, const Mat3 &m1) +{ + Mat3::type> result(m0); + result -= m1; + return result; +} + + +/// @brief Multiply @a m0 by @a m1 and return the resulting matrix. +template +Mat3::type>operator*(const Mat3 &m0, const Mat3 &m1) +{ + Mat3::type> result(m0); + result *= m1; + return result; +} + +/// @relates Mat3 +/// @brief Multiply @a _m by @a _v and return the resulting vector. +template +inline Vec3::type> +operator*(const Mat3 &_m, const Vec3 &_v) +{ + MT const *m = _m.asPointer(); + return Vec3::type>( + _v[0]*m[0] + _v[1]*m[1] + _v[2]*m[2], + _v[0]*m[3] + _v[1]*m[4] + _v[2]*m[5], + _v[0]*m[6] + _v[1]*m[7] + _v[2]*m[8]); +} + +/// @relates Mat3 +/// @brief Multiply @a _v by @a _m and return the resulting vector. +template +inline Vec3::type> +operator*(const Vec3 &_v, const Mat3 &_m) +{ + MT const *m = _m.asPointer(); + return Vec3::type>( + _v[0]*m[0] + _v[1]*m[3] + _v[2]*m[6], + _v[0]*m[1] + _v[1]*m[4] + _v[2]*m[7], + _v[0]*m[2] + _v[1]*m[5] + _v[2]*m[8]); +} + +/// @relates Mat3 +/// @brief Multiply @a _v by @a _m and replace @a _v with the resulting vector. +template +inline Vec3 &operator *= (Vec3 &_v, const Mat3 &_m) +{ + Vec3 mult = _v * _m; + _v = mult; + return _v; +} + +/// Returns outer product of v1, v2, i.e. v1 v2^T if v1 and v2 are +/// column vectors, e.g. M = Mat3f::outerproduct(v1,v2); +template +Mat3 outerProduct(const Vec3& v1, const Vec3& v2) +{ + return Mat3(v1[0]*v2[0], v1[0]*v2[1], v1[0]*v2[2], + v1[1]*v2[0], v1[1]*v2[1], v1[1]*v2[2], + v1[2]*v2[0], v1[2]*v2[1], v1[2]*v2[2]); +}// outerProduct + + +/// Interpolate the rotation between m1 and m2 using Mat::powSolve. +/// Unlike slerp, translation is not treated independently. +/// This results in smoother animation results. +template +Mat3 powLerp(const Mat3 &m1, const Mat3 &m2, T t) +{ + Mat3 x = m1.inverse() * m2; + powSolve(x, x, t); + Mat3 m = m1 * x; + return m; +} + + +namespace mat3_internal { + +template +inline void +pivot(int i, int j, Mat3& S, Vec3& D, Mat3& Q) +{ + const int& n = Mat3::size; // should be 3 + T temp; + /// scratch variables used in pivoting + double cotan_of_2_theta; + double tan_of_theta; + double cosin_of_theta; + double sin_of_theta; + double z; + + double Sij = S(i,j); + + double Sjj_minus_Sii = D[j] - D[i]; + + if (fabs(Sjj_minus_Sii) * (10*math::Tolerance::value()) > fabs(Sij)) { + tan_of_theta = Sij / Sjj_minus_Sii; + } else { + /// pivot on Sij + cotan_of_2_theta = 0.5*Sjj_minus_Sii / Sij ; + + if (cotan_of_2_theta < 0.) { + tan_of_theta = + -1./(sqrt(1. + cotan_of_2_theta*cotan_of_2_theta) - cotan_of_2_theta); + } else { + tan_of_theta = + 1./(sqrt(1. + cotan_of_2_theta*cotan_of_2_theta) + cotan_of_2_theta); + } + } + + cosin_of_theta = 1./sqrt( 1. + tan_of_theta * tan_of_theta); + sin_of_theta = cosin_of_theta * tan_of_theta; + z = tan_of_theta * Sij; + S(i,j) = 0; + D[i] -= z; + D[j] += z; + for (int k = 0; k < i; ++k) { + temp = S(k,i); + S(k,i) = cosin_of_theta * temp - sin_of_theta * S(k,j); + S(k,j)= sin_of_theta * temp + cosin_of_theta * S(k,j); + } + for (int k = i+1; k < j; ++k) { + temp = S(i,k); + S(i,k) = cosin_of_theta * temp - sin_of_theta * S(k,j); + S(k,j) = sin_of_theta * temp + cosin_of_theta * S(k,j); + } + for (int k = j+1; k < n; ++k) { + temp = S(i,k); + S(i,k) = cosin_of_theta * temp - sin_of_theta * S(j,k); + S(j,k) = sin_of_theta * temp + cosin_of_theta * S(j,k); + } + for (int k = 0; k < n; ++k) + { + temp = Q(k,i); + Q(k,i) = cosin_of_theta * temp - sin_of_theta*Q(k,j); + Q(k,j) = sin_of_theta * temp + cosin_of_theta*Q(k,j); + } +} + +} // namespace mat3_internal + + +/// @brief Use Jacobi iterations to decompose a symmetric 3x3 matrix +/// (diagonalize and compute eigenvectors) +/// @details This is based on the "Efficient numerical diagonalization of Hermitian 3x3 matrices" +/// Joachim Kopp. arXiv.org preprint: physics/0610206 +/// with the addition of largest pivot +template +inline bool +diagonalizeSymmetricMatrix(const Mat3& input, Mat3& Q, Vec3& D, + unsigned int MAX_ITERATIONS=250) +{ + /// use Givens rotation matrix to eliminate off-diagonal entries. + /// initialize the rotation matrix as idenity + Q = Mat3::identity(); + int n = Mat3::size; // should be 3 + + /// temp matrix. Assumed to be symmetric + Mat3 S(input); + + for (int i = 0; i < n; ++i) { + D[i] = S(i,i); + } + + unsigned int iterations(0); + /// Just iterate over all the non-diagonal enteries + /// using the largest as a pivot. + do { + /// check for absolute convergence + /// are symmetric off diagonals all zero + double er = 0; + for (int i = 0; i < n; ++i) { + for (int j = i+1; j < n; ++j) { + er += fabs(S(i,j)); + } + } + if (std::abs(er) < math::Tolerance::value()) { + return true; + } + iterations++; + + T max_element = 0; + int ip = 0; + int jp = 0; + /// loop over all the off-diagonals above the diagonal + for (int i = 0; i < n; ++i) { + for (int j = i+1; j < n; ++j){ + + if ( fabs(D[i]) * (10*math::Tolerance::value()) > fabs(S(i,j))) { + /// value too small to pivot on + S(i,j) = 0; + } + if (fabs(S(i,j)) > max_element) { + max_element = fabs(S(i,j)); + ip = i; + jp = j; + } + } + } + mat3_internal::pivot(ip, jp, S, D, Q); + } while (iterations < MAX_ITERATIONS); + + return false; +} + + +using Mat3s = Mat3; +using Mat3d = Mat3; +using Mat3f = Mat3d; + +} // namespace math + + +template<> inline math::Mat3s zeroVal() { return math::Mat3s::zero(); } +template<> inline math::Mat3d zeroVal() { return math::Mat3d::zero(); } + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_MAT3_H_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Mat4.h b/openvdb/math/Mat4.h new file mode 100644 index 00000000..51317af2 --- /dev/null +++ b/openvdb/math/Mat4.h @@ -0,0 +1,1346 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_MAT4_H_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_MAT4_H_HAS_BEEN_INCLUDED + +#include +#include +#include "Math.h" +#include "Mat3.h" +#include "Vec3.h" +#include "Vec4.h" +#include // for std::copy(), std::swap() +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Vec4; + + +/// @class Mat4 Mat4.h +/// @brief 4x4 -matrix class. +template +class Mat4: public Mat<4, T> +{ +public: + /// Data type held by the matrix. + using value_type = T; + using ValueType = T; + using MyBase = Mat<4, T>; + + /// Trivial constructor, the matrix is NOT initialized + Mat4() {} + + /// Constructor given array of elements, the ordering is in row major form: + /** @verbatim + a[ 0] a[1] a[ 2] a[ 3] + a[ 4] a[5] a[ 6] a[ 7] + a[ 8] a[9] a[10] a[11] + a[12] a[13] a[14] a[15] + @endverbatim */ + template + Mat4(Source *a) + { + for (int i = 0; i < 16; i++) { + MyBase::mm[i] = static_cast(a[i]); + } + } + + /// Constructor given array of elements, the ordering is in row major form: + /** @verbatim + a b c d + e f g h + i j k l + m n o p + @endverbatim */ + template + Mat4(Source a, Source b, Source c, Source d, + Source e, Source f, Source g, Source h, + Source i, Source j, Source k, Source l, + Source m, Source n, Source o, Source p) + { + MyBase::mm[ 0] = static_cast(a); + MyBase::mm[ 1] = static_cast(b); + MyBase::mm[ 2] = static_cast(c); + MyBase::mm[ 3] = static_cast(d); + + MyBase::mm[ 4] = static_cast(e); + MyBase::mm[ 5] = static_cast(f); + MyBase::mm[ 6] = static_cast(g); + MyBase::mm[ 7] = static_cast(h); + + MyBase::mm[ 8] = static_cast(i); + MyBase::mm[ 9] = static_cast(j); + MyBase::mm[10] = static_cast(k); + MyBase::mm[11] = static_cast(l); + + MyBase::mm[12] = static_cast(m); + MyBase::mm[13] = static_cast(n); + MyBase::mm[14] = static_cast(o); + MyBase::mm[15] = static_cast(p); + } + + /// Construct matrix from rows or columns vectors (defaults to rows + /// for historical reasons) + template + Mat4(const Vec4 &v1, const Vec4 &v2, + const Vec4 &v3, const Vec4 &v4, bool rows = true) + { + if (rows) { + this->setRows(v1, v2, v3, v4); + } else { + this->setColumns(v1, v2, v3, v4); + } + } + + /// Copy constructor + Mat4(const Mat<4, T> &m) + { + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + MyBase::mm[i*4 + j] = m[i][j]; + } + } + } + + /// Conversion constructor + template + explicit Mat4(const Mat4 &m) + { + const Source *src = m.asPointer(); + + for (int i=0; i<16; ++i) { + MyBase::mm[i] = static_cast(src[i]); + } + } + + /// Predefined constant for identity matrix + static const Mat4& identity() { + static const Mat4 sIdentity = Mat4( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); + return sIdentity; + } + + /// Predefined constant for zero matrix + static const Mat4& zero() { + static const Mat4 sZero = Mat4( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 + ); + return sZero; + } + + /// Set ith row to vector v + void setRow(int i, const Vec4 &v) + { + // assert(i>=0 && i<4); + int i4 = i * 4; + MyBase::mm[i4+0] = v[0]; + MyBase::mm[i4+1] = v[1]; + MyBase::mm[i4+2] = v[2]; + MyBase::mm[i4+3] = v[3]; + } + + /// Get ith row, e.g. Vec4f v = m.row(1); + Vec4 row(int i) const + { + // assert(i>=0 && i<3); + return Vec4((*this)(i,0), (*this)(i,1), (*this)(i,2), (*this)(i,3)); + } + + /// Set jth column to vector v + void setCol(int j, const Vec4& v) + { + // assert(j>=0 && j<4); + MyBase::mm[ 0+j] = v[0]; + MyBase::mm[ 4+j] = v[1]; + MyBase::mm[ 8+j] = v[2]; + MyBase::mm[12+j] = v[3]; + } + + /// Get jth column, e.g. Vec4f v = m.col(0); + Vec4 col(int j) const + { + // assert(j>=0 && j<4); + return Vec4((*this)(0,j), (*this)(1,j), (*this)(2,j), (*this)(3,j)); + } + + //@{ + /// Array style reference to ith row + /// e.g. m[1][3] = 4; + T* operator[](int i) { return &(MyBase::mm[i<<2]); } + const T* operator[](int i) const { return &(MyBase::mm[i<<2]); } + //@} + + /// Direct access to the internal data + T* asPointer() {return MyBase::mm;} + const T* asPointer() const {return MyBase::mm;} + + /// Alternative indexed reference to the elements + /// Note that the indices are row first and column second. + /// e.g. m(0,0) = 1; + T& operator()(int i, int j) + { + // assert(i>=0 && i<4); + // assert(j>=0 && j<4); + return MyBase::mm[4*i+j]; + } + + /// Alternative indexed constant reference to the elements, + /// Note that the indices are row first and column second. + /// e.g. float f = m(1,0); + T operator()(int i, int j) const + { + // assert(i>=0 && i<4); + // assert(j>=0 && j<4); + return MyBase::mm[4*i+j]; + } + + /// Set the rows of this matrix to the vectors v1, v2, v3, v4 + void setRows(const Vec4 &v1, const Vec4 &v2, + const Vec4 &v3, const Vec4 &v4) + { + MyBase::mm[ 0] = v1[0]; + MyBase::mm[ 1] = v1[1]; + MyBase::mm[ 2] = v1[2]; + MyBase::mm[ 3] = v1[3]; + + MyBase::mm[ 4] = v2[0]; + MyBase::mm[ 5] = v2[1]; + MyBase::mm[ 6] = v2[2]; + MyBase::mm[ 7] = v2[3]; + + MyBase::mm[ 8] = v3[0]; + MyBase::mm[ 9] = v3[1]; + MyBase::mm[10] = v3[2]; + MyBase::mm[11] = v3[3]; + + MyBase::mm[12] = v4[0]; + MyBase::mm[13] = v4[1]; + MyBase::mm[14] = v4[2]; + MyBase::mm[15] = v4[3]; + } + + /// Set the columns of this matrix to the vectors v1, v2, v3, v4 + void setColumns(const Vec4 &v1, const Vec4 &v2, + const Vec4 &v3, const Vec4 &v4) + { + MyBase::mm[ 0] = v1[0]; + MyBase::mm[ 1] = v2[0]; + MyBase::mm[ 2] = v3[0]; + MyBase::mm[ 3] = v4[0]; + + MyBase::mm[ 4] = v1[1]; + MyBase::mm[ 5] = v2[1]; + MyBase::mm[ 6] = v3[1]; + MyBase::mm[ 7] = v4[1]; + + MyBase::mm[ 8] = v1[2]; + MyBase::mm[ 9] = v2[2]; + MyBase::mm[10] = v3[2]; + MyBase::mm[11] = v4[2]; + + MyBase::mm[12] = v1[3]; + MyBase::mm[13] = v2[3]; + MyBase::mm[14] = v3[3]; + MyBase::mm[15] = v4[3]; + } + + // Set this matrix to zero + void setZero() + { + MyBase::mm[ 0] = 0; + MyBase::mm[ 1] = 0; + MyBase::mm[ 2] = 0; + MyBase::mm[ 3] = 0; + MyBase::mm[ 4] = 0; + MyBase::mm[ 5] = 0; + MyBase::mm[ 6] = 0; + MyBase::mm[ 7] = 0; + MyBase::mm[ 8] = 0; + MyBase::mm[ 9] = 0; + MyBase::mm[10] = 0; + MyBase::mm[11] = 0; + MyBase::mm[12] = 0; + MyBase::mm[13] = 0; + MyBase::mm[14] = 0; + MyBase::mm[15] = 0; + } + + /// Set this matrix to identity + void setIdentity() + { + MyBase::mm[ 0] = 1; + MyBase::mm[ 1] = 0; + MyBase::mm[ 2] = 0; + MyBase::mm[ 3] = 0; + + MyBase::mm[ 4] = 0; + MyBase::mm[ 5] = 1; + MyBase::mm[ 6] = 0; + MyBase::mm[ 7] = 0; + + MyBase::mm[ 8] = 0; + MyBase::mm[ 9] = 0; + MyBase::mm[10] = 1; + MyBase::mm[11] = 0; + + MyBase::mm[12] = 0; + MyBase::mm[13] = 0; + MyBase::mm[14] = 0; + MyBase::mm[15] = 1; + } + + + /// Set upper left to a Mat3 + void setMat3(const Mat3 &m) + { + for (int i = 0; i < 3; i++) + for (int j=0; j < 3; j++) + MyBase::mm[i*4+j] = m[i][j]; + } + + Mat3 getMat3() const + { + Mat3 m; + + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + m[i][j] = MyBase::mm[i*4+j]; + + return m; + } + + /// Return the translation component + Vec3 getTranslation() const + { + return Vec3(MyBase::mm[12], MyBase::mm[13], MyBase::mm[14]); + } + + void setTranslation(const Vec3 &t) + { + MyBase::mm[12] = t[0]; + MyBase::mm[13] = t[1]; + MyBase::mm[14] = t[2]; + } + + /// Assignment operator + template + const Mat4& operator=(const Mat4 &m) + { + const Source *src = m.asPointer(); + + // don't suppress warnings when assigning from different numerical types + std::copy(src, (src + this->numElements()), MyBase::mm); + return *this; + } + + /// Return @c true if this matrix is equivalent to @a m within a tolerance of @a eps. + bool eq(const Mat4 &m, T eps=1.0e-8) const + { + for (int i = 0; i < 16; i++) { + if (!isApproxEqual(MyBase::mm[i], m.mm[i], eps)) + return false; + } + return true; + } + + /// Negation operator, for e.g. m1 = -m2; + Mat4 operator-() const + { + return Mat4( + -MyBase::mm[ 0], -MyBase::mm[ 1], -MyBase::mm[ 2], -MyBase::mm[ 3], + -MyBase::mm[ 4], -MyBase::mm[ 5], -MyBase::mm[ 6], -MyBase::mm[ 7], + -MyBase::mm[ 8], -MyBase::mm[ 9], -MyBase::mm[10], -MyBase::mm[11], + -MyBase::mm[12], -MyBase::mm[13], -MyBase::mm[14], -MyBase::mm[15] + ); + } // trivial + + /// Multiply each element of this matrix by @a scalar. + template + const Mat4& operator*=(S scalar) + { + MyBase::mm[ 0] *= scalar; + MyBase::mm[ 1] *= scalar; + MyBase::mm[ 2] *= scalar; + MyBase::mm[ 3] *= scalar; + + MyBase::mm[ 4] *= scalar; + MyBase::mm[ 5] *= scalar; + MyBase::mm[ 6] *= scalar; + MyBase::mm[ 7] *= scalar; + + MyBase::mm[ 8] *= scalar; + MyBase::mm[ 9] *= scalar; + MyBase::mm[10] *= scalar; + MyBase::mm[11] *= scalar; + + MyBase::mm[12] *= scalar; + MyBase::mm[13] *= scalar; + MyBase::mm[14] *= scalar; + MyBase::mm[15] *= scalar; + return *this; + } + + /// Add each element of the given matrix to the corresponding element of this matrix. + template + const Mat4 &operator+=(const Mat4 &m1) + { + const S* s = m1.asPointer(); + + MyBase::mm[ 0] += s[ 0]; + MyBase::mm[ 1] += s[ 1]; + MyBase::mm[ 2] += s[ 2]; + MyBase::mm[ 3] += s[ 3]; + + MyBase::mm[ 4] += s[ 4]; + MyBase::mm[ 5] += s[ 5]; + MyBase::mm[ 6] += s[ 6]; + MyBase::mm[ 7] += s[ 7]; + + MyBase::mm[ 8] += s[ 8]; + MyBase::mm[ 9] += s[ 9]; + MyBase::mm[10] += s[10]; + MyBase::mm[11] += s[11]; + + MyBase::mm[12] += s[12]; + MyBase::mm[13] += s[13]; + MyBase::mm[14] += s[14]; + MyBase::mm[15] += s[15]; + + return *this; + } + + /// Subtract each element of the given matrix from the corresponding element of this matrix. + template + const Mat4 &operator-=(const Mat4 &m1) + { + const S* s = m1.asPointer(); + + MyBase::mm[ 0] -= s[ 0]; + MyBase::mm[ 1] -= s[ 1]; + MyBase::mm[ 2] -= s[ 2]; + MyBase::mm[ 3] -= s[ 3]; + + MyBase::mm[ 4] -= s[ 4]; + MyBase::mm[ 5] -= s[ 5]; + MyBase::mm[ 6] -= s[ 6]; + MyBase::mm[ 7] -= s[ 7]; + + MyBase::mm[ 8] -= s[ 8]; + MyBase::mm[ 9] -= s[ 9]; + MyBase::mm[10] -= s[10]; + MyBase::mm[11] -= s[11]; + + MyBase::mm[12] -= s[12]; + MyBase::mm[13] -= s[13]; + MyBase::mm[14] -= s[14]; + MyBase::mm[15] -= s[15]; + + return *this; + } + + /// Multiply this matrix by the given matrix. + template + const Mat4 &operator*=(const Mat4 &m1) + { + Mat4 m0(*this); + + const T* s0 = m0.asPointer(); + const S* s1 = m1.asPointer(); + + for (int i = 0; i < 4; i++) { + int i4 = 4 * i; + MyBase::mm[i4+0] = static_cast(s0[i4+0] * s1[ 0] + + s0[i4+1] * s1[ 4] + + s0[i4+2] * s1[ 8] + + s0[i4+3] * s1[12]); + + MyBase::mm[i4+1] = static_cast(s0[i4+0] * s1[ 1] + + s0[i4+1] * s1[ 5] + + s0[i4+2] * s1[ 9] + + s0[i4+3] * s1[13]); + + MyBase::mm[i4+2] = static_cast(s0[i4+0] * s1[ 2] + + s0[i4+1] * s1[ 6] + + s0[i4+2] * s1[10] + + s0[i4+3] * s1[14]); + + MyBase::mm[i4+3] = static_cast(s0[i4+0] * s1[ 3] + + s0[i4+1] * s1[ 7] + + s0[i4+2] * s1[11] + + s0[i4+3] * s1[15]); + } + return *this; + } + + /// @return transpose of this + Mat4 transpose() const + { + return Mat4( + MyBase::mm[ 0], MyBase::mm[ 4], MyBase::mm[ 8], MyBase::mm[12], + MyBase::mm[ 1], MyBase::mm[ 5], MyBase::mm[ 9], MyBase::mm[13], + MyBase::mm[ 2], MyBase::mm[ 6], MyBase::mm[10], MyBase::mm[14], + MyBase::mm[ 3], MyBase::mm[ 7], MyBase::mm[11], MyBase::mm[15] + ); + } + + + /// @return inverse of this + /// @throw ArithmeticError if singular + Mat4 inverse(T tolerance = 0) const + { + // + // inv [ A | b ] = [ E | f ] A: 3x3, b: 3x1, c': 1x3 d: 1x1 + // [ c' | d ] [ g' | h ] + // + // If A is invertible use + // + // E = A^-1 + p*h*r + // p = A^-1 * b + // f = -p * h + // g' = -h * c' + // h = 1 / (d - c'*p) + // r' = c'*A^-1 + // + // Otherwise use gauss-jordan elimination + // + + // + // We create this alias to ourself so we can easily use own subscript + // operator. + const Mat4& m(*this); + + T m0011 = m[0][0] * m[1][1]; + T m0012 = m[0][0] * m[1][2]; + T m0110 = m[0][1] * m[1][0]; + T m0210 = m[0][2] * m[1][0]; + T m0120 = m[0][1] * m[2][0]; + T m0220 = m[0][2] * m[2][0]; + + T detA = m0011 * m[2][2] - m0012 * m[2][1] - m0110 * m[2][2] + + m0210 * m[2][1] + m0120 * m[1][2] - m0220 * m[1][1]; + + bool hasPerspective = + (!isExactlyEqual(m[0][3], T(0.0)) || + !isExactlyEqual(m[1][3], T(0.0)) || + !isExactlyEqual(m[2][3], T(0.0)) || + !isExactlyEqual(m[3][3], T(1.0))); + + T det; + if (hasPerspective) { + det = m[0][3] * det3(m, 1,2,3, 0,2,1) + + m[1][3] * det3(m, 2,0,3, 0,2,1) + + m[2][3] * det3(m, 3,0,1, 0,2,1) + + m[3][3] * detA; + } else { + det = detA * m[3][3]; + } + + Mat4 inv; + bool invertible; + + if (isApproxEqual(det,T(0.0),tolerance)) { + invertible = false; + + } else if (isApproxEqual(detA,T(0.0),T(1e-8))) { + // det is too small to rely on inversion by subblocks + invertible = m.invert(inv, tolerance); + + } else { + invertible = true; + detA = 1.0 / detA; + + // + // Calculate A^-1 + // + inv[0][0] = detA * ( m[1][1] * m[2][2] - m[1][2] * m[2][1]); + inv[0][1] = detA * (-m[0][1] * m[2][2] + m[0][2] * m[2][1]); + inv[0][2] = detA * ( m[0][1] * m[1][2] - m[0][2] * m[1][1]); + + inv[1][0] = detA * (-m[1][0] * m[2][2] + m[1][2] * m[2][0]); + inv[1][1] = detA * ( m[0][0] * m[2][2] - m0220); + inv[1][2] = detA * ( m0210 - m0012); + + inv[2][0] = detA * ( m[1][0] * m[2][1] - m[1][1] * m[2][0]); + inv[2][1] = detA * ( m0120 - m[0][0] * m[2][1]); + inv[2][2] = detA * ( m0011 - m0110); + + if (hasPerspective) { + // + // Calculate r, p, and h + // + Vec3 r; + r[0] = m[3][0] * inv[0][0] + m[3][1] * inv[1][0] + + m[3][2] * inv[2][0]; + r[1] = m[3][0] * inv[0][1] + m[3][1] * inv[1][1] + + m[3][2] * inv[2][1]; + r[2] = m[3][0] * inv[0][2] + m[3][1] * inv[1][2] + + m[3][2] * inv[2][2]; + + Vec3 p; + p[0] = inv[0][0] * m[0][3] + inv[0][1] * m[1][3] + + inv[0][2] * m[2][3]; + p[1] = inv[1][0] * m[0][3] + inv[1][1] * m[1][3] + + inv[1][2] * m[2][3]; + p[2] = inv[2][0] * m[0][3] + inv[2][1] * m[1][3] + + inv[2][2] * m[2][3]; + + T h = m[3][3] - p.dot(Vec3(m[3][0],m[3][1],m[3][2])); + if (isApproxEqual(h,T(0.0),tolerance)) { + invertible = false; + + } else { + h = 1.0 / h; + + // + // Calculate h, g, and f + // + inv[3][3] = h; + inv[3][0] = -h * r[0]; + inv[3][1] = -h * r[1]; + inv[3][2] = -h * r[2]; + + inv[0][3] = -h * p[0]; + inv[1][3] = -h * p[1]; + inv[2][3] = -h * p[2]; + + // + // Calculate E + // + p *= h; + inv[0][0] += p[0] * r[0]; + inv[0][1] += p[0] * r[1]; + inv[0][2] += p[0] * r[2]; + inv[1][0] += p[1] * r[0]; + inv[1][1] += p[1] * r[1]; + inv[1][2] += p[1] * r[2]; + inv[2][0] += p[2] * r[0]; + inv[2][1] += p[2] * r[1]; + inv[2][2] += p[2] * r[2]; + } + } else { + // Equations are much simpler in the non-perspective case + inv[3][0] = - (m[3][0] * inv[0][0] + m[3][1] * inv[1][0] + + m[3][2] * inv[2][0]); + inv[3][1] = - (m[3][0] * inv[0][1] + m[3][1] * inv[1][1] + + m[3][2] * inv[2][1]); + inv[3][2] = - (m[3][0] * inv[0][2] + m[3][1] * inv[1][2] + + m[3][2] * inv[2][2]); + inv[0][3] = 0.0; + inv[1][3] = 0.0; + inv[2][3] = 0.0; + inv[3][3] = 1.0; + } + } + + if (!invertible) OPENVDB_THROW(ArithmeticError, "Inversion of singular 4x4 matrix"); + return inv; + } + + + /// Determinant of matrix + T det() const + { + const T *ap; + Mat3 submat; + T det; + T *sp; + int i, j, k, sign; + + det = 0; + sign = 1; + for (i = 0; i < 4; i++) { + ap = &MyBase::mm[ 0]; + sp = submat.asPointer(); + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + if ((k != i) && (j != 0)) { + *sp++ = *ap; + } + ap++; + } + } + + det += sign * MyBase::mm[i] * submat.det(); + sign = -sign; + } + + return det; + } + + /// Sets the matrix to a matrix that translates by v + static Mat4 translation(const Vec3d& v) + { + return Mat4( + T(1), T(0), T(0), T(0), + T(0), T(1), T(0), T(0), + T(0), T(0), T(1), T(0), + T(v.x()), T(v.y()),T(v.z()), T(1)); + } + + /// Sets the matrix to a matrix that translates by v + template + void setToTranslation(const Vec3& v) + { + MyBase::mm[ 0] = 1; + MyBase::mm[ 1] = 0; + MyBase::mm[ 2] = 0; + MyBase::mm[ 3] = 0; + + MyBase::mm[ 4] = 0; + MyBase::mm[ 5] = 1; + MyBase::mm[ 6] = 0; + MyBase::mm[ 7] = 0; + + MyBase::mm[ 8] = 0; + MyBase::mm[ 9] = 0; + MyBase::mm[10] = 1; + MyBase::mm[11] = 0; + + MyBase::mm[12] = v.x(); + MyBase::mm[13] = v.y(); + MyBase::mm[14] = v.z(); + MyBase::mm[15] = 1; + } + + /// Left multiples by the specified translation, i.e. Trans * (*this) + template + void preTranslate(const Vec3& tr) + { + Vec3 tmp(tr.x(), tr.y(), tr.z()); + Mat4 Tr = Mat4::translation(tmp); + + *this = Tr * (*this); + + } + + /// Right multiplies by the specified translation matrix, i.e. (*this) * Trans + template + void postTranslate(const Vec3& tr) + { + Vec3 tmp(tr.x(), tr.y(), tr.z()); + Mat4 Tr = Mat4::translation(tmp); + + *this = (*this) * Tr; + + } + + + /// Sets the matrix to a matrix that scales by v + template + void setToScale(const Vec3& v) + { + this->setIdentity(); + MyBase::mm[ 0] = v.x(); + MyBase::mm[ 5] = v.y(); + MyBase::mm[10] = v.z(); + } + + // Left multiples by the specified scale matrix, i.e. Sc * (*this) + template + void preScale(const Vec3& v) + { + MyBase::mm[ 0] *= v.x(); + MyBase::mm[ 1] *= v.x(); + MyBase::mm[ 2] *= v.x(); + MyBase::mm[ 3] *= v.x(); + + MyBase::mm[ 4] *= v.y(); + MyBase::mm[ 5] *= v.y(); + MyBase::mm[ 6] *= v.y(); + MyBase::mm[ 7] *= v.y(); + + MyBase::mm[ 8] *= v.z(); + MyBase::mm[ 9] *= v.z(); + MyBase::mm[10] *= v.z(); + MyBase::mm[11] *= v.z(); + } + + + + // Right multiples by the specified scale matrix, i.e. (*this) * Sc + template + void postScale(const Vec3& v) + { + + MyBase::mm[ 0] *= v.x(); + MyBase::mm[ 1] *= v.y(); + MyBase::mm[ 2] *= v.z(); + + MyBase::mm[ 4] *= v.x(); + MyBase::mm[ 5] *= v.y(); + MyBase::mm[ 6] *= v.z(); + + MyBase::mm[ 8] *= v.x(); + MyBase::mm[ 9] *= v.y(); + MyBase::mm[10] *= v.z(); + + MyBase::mm[12] *= v.x(); + MyBase::mm[13] *= v.y(); + MyBase::mm[14] *= v.z(); + + } + + + /// @brief Sets the matrix to a rotation about the given axis. + /// @param axis The axis (one of X, Y, Z) to rotate about. + /// @param angle The rotation angle, in radians. + void setToRotation(Axis axis, T angle) {*this = rotation >(axis, angle);} + + /// @brief Sets the matrix to a rotation about an arbitrary axis + /// @param axis The axis of rotation (cannot be zero-length) + /// @param angle The rotation angle, in radians. + void setToRotation(const Vec3& axis, T angle) {*this = rotation >(axis, angle);} + + /// @brief Sets the matrix to a rotation that maps v1 onto v2 about the cross + /// product of v1 and v2. + void setToRotation(const Vec3& v1, const Vec3& v2) {*this = rotation >(v1, v2);} + + + /// @brief Left multiplies by a rotation clock-wiseabout the given axis into this matrix. + /// @param axis The axis (one of X, Y, Z) of rotation. + /// @param angle The clock-wise rotation angle, in radians. + void preRotate(Axis axis, T angle) + { + T c = static_cast(cos(angle)); + T s = -static_cast(sin(angle)); // the "-" makes it clockwise + + switch (axis) { + case X_AXIS: + { + T a4, a5, a6, a7; + + a4 = c * MyBase::mm[ 4] - s * MyBase::mm[ 8]; + a5 = c * MyBase::mm[ 5] - s * MyBase::mm[ 9]; + a6 = c * MyBase::mm[ 6] - s * MyBase::mm[10]; + a7 = c * MyBase::mm[ 7] - s * MyBase::mm[11]; + + + MyBase::mm[ 8] = s * MyBase::mm[ 4] + c * MyBase::mm[ 8]; + MyBase::mm[ 9] = s * MyBase::mm[ 5] + c * MyBase::mm[ 9]; + MyBase::mm[10] = s * MyBase::mm[ 6] + c * MyBase::mm[10]; + MyBase::mm[11] = s * MyBase::mm[ 7] + c * MyBase::mm[11]; + + MyBase::mm[ 4] = a4; + MyBase::mm[ 5] = a5; + MyBase::mm[ 6] = a6; + MyBase::mm[ 7] = a7; + } + break; + + case Y_AXIS: + { + T a0, a1, a2, a3; + + a0 = c * MyBase::mm[ 0] + s * MyBase::mm[ 8]; + a1 = c * MyBase::mm[ 1] + s * MyBase::mm[ 9]; + a2 = c * MyBase::mm[ 2] + s * MyBase::mm[10]; + a3 = c * MyBase::mm[ 3] + s * MyBase::mm[11]; + + MyBase::mm[ 8] = -s * MyBase::mm[ 0] + c * MyBase::mm[ 8]; + MyBase::mm[ 9] = -s * MyBase::mm[ 1] + c * MyBase::mm[ 9]; + MyBase::mm[10] = -s * MyBase::mm[ 2] + c * MyBase::mm[10]; + MyBase::mm[11] = -s * MyBase::mm[ 3] + c * MyBase::mm[11]; + + + MyBase::mm[ 0] = a0; + MyBase::mm[ 1] = a1; + MyBase::mm[ 2] = a2; + MyBase::mm[ 3] = a3; + } + break; + + case Z_AXIS: + { + T a0, a1, a2, a3; + + a0 = c * MyBase::mm[ 0] - s * MyBase::mm[ 4]; + a1 = c * MyBase::mm[ 1] - s * MyBase::mm[ 5]; + a2 = c * MyBase::mm[ 2] - s * MyBase::mm[ 6]; + a3 = c * MyBase::mm[ 3] - s * MyBase::mm[ 7]; + + MyBase::mm[ 4] = s * MyBase::mm[ 0] + c * MyBase::mm[ 4]; + MyBase::mm[ 5] = s * MyBase::mm[ 1] + c * MyBase::mm[ 5]; + MyBase::mm[ 6] = s * MyBase::mm[ 2] + c * MyBase::mm[ 6]; + MyBase::mm[ 7] = s * MyBase::mm[ 3] + c * MyBase::mm[ 7]; + + MyBase::mm[ 0] = a0; + MyBase::mm[ 1] = a1; + MyBase::mm[ 2] = a2; + MyBase::mm[ 3] = a3; + } + break; + + default: + assert(axis==X_AXIS || axis==Y_AXIS || axis==Z_AXIS); + } + } + + + /// @brief Right multiplies by a rotation clock-wiseabout the given axis into this matrix. + /// @param axis The axis (one of X, Y, Z) of rotation. + /// @param angle The clock-wise rotation angle, in radians. + void postRotate(Axis axis, T angle) + { + T c = static_cast(cos(angle)); + T s = -static_cast(sin(angle)); // the "-" makes it clockwise + + + + switch (axis) { + case X_AXIS: + { + T a2, a6, a10, a14; + + a2 = c * MyBase::mm[ 2] - s * MyBase::mm[ 1]; + a6 = c * MyBase::mm[ 6] - s * MyBase::mm[ 5]; + a10 = c * MyBase::mm[10] - s * MyBase::mm[ 9]; + a14 = c * MyBase::mm[14] - s * MyBase::mm[13]; + + + MyBase::mm[ 1] = c * MyBase::mm[ 1] + s * MyBase::mm[ 2]; + MyBase::mm[ 5] = c * MyBase::mm[ 5] + s * MyBase::mm[ 6]; + MyBase::mm[ 9] = c * MyBase::mm[ 9] + s * MyBase::mm[10]; + MyBase::mm[13] = c * MyBase::mm[13] + s * MyBase::mm[14]; + + MyBase::mm[ 2] = a2; + MyBase::mm[ 6] = a6; + MyBase::mm[10] = a10; + MyBase::mm[14] = a14; + } + break; + + case Y_AXIS: + { + T a2, a6, a10, a14; + + a2 = c * MyBase::mm[ 2] + s * MyBase::mm[ 0]; + a6 = c * MyBase::mm[ 6] + s * MyBase::mm[ 4]; + a10 = c * MyBase::mm[10] + s * MyBase::mm[ 8]; + a14 = c * MyBase::mm[14] + s * MyBase::mm[12]; + + MyBase::mm[ 0] = c * MyBase::mm[ 0] - s * MyBase::mm[ 2]; + MyBase::mm[ 4] = c * MyBase::mm[ 4] - s * MyBase::mm[ 6]; + MyBase::mm[ 8] = c * MyBase::mm[ 8] - s * MyBase::mm[10]; + MyBase::mm[12] = c * MyBase::mm[12] - s * MyBase::mm[14]; + + MyBase::mm[ 2] = a2; + MyBase::mm[ 6] = a6; + MyBase::mm[10] = a10; + MyBase::mm[14] = a14; + } + break; + + case Z_AXIS: + { + T a1, a5, a9, a13; + + a1 = c * MyBase::mm[ 1] - s * MyBase::mm[ 0]; + a5 = c * MyBase::mm[ 5] - s * MyBase::mm[ 4]; + a9 = c * MyBase::mm[ 9] - s * MyBase::mm[ 8]; + a13 = c * MyBase::mm[13] - s * MyBase::mm[12]; + + MyBase::mm[ 0] = c * MyBase::mm[ 0] + s * MyBase::mm[ 1]; + MyBase::mm[ 4] = c * MyBase::mm[ 4] + s * MyBase::mm[ 5]; + MyBase::mm[ 8] = c * MyBase::mm[ 8] + s * MyBase::mm[ 9]; + MyBase::mm[12] = c * MyBase::mm[12] + s * MyBase::mm[13]; + + MyBase::mm[ 1] = a1; + MyBase::mm[ 5] = a5; + MyBase::mm[ 9] = a9; + MyBase::mm[13] = a13; + + } + break; + + default: + assert(axis==X_AXIS || axis==Y_AXIS || axis==Z_AXIS); + } + } + + /// @brief Sets the matrix to a shear along axis0 by a fraction of axis1. + /// @param axis0 The fixed axis of the shear. + /// @param axis1 The shear axis. + /// @param shearby The shear factor. + void setToShear(Axis axis0, Axis axis1, T shearby) + { + *this = shear >(axis0, axis1, shearby); + } + + + /// @brief Left multiplies a shearing transformation into the matrix. + /// @see setToShear + void preShear(Axis axis0, Axis axis1, T shear) + { + int index0 = static_cast(axis0); + int index1 = static_cast(axis1); + + // to row "index1" add a multiple of the index0 row + MyBase::mm[index1 * 4 + 0] += shear * MyBase::mm[index0 * 4 + 0]; + MyBase::mm[index1 * 4 + 1] += shear * MyBase::mm[index0 * 4 + 1]; + MyBase::mm[index1 * 4 + 2] += shear * MyBase::mm[index0 * 4 + 2]; + MyBase::mm[index1 * 4 + 3] += shear * MyBase::mm[index0 * 4 + 3]; + } + + + /// @brief Right multiplies a shearing transformation into the matrix. + /// @see setToShear + void postShear(Axis axis0, Axis axis1, T shear) + { + int index0 = static_cast(axis0); + int index1 = static_cast(axis1); + + // to collumn "index0" add a multiple of the index1 row + MyBase::mm[index0 + 0] += shear * MyBase::mm[index1 + 0]; + MyBase::mm[index0 + 4] += shear * MyBase::mm[index1 + 4]; + MyBase::mm[index0 + 8] += shear * MyBase::mm[index1 + 8]; + MyBase::mm[index0 + 12] += shear * MyBase::mm[index1 + 12]; + + } + + /// Transform a Vec4 by post-multiplication. + template + Vec4 transform(const Vec4 &v) const + { + return static_cast< Vec4 >(v * *this); + } + + /// Transform a Vec3 by post-multiplication, without homogenous division. + template + Vec3 transform(const Vec3 &v) const + { + return static_cast< Vec3 >(v * *this); + } + + /// Transform a Vec4 by pre-multiplication. + template + Vec4 pretransform(const Vec4 &v) const + { + return static_cast< Vec4 >(*this * v); + } + + /// Transform a Vec3 by pre-multiplication, without homogenous division. + template + Vec3 pretransform(const Vec3 &v) const + { + return static_cast< Vec3 >(*this * v); + } + + /// Transform a Vec3 by post-multiplication, doing homogenous divison. + template + Vec3 transformH(const Vec3 &p) const + { + T0 w; + + // w = p * (*this).col(3); + w = static_cast(p[0] * MyBase::mm[ 3] + p[1] * MyBase::mm[ 7] + + p[2] * MyBase::mm[11] + MyBase::mm[15]); + + if ( !isExactlyEqual(w , 0.0) ) { + return Vec3(static_cast((p[0] * MyBase::mm[ 0] + p[1] * MyBase::mm[ 4] + + p[2] * MyBase::mm[ 8] + MyBase::mm[12]) / w), + static_cast((p[0] * MyBase::mm[ 1] + p[1] * MyBase::mm[ 5] + + p[2] * MyBase::mm[ 9] + MyBase::mm[13]) / w), + static_cast((p[0] * MyBase::mm[ 2] + p[1] * MyBase::mm[ 6] + + p[2] * MyBase::mm[10] + MyBase::mm[14]) / w)); + } + + return Vec3(0, 0, 0); + } + + /// Transform a Vec3 by pre-multiplication, doing homogenous division. + template + Vec3 pretransformH(const Vec3 &p) const + { + T0 w; + + // w = p * (*this).col(3); + w = p[0] * MyBase::mm[12] + p[1] * MyBase::mm[13] + p[2] * MyBase::mm[14] + MyBase::mm[15]; + + if ( !isExactlyEqual(w , 0.0) ) { + return Vec3(static_cast((p[0] * MyBase::mm[ 0] + p[1] * MyBase::mm[ 1] + + p[2] * MyBase::mm[ 2] + MyBase::mm[ 3]) / w), + static_cast((p[0] * MyBase::mm[ 4] + p[1] * MyBase::mm[ 5] + + p[2] * MyBase::mm[ 6] + MyBase::mm[ 7]) / w), + static_cast((p[0] * MyBase::mm[ 8] + p[1] * MyBase::mm[ 9] + + p[2] * MyBase::mm[10] + MyBase::mm[11]) / w)); + } + + return Vec3(0, 0, 0); + } + + /// Transform a Vec3 by post-multiplication, without translation. + template + Vec3 transform3x3(const Vec3 &v) const + { + return Vec3( + static_cast(v[0] * MyBase::mm[ 0] + v[1] * MyBase::mm[ 4] + v[2] * MyBase::mm[ 8]), + static_cast(v[0] * MyBase::mm[ 1] + v[1] * MyBase::mm[ 5] + v[2] * MyBase::mm[ 9]), + static_cast(v[0] * MyBase::mm[ 2] + v[1] * MyBase::mm[ 6] + v[2] * MyBase::mm[10])); + } + + +private: + bool invert(Mat4 &inverse, T tolerance) const; + + T det2(const Mat4 &a, int i0, int i1, int j0, int j1) const { + int i0row = i0 * 4; + int i1row = i1 * 4; + return a.mm[i0row+j0]*a.mm[i1row+j1] - a.mm[i0row+j1]*a.mm[i1row+j0]; + } + + T det3(const Mat4 &a, int i0, int i1, int i2, + int j0, int j1, int j2) const { + int i0row = i0 * 4; + return a.mm[i0row+j0]*det2(a, i1,i2, j1,j2) + + a.mm[i0row+j1]*det2(a, i1,i2, j2,j0) + + a.mm[i0row+j2]*det2(a, i1,i2, j0,j1); + } +}; // class Mat4 + + +/// @relates Mat4 +/// @brief Equality operator, does exact floating point comparisons +template +bool operator==(const Mat4 &m0, const Mat4 &m1) +{ + const T0 *t0 = m0.asPointer(); + const T1 *t1 = m1.asPointer(); + + for (int i=0; i<16; ++i) if (!isExactlyEqual(t0[i], t1[i])) return false; + return true; +} + +/// @relates Mat4 +/// @brief Inequality operator, does exact floating point comparisons +template +bool operator!=(const Mat4 &m0, const Mat4 &m1) { return !(m0 == m1); } + +/// @relates Mat4 +/// @brief Multiply each element of the given matrix by @a scalar and return the result. +template +Mat4::type> operator*(S scalar, const Mat4 &m) +{ + return m*scalar; +} + +/// @relates Mat4 +/// @brief Multiply each element of the given matrix by @a scalar and return the result. +template +Mat4::type> operator*(const Mat4 &m, S scalar) +{ + Mat4::type> result(m); + result *= scalar; + return result; +} + +/// @relates Mat4 +/// @brief Multiply @a _m by @a _v and return the resulting vector. +template +inline Vec4::type> +operator*(const Mat4 &_m, + const Vec4 &_v) +{ + MT const *m = _m.asPointer(); + return Vec4::type>( + _v[0]*m[0] + _v[1]*m[1] + _v[2]*m[2] + _v[3]*m[3], + _v[0]*m[4] + _v[1]*m[5] + _v[2]*m[6] + _v[3]*m[7], + _v[0]*m[8] + _v[1]*m[9] + _v[2]*m[10] + _v[3]*m[11], + _v[0]*m[12] + _v[1]*m[13] + _v[2]*m[14] + _v[3]*m[15]); +} + +/// @relates Mat4 +/// @brief Multiply @a _v by @a _m and return the resulting vector. +template +inline Vec4::type> +operator*(const Vec4 &_v, + const Mat4 &_m) +{ + MT const *m = _m.asPointer(); + return Vec4::type>( + _v[0]*m[0] + _v[1]*m[4] + _v[2]*m[8] + _v[3]*m[12], + _v[0]*m[1] + _v[1]*m[5] + _v[2]*m[9] + _v[3]*m[13], + _v[0]*m[2] + _v[1]*m[6] + _v[2]*m[10] + _v[3]*m[14], + _v[0]*m[3] + _v[1]*m[7] + _v[2]*m[11] + _v[3]*m[15]); +} + +/// @relates Mat4 +/// @brief Multiply @a _m by @a _v and return the resulting vector. +template +inline Vec3::type> +operator*(const Mat4 &_m, const Vec3 &_v) +{ + MT const *m = _m.asPointer(); + return Vec3::type>( + _v[0]*m[0] + _v[1]*m[1] + _v[2]*m[2] + m[3], + _v[0]*m[4] + _v[1]*m[5] + _v[2]*m[6] + m[7], + _v[0]*m[8] + _v[1]*m[9] + _v[2]*m[10] + m[11]); +} + +/// @relates Mat4 +/// @brief Multiply @a _v by @a _m and return the resulting vector. +template +inline Vec3::type> +operator*(const Vec3 &_v, const Mat4 &_m) +{ + MT const *m = _m.asPointer(); + return Vec3::type>( + _v[0]*m[0] + _v[1]*m[4] + _v[2]*m[8] + m[12], + _v[0]*m[1] + _v[1]*m[5] + _v[2]*m[9] + m[13], + _v[0]*m[2] + _v[1]*m[6] + _v[2]*m[10] + m[14]); +} + +/// @relates Mat4 +/// @brief Add corresponding elements of @a m0 and @a m1 and return the result. +template +Mat4::type> +operator+(const Mat4 &m0, const Mat4 &m1) +{ + Mat4::type> result(m0); + result += m1; + return result; +} + +/// @relates Mat4 +/// @brief Subtract corresponding elements of @a m0 and @a m1 and return the result. +template +Mat4::type> +operator-(const Mat4 &m0, const Mat4 &m1) +{ + Mat4::type> result(m0); + result -= m1; + return result; +} + +/// @relates Mat4 +/// @brief Multiply @a m0 by @a m1 and return the resulting matrix. +template +Mat4::type> +operator*(const Mat4 &m0, const Mat4 &m1) +{ + Mat4::type> result(m0); + result *= m1; + return result; +} + + +/// Transform a Vec3 by pre-multiplication, without translation. +/// Presumes this matrix is inverse of coordinate transform +/// Synonymous to "pretransform3x3" +template +Vec3 transformNormal(const Mat4 &m, const Vec3 &n) +{ + return Vec3( + static_cast(m[0][0]*n[0] + m[0][1]*n[1] + m[0][2]*n[2]), + static_cast(m[1][0]*n[0] + m[1][1]*n[1] + m[1][2]*n[2]), + static_cast(m[2][0]*n[0] + m[2][1]*n[1] + m[2][2]*n[2])); +} + + +/// Invert via gauss-jordan elimination. Modified from dreamworks internal mx library +template +bool Mat4::invert(Mat4 &inverse, T tolerance) const +{ + Mat4 temp(*this); + inverse.setIdentity(); + + // Forward elimination step + double det = 1.0; + for (int i = 0; i < 4; ++i) { + int row = i; + double max = fabs(temp[i][i]); + + for (int k = i+1; k < 4; ++k) { + if (fabs(temp[k][i]) > max) { + row = k; + max = fabs(temp[k][i]); + } + } + + if (isExactlyEqual(max, 0.0)) return false; + + // must move pivot to row i + if (row != i) { + det = -det; + for (int k = 0; k < 4; ++k) { + std::swap(temp[row][k], temp[i][k]); + std::swap(inverse[row][k], inverse[i][k]); + } + } + + double pivot = temp[i][i]; + det *= pivot; + + // scale row i + for (int k = 0; k < 4; ++k) { + temp[i][k] /= pivot; + inverse[i][k] /= pivot; + } + + // eliminate in rows below i + for (int j = i+1; j < 4; ++j) { + double t = temp[j][i]; + if (!isExactlyEqual(t, 0.0)) { + // subtract scaled row i from row j + for (int k = 0; k < 4; ++k) { + temp[j][k] -= temp[i][k] * t; + inverse[j][k] -= inverse[i][k] * t; + } + } + } + } + + // Backward elimination step + for (int i = 3; i > 0; --i) { + for (int j = 0; j < i; ++j) { + double t = temp[j][i]; + + if (!isExactlyEqual(t, 0.0)) { + for (int k = 0; k < 4; ++k) { + inverse[j][k] -= inverse[i][k]*t; + } + } + } + } + return det*det >= tolerance*tolerance; +} + +template +inline bool isAffine(const Mat4& m) { + return (m.col(3) == Vec4(0, 0, 0, 1)); +} + +template +inline bool hasTranslation(const Mat4& m) { + return (m.row(3) != Vec4(0, 0, 0, 1)); +} + + +using Mat4s = Mat4; +using Mat4d = Mat4; +using Mat4f = Mat4d; + +} // namespace math + + +template<> inline math::Mat4s zeroVal() { return math::Mat4s::zero(); } +template<> inline math::Mat4d zeroVal() { return math::Mat4d::zero(); } + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_MAT4_H_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Math.h b/openvdb/math/Math.h new file mode 100644 index 00000000..d59e0a14 --- /dev/null +++ b/openvdb/math/Math.h @@ -0,0 +1,913 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Math.h +/// @brief General-purpose arithmetic and comparison routines, most of which +/// accept arbitrary value types (or at least arbitrary numeric value types) + +#ifndef OPENVDB_MATH_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_HAS_BEEN_INCLUDED + +#include +#include +#include +#include // for std::max() +#include +#include // for std::ceil(), std::fabs(), std::pow(), std::sqrt(), etc. +#include // for abs(int) +#include +#include +#include // for std::is_arithmetic + + +// Compile pragmas + +// Intel(r) compiler fires remark #1572: floating-point equality and inequality +// comparisons are unrealiable when == or != is used with floating point operands. +#if defined(__INTEL_COMPILER) + #define OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN \ + _Pragma("warning (push)") \ + _Pragma("warning (disable:1572)") + #define OPENVDB_NO_FP_EQUALITY_WARNING_END \ + _Pragma("warning (pop)") +#elif defined(__clang__) + #define OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN \ + PRAGMA(clang diagnostic push) \ + PRAGMA(clang diagnostic ignored "-Wfloat-equal") + #define OPENVDB_NO_FP_EQUALITY_WARNING_END \ + PRAGMA(clang diagnostic pop) +#else + // For GCC, #pragma GCC diagnostic ignored "-Wfloat-equal" + // isn't working until gcc 4.2+, + // Trying + // #pragma GCC system_header + // creates other problems, most notably "warning: will never be executed" + // in from templates, unsure of how to work around. + // If necessary, could use integer based comparisons for equality + #define OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + #define OPENVDB_NO_FP_EQUALITY_WARNING_END +#endif + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +/// @brief Return the value of type T that corresponds to zero. +/// @note A zeroVal() specialization must be defined for each @c ValueType T +/// that cannot be constructed using the form @c T(0). For example, @c std::string(0) +/// treats 0 as @c nullptr and throws a @c std::logic_error. +template inline T zeroVal() { return T(0); } +/// Return the @c std::string value that corresponds to zero. +template<> inline std::string zeroVal() { return ""; } +/// Return the @c bool value that corresponds to zero. +template<> inline bool zeroVal() { return false; } + +/// @todo These won't be needed if we eliminate StringGrids. +//@{ +/// @brief Needed to support the (zeroVal() + val) idiom +/// when @c ValueType is @c std::string +inline std::string operator+(const std::string& s, bool) { return s; } +inline std::string operator+(const std::string& s, int) { return s; } +inline std::string operator+(const std::string& s, float) { return s; } +inline std::string operator+(const std::string& s, double) { return s; } +//@} + + +namespace math { + +/// @brief Return the unary negation of the given value. +/// @note A negative() specialization must be defined for each ValueType T +/// for which unary negation is not defined. +template inline T negative(const T& val) { return T(-val); } +/// Return the negation of the given boolean. +template<> inline bool negative(const bool& val) { return !val; } +/// Return the "negation" of the given string. +template<> inline std::string negative(const std::string& val) { return val; } + + +//@{ +/// Tolerance for floating-point comparison +template struct Tolerance { static T value() { return zeroVal(); } }; +template<> struct Tolerance { static float value() { return 1e-8f; } }; +template<> struct Tolerance { static double value() { return 1e-15; } }; +//@} + +//@{ +/// Delta for small floating-point offsets +template struct Delta { static T value() { return zeroVal(); } }; +template<> struct Delta { static float value() { return 1e-5f; } }; +template<> struct Delta { static double value() { return 1e-9; } }; +//@} + + +// ==========> Random Values <================== + +/// @brief Simple generator of random numbers over the range [0, 1) +/// @details Thread-safe as long as each thread has its own Rand01 instance +template +class Rand01 +{ +private: + EngineType mEngine; + std::uniform_real_distribution mRand; + +public: + using ValueType = FloatType; + + /// @brief Initialize the generator. + /// @param engine random number generator + Rand01(const EngineType& engine): mEngine(engine) {} + + /// @brief Initialize the generator. + /// @param seed seed value for the random number generator + Rand01(unsigned int seed): mEngine(static_cast(seed)) {} + + /// Set the seed value for the random number generator + void setSeed(unsigned int seed) + { + mEngine.seed(static_cast(seed)); + } + + /// Return a const reference to the random number generator. + const EngineType& engine() const { return mEngine; } + + /// Return a uniformly distributed random number in the range [0, 1). + FloatType operator()() { return mRand(mEngine); } +}; + +using Random01 = Rand01; + + +/// @brief Simple random integer generator +/// @details Thread-safe as long as each thread has its own RandInt instance +template +class RandInt +{ +private: + using Distr = std::uniform_int_distribution; + EngineType mEngine; + Distr mRand; + +public: + /// @brief Initialize the generator. + /// @param engine random number generator + /// @param imin,imax generate integers that are uniformly distributed over [imin, imax] + RandInt(const EngineType& engine, IntType imin, IntType imax): + mEngine(engine), + mRand(std::min(imin, imax), std::max(imin, imax)) + {} + + /// @brief Initialize the generator. + /// @param seed seed value for the random number generator + /// @param imin,imax generate integers that are uniformly distributed over [imin, imax] + RandInt(unsigned int seed, IntType imin, IntType imax): + mEngine(static_cast(seed)), + mRand(std::min(imin, imax), std::max(imin, imax)) + {} + + /// Change the range over which integers are distributed to [imin, imax]. + void setRange(IntType imin, IntType imax) + { + mRand = Distr(std::min(imin, imax), std::max(imin, imax)); + } + + /// Set the seed value for the random number generator + void setSeed(unsigned int seed) + { + mEngine.seed(static_cast(seed)); + } + + /// Return a const reference to the random number generator. + const EngineType& engine() const { return mEngine; } + + /// Return a randomly-generated integer in the current range. + IntType operator()() { return mRand(mEngine); } + + /// @brief Return a randomly-generated integer in the new range [imin, imax], + /// without changing the current range. + IntType operator()(IntType imin, IntType imax) + { + const IntType lo = std::min(imin, imax), hi = std::max(imin, imax); + return mRand(mEngine, typename Distr::param_type(lo, hi)); + } +}; + +using RandomInt = RandInt; + + +// ==========> Clamp <================== + +/// Return @a x clamped to [@a min, @a max] +template +inline Type +Clamp(Type x, Type min, Type max) +{ + assert( !(min>max) ); + return x > min ? x < max ? x : max : min; +} + + +/// Return @a x clamped to [0, 1] +template +inline Type +Clamp01(Type x) { return x > Type(0) ? x < Type(1) ? x : Type(1) : Type(0); } + + +/// Return @c true if @a x is outside [0,1] +template +inline bool +ClampTest01(Type &x) +{ + if (x >= Type(0) && x <= Type(1)) return false; + x = x < Type(0) ? Type(0) : Type(1); + return true; +} + +/// @brief Return 0 if @a x < @a 0, 1 if @a x > 1 or else (3 − 2 @a x) @a x². +template +inline Type +SmoothUnitStep(Type x) +{ + return x > 0 ? x < 1 ? (3-2*x)*x*x : Type(1) : Type(0); +} + +/// @brief Return 0 if @a x < @a min, 1 if @a x > @a max or else (3 − 2 @a t) @a t², +/// where @a t = (@a x − @a min)/(@a max − @a min). +template +inline Type +SmoothUnitStep(Type x, Type min, Type max) +{ + assert(min < max); + return SmoothUnitStep((x-min)/(max-min)); +} + + +// ==========> Absolute Value <================== + + +//@{ +/// Return the absolute value of the given quantity. +inline int32_t Abs(int32_t i) { return abs(i); } +inline int64_t Abs(int64_t i) +{ +#ifdef _MSC_VER + return (i < int64_t(0) ? -i : i); +#else + return labs(i); +#endif +} +inline float Abs(float x) { return std::fabs(x); } +inline double Abs(double x) { return std::fabs(x); } +inline long double Abs(long double x) { return std::fabs(x); } +inline uint32_t Abs(uint32_t i) { return i; } +inline uint64_t Abs(uint64_t i) { return i; } +inline bool Abs(bool b) { return b; } +// On OSX size_t and uint64_t are different types +#if defined(__APPLE__) || defined(MACOSX) +inline size_t Abs(size_t i) { return i; } +#endif +//@} + + +//////////////////////////////////////// + + +// ==========> Value Comparison <================== + + +/// Return @c true if @a x is exactly equal to zero. +template +inline bool +isZero(const Type& x) +{ + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + return x == zeroVal(); + OPENVDB_NO_FP_EQUALITY_WARNING_END +} + + +/// @brief Return @c true if @a x is equal to zero to within +/// the default floating-point comparison tolerance. +template +inline bool +isApproxZero(const Type& x) +{ + const Type tolerance = Type(zeroVal() + Tolerance::value()); + return !(x > tolerance) && !(x < -tolerance); +} + +/// Return @c true if @a x is equal to zero to within the given tolerance. +template +inline bool +isApproxZero(const Type& x, const Type& tolerance) +{ + return !(x > tolerance) && !(x < -tolerance); +} + + +/// Return @c true if @a x is less than zero. +template +inline bool +isNegative(const Type& x) { return x < zeroVal(); } + +// Return false, since bool values are never less than zero. +template<> inline bool isNegative(const bool&) { return false; } + + +/// Return @c true if @a x is finite. +inline bool +isFinite(const float x) { return std::isfinite(x); } + +/// Return @c true if @a x is finite. +template::value, int>::type = 0> +inline bool +isFinite(const Type& x) { return std::isfinite(static_cast(x)); } + + +/// Return @c true if @a x is an infinity value (either positive infinity or negative infinity). +inline bool +isInfinite(const float x) { return std::isinf(x); } + +/// Return @c true if @a x is an infinity value (either positive infinity or negative infinity). +template::value, int>::type = 0> +inline bool +isInfinite(const Type& x) { return std::isinf(static_cast(x)); } + + +/// Return @c true if @a x is a NaN (Not-A-Number) value. +inline bool +isNan(const float x) { return std::isnan(x); } + +/// Return @c true if @a x is a NaN (Not-A-Number) value. +template::value, int>::type = 0> +inline bool +isNan(const Type& x) { return std::isnan(static_cast(x)); } + + +/// @brief Return @c true if @a a is equal to @a b to within +/// the default floating-point comparison tolerance. +template +inline bool +isApproxEqual(const Type& a, const Type& b) +{ + const Type tolerance = Type(zeroVal() + Tolerance::value()); + return !(Abs(a - b) > tolerance); +} + + +/// Return @c true if @a a is equal to @a b to within the given tolerance. +template +inline bool +isApproxEqual(const Type& a, const Type& b, const Type& tolerance) +{ + return !(Abs(a - b) > tolerance); +} + +#define OPENVDB_EXACT_IS_APPROX_EQUAL(T) \ + template<> inline bool isApproxEqual(const T& a, const T& b) { return a == b; } \ + template<> inline bool isApproxEqual(const T& a, const T& b, const T&) { return a == b; } \ + /**/ + +OPENVDB_EXACT_IS_APPROX_EQUAL(bool) +OPENVDB_EXACT_IS_APPROX_EQUAL(std::string) + + +/// @brief Return @c true if @a a is larger than @a b to within +/// the given tolerance, i.e., if @a b - @a a < @a tolerance. +template +inline bool +isApproxLarger(const Type& a, const Type& b, const Type& tolerance) +{ + return (b - a < tolerance); +} + + +/// @brief Return @c true if @a a is exactly equal to @a b. +template +inline bool +isExactlyEqual(const T0& a, const T1& b) +{ + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + return a == b; + OPENVDB_NO_FP_EQUALITY_WARNING_END +} + + +template +inline bool +isRelOrApproxEqual(const Type& a, const Type& b, const Type& absTol, const Type& relTol) +{ + // First check to see if we are inside the absolute tolerance + // Necessary for numbers close to 0 + if (!(Abs(a - b) > absTol)) return true; + + // Next check to see if we are inside the relative tolerance + // to handle large numbers that aren't within the abs tolerance + // but could be the closest floating point representation + double relError; + if (Abs(b) > Abs(a)) { + relError = Abs((a - b) / b); + } else { + relError = Abs((a - b) / a); + } + return (relError <= relTol); +} + +template<> +inline bool +isRelOrApproxEqual(const bool& a, const bool& b, const bool&, const bool&) +{ + return (a == b); +} + + +// Avoid strict aliasing issues by using type punning +// http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html +// Using "casting through a union(2)" +inline int32_t +floatToInt32(const float aFloatValue) +{ + union FloatOrInt32 { float floatValue; int32_t int32Value; }; + const FloatOrInt32* foi = reinterpret_cast(&aFloatValue); + return foi->int32Value; +} + + +inline int64_t +doubleToInt64(const double aDoubleValue) +{ + union DoubleOrInt64 { double doubleValue; int64_t int64Value; }; + const DoubleOrInt64* dol = reinterpret_cast(&aDoubleValue); + return dol->int64Value; +} + + +// aUnitsInLastPlace is the allowed difference between the least significant digits +// of the numbers' floating point representation +// Please read the reference paper before trying to use isUlpsEqual +// http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm +inline bool +isUlpsEqual(const double aLeft, const double aRight, const int64_t aUnitsInLastPlace) +{ + int64_t longLeft = doubleToInt64(aLeft); + // Because of 2's complement, must restore lexicographical order + if (longLeft < 0) { + longLeft = INT64_C(0x8000000000000000) - longLeft; + } + + int64_t longRight = doubleToInt64(aRight); + // Because of 2's complement, must restore lexicographical order + if (longRight < 0) { + longRight = INT64_C(0x8000000000000000) - longRight; + } + + int64_t difference = labs(longLeft - longRight); + return (difference <= aUnitsInLastPlace); +} + +inline bool +isUlpsEqual(const float aLeft, const float aRight, const int32_t aUnitsInLastPlace) +{ + int32_t intLeft = floatToInt32(aLeft); + // Because of 2's complement, must restore lexicographical order + if (intLeft < 0) { + intLeft = 0x80000000 - intLeft; + } + + int32_t intRight = floatToInt32(aRight); + // Because of 2's complement, must restore lexicographical order + if (intRight < 0) { + intRight = 0x80000000 - intRight; + } + + int32_t difference = abs(intLeft - intRight); + return (difference <= aUnitsInLastPlace); +} + + +//////////////////////////////////////// + + +// ==========> Pow <================== + +/// Return @a x2. +template +inline Type Pow2(Type x) { return x*x; } + +/// Return @a x3. +template +inline Type Pow3(Type x) { return x*x*x; } + +/// Return @a x4. +template +inline Type Pow4(Type x) { return Pow2(Pow2(x)); } + +/// Return @a x@a n. +template +Type +Pow(Type x, int n) +{ + Type ans = 1; + if (n < 0) { + n = -n; + x = Type(1)/x; + } + while (n--) ans *= x; + return ans; +} + +//@{ +/// Return @a b@a e. +inline float +Pow(float b, float e) +{ + assert( b >= 0.0f && "Pow(float,float): base is negative" ); + return powf(b,e); +} + +inline double +Pow(double b, double e) +{ + assert( b >= 0.0 && "Pow(double,double): base is negative" ); + return std::pow(b,e); +} +//@} + + +// ==========> Max <================== + +/// Return the maximum of two values +template +inline const Type& +Max(const Type& a, const Type& b) +{ + return std::max(a,b); +} + +/// Return the maximum of three values +template +inline const Type& +Max(const Type& a, const Type& b, const Type& c) +{ + return std::max(std::max(a,b), c); +} + +/// Return the maximum of four values +template +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d) +{ + return std::max(std::max(a,b), std::max(c,d)); +} + +/// Return the maximum of five values +template +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e) +{ + return std::max(std::max(a,b), Max(c,d,e)); +} + +/// Return the maximum of six values +template +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f) +{ + return std::max(Max(a,b,c), Max(d,e,f)); +} + +/// Return the maximum of seven values +template +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d, + const Type& e, const Type& f, const Type& g) +{ + return std::max(Max(a,b,c,d), Max(e,f,g)); +} + +/// Return the maximum of eight values +template +inline const Type& +Max(const Type& a, const Type& b, const Type& c, const Type& d, + const Type& e, const Type& f, const Type& g, const Type& h) +{ + return std::max(Max(a,b,c,d), Max(e,f,g,h)); +} + + +// ==========> Min <================== + +/// Return the minimum of two values +template +inline const Type& +Min(const Type& a, const Type& b) { return std::min(a, b); } + +/// Return the minimum of three values +template +inline const Type& +Min(const Type& a, const Type& b, const Type& c) { return std::min(std::min(a, b), c); } + +/// Return the minimum of four values +template +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d) +{ + return std::min(std::min(a, b), std::min(c, d)); +} + +/// Return the minimum of five values +template +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e) +{ + return std::min(std::min(a,b), Min(c,d,e)); +} + +/// Return the minimum of six values +template +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f) +{ + return std::min(Min(a,b,c), Min(d,e,f)); +} + +/// Return the minimum of seven values +template +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d, + const Type& e, const Type& f, const Type& g) +{ + return std::min(Min(a,b,c,d), Min(e,f,g)); +} + +/// Return the minimum of eight values +template +inline const Type& +Min(const Type& a, const Type& b, const Type& c, const Type& d, + const Type& e, const Type& f, const Type& g, const Type& h) +{ + return std::min(Min(a,b,c,d), Min(e,f,g,h)); +} + + +// ============> Exp <================== + +/// Return @a e@a x. +template +inline Type Exp(const Type& x) { return std::exp(x); } + +// ============> Sin <================== + +//@{ +/// Return sin @a x. +inline float Sin(const float& x) { return std::sin(x); } + +inline double Sin(const double& x) { return std::sin(x); } +//@} + +// ============> Cos <================== + +//@{ +/// Return cos @a x. +inline float Cos(const float& x) { return std::cos(x); } + +inline double Cos(const double& x) { return std::cos(x); } +//@} + + +//////////////////////////////////////// + + +/// Return the sign of the given value as an integer (either -1, 0 or 1). +template +inline int Sign(const Type &x) { return (zeroVal() < x) - (x < zeroVal()); } + + +/// @brief Return @c true if @a a and @a b have different signs. +/// @note Zero is considered a positive number. +template +inline bool +SignChange(const Type& a, const Type& b) +{ + return ( (a()) ^ (b()) ); +} + + +/// @brief Return @c true if the interval [@a a, @a b] includes zero, +/// i.e., if either @a a or @a b is zero or if they have different signs. +template +inline bool +ZeroCrossing(const Type& a, const Type& b) +{ + return a * b <= zeroVal(); +} + + +//@{ +/// Return the square root of a floating-point value. +inline float Sqrt(float x) { return std::sqrt(x); } +inline double Sqrt(double x) { return std::sqrt(x); } +inline long double Sqrt(long double x) { return std::sqrt(x); } +//@} + + +//@{ +/// Return the cube root of a floating-point value. +inline float Cbrt(float x) { return std::cbrt(x); } +inline double Cbrt(double x) { return std::cbrt(x); } +inline long double Cbrt(long double x) { return std::cbrt(x); } +//@} + + +//@{ +/// Return the remainder of @a x / @a y. +inline int Mod(int x, int y) { return (x % y); } +inline float Mod(float x, float y) { return std::fmod(x, y); } +inline double Mod(double x, double y) { return std::fmod(x, y); } +inline long double Mod(long double x, long double y) { return std::fmod(x, y); } +template inline Type Remainder(Type x, Type y) { return Mod(x, y); } +//@} + + +//@{ +/// Return @a x rounded up to the nearest integer. +inline float RoundUp(float x) { return std::ceil(x); } +inline double RoundUp(double x) { return std::ceil(x); } +inline long double RoundUp(long double x) { return std::ceil(x); } +//@} +/// Return @a x rounded up to the nearest multiple of @a base. +template +inline Type +RoundUp(Type x, Type base) +{ + Type remainder = Remainder(x, base); + return remainder ? x-remainder+base : x; +} + + +//@{ +/// Return @a x rounded down to the nearest integer. +inline float RoundDown(float x) { return std::floor(x); } +inline double RoundDown(double x) { return std::floor(x); } +inline long double RoundDown(long double x) { return std::floor(x); } +//@} +/// Return @a x rounded down to the nearest multiple of @a base. +template +inline Type +RoundDown(Type x, Type base) +{ + Type remainder = Remainder(x, base); + return remainder ? x-remainder : x; +} + + +//@{ +/// Return @a x rounded to the nearest integer. +inline float Round(float x) { return RoundDown(x + 0.5f); } +inline double Round(double x) { return RoundDown(x + 0.5); } +inline long double Round(long double x) { return RoundDown(x + 0.5l); } +//@} + + +/// Return the euclidean remainder of @a x. +/// Note unlike % operator this will always return a positive result +template +inline Type +EuclideanRemainder(Type x) { return x - RoundDown(x); } + + +/// Return the integer part of @a x. +template +inline Type +IntegerPart(Type x) +{ + return (x > 0 ? RoundDown(x) : RoundUp(x)); +} + +/// Return the fractional part of @a x. +template +inline Type +FractionalPart(Type x) { return Mod(x,Type(1)); } + + +//@{ +/// Return the floor of @a x. +inline int Floor(float x) { return int(RoundDown(x)); } +inline int Floor(double x) { return int(RoundDown(x)); } +inline int Floor(long double x) { return int(RoundDown(x)); } +//@} + + +//@{ +/// Return the ceiling of @a x. +inline int Ceil(float x) { return int(RoundUp(x)); } +inline int Ceil(double x) { return int(RoundUp(x)); } +inline int Ceil(long double x) { return int(RoundUp(x)); } +//@} + + +/// Return @a x if it is greater or equal in magnitude than @a delta. Otherwise, return zero. +template +inline Type Chop(Type x, Type delta) { return (Abs(x) < delta ? zeroVal() : x); } + + +/// Return @a x truncated to the given number of decimal digits. +template +inline Type +Truncate(Type x, unsigned int digits) +{ + Type tenth = Pow(10,digits); + return RoundDown(x*tenth+0.5)/tenth; +} + + +//////////////////////////////////////// + + +/// @brief 8-bit integer values print to std::ostreams as characters. +/// Cast them so that they print as integers instead. +template +inline auto PrintCast(const T& val) -> typename std::enable_if::value + && !std::is_same::value, const T&>::type { return val; } +inline int32_t PrintCast(int8_t val) { return int32_t(val); } +inline uint32_t PrintCast(uint8_t val) { return uint32_t(val); } + + +//////////////////////////////////////// + + +/// Return the inverse of @a x. +template +inline Type +Inv(Type x) +{ + assert(x); + return Type(1)/x; +} + + +enum Axis { + X_AXIS = 0, + Y_AXIS = 1, + Z_AXIS = 2 +}; + +// enum values are consistent with their historical mx analogs. +enum RotationOrder { + XYZ_ROTATION = 0, + XZY_ROTATION, + YXZ_ROTATION, + YZX_ROTATION, + ZXY_ROTATION, + ZYX_ROTATION, + XZX_ROTATION, + ZXZ_ROTATION +}; + + +template +struct promote { + using type = typename boost::numeric::conversion_traits::supertype; +}; + + +/// @brief Return the index [0,1,2] of the smallest value in a 3D vector. +/// @note This methods assumes operator[] exists and avoids branching. +/// @details If two components of the input vector are equal and smaller than the +/// third component, the largest index of the two is always returned. +/// If all three vector components are equal the largest index, i.e. 2, is +/// returned. In other words the return value corresponds to the largest index +/// of the of the smallest vector components. +template +size_t +MinIndex(const Vec3T& v) +{ + static const size_t hashTable[8] = { 2, 1, 9, 1, 2, 9, 0, 0 };//9 is a dummy value + const size_t hashKey = + ((v[0] < v[1]) << 2) + ((v[0] < v[2]) << 1) + (v[1] < v[2]);// ?*4+?*2+?*1 + return hashTable[hashKey]; +} + + +/// @brief Return the index [0,1,2] of the largest value in a 3D vector. +/// @note This methods assumes operator[] exists and avoids branching. +/// @details If two components of the input vector are equal and larger than the +/// third component, the largest index of the two is always returned. +/// If all three vector components are equal the largest index, i.e. 2, is +/// returned. In other words the return value corresponds to the largest index +/// of the largest vector components. +template +size_t +MaxIndex(const Vec3T& v) +{ + static const size_t hashTable[8] = { 2, 1, 9, 1, 2, 9, 0, 0 };//9 is a dummy value + const size_t hashKey = + ((v[0] > v[1]) << 2) + ((v[0] > v[2]) << 1) + (v[1] > v[2]);// ?*4+?*2+?*1 + return hashTable[hashKey]; +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_MATH_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Operators.h b/openvdb/math/Operators.h new file mode 100644 index 00000000..74f1ff63 --- /dev/null +++ b/openvdb/math/Operators.h @@ -0,0 +1,2126 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file math/Operators.h + +#ifndef OPENVDB_MATH_OPERATORS_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_OPERATORS_HAS_BEEN_INCLUDED + +#include "FiniteDifference.h" +#include "Stencils.h" +#include "Maps.h" +#include "Transform.h" +#include // for std::sqrt() + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +// Simple tools to help determine when type conversions are needed +template struct is_vec3d { static const bool value = false; }; +template<> struct is_vec3d { static const bool value = true; }; + +template struct is_double { static const bool value = false; }; +template<> struct is_double { static const bool value = true; }; + + +/// @brief Adapter to associate a map with a world-space operator, +/// giving it the same call signature as an index-space operator +/// @todo For now, the operator's result type must be specified explicitly, +/// but eventually it should be possible, via traits, to derive the result type +/// from the operator type. +template +struct MapAdapter { + MapAdapter(const MapType& m): map(m) {} + + template + inline ResultType + result(const AccessorType& grid, const Coord& ijk) { return OpType::result(map, grid, ijk); } + + template + inline ResultType + result(const StencilType& stencil) { return OpType::result(map, stencil); } + + const MapType map; +}; + + +/// Adapter for vector-valued index-space operators to return the vector magnitude +template +struct ISOpMagnitude { + template + static inline double result(const AccessorType& grid, const Coord& ijk) { + return double(OpType::result(grid, ijk).length()); + } + + template + static inline double result(const StencilType& stencil) { + return double(OpType::result(stencil).length()); + } +}; + +/// Adapter for vector-valued world-space operators to return the vector magnitude +template +struct OpMagnitude { + template + static inline double result(const MapT& map, const AccessorType& grid, const Coord& ijk) { + return double(OpType::result(map, grid, ijk).length()); + } + + template + static inline double result(const MapT& map, const StencilType& stencil) { + return double(OpType::result(map, stencil).length()); + } +}; + + +namespace internal { + +// This additional layer is necessary for Visual C++ to compile. +template +struct ReturnValue { + using ValueType = typename T::ValueType; + using Vec3Type = math::Vec3; +}; + +} // namespace internal + +// ---- Operators defined in index space + + +//@{ +/// @brief Gradient operators defined in index space of various orders +template +struct ISGradient +{ + // random access version + template static Vec3 + result(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + using Vec3Type = Vec3; + return Vec3Type( D1::inX(grid, ijk), + D1::inY(grid, ijk), + D1::inZ(grid, ijk) ); + } + + // stencil access version + template static Vec3 + result(const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + using Vec3Type = Vec3; + return Vec3Type( D1::inX(stencil), + D1::inY(stencil), + D1::inZ(stencil) ); + } +}; +//@} + +/// struct that relates the BiasedGradientScheme to the +/// forward and backward difference methods used, as well as to +/// the correct stencil type for index space use +template +struct BIAS_SCHEME { + static const DScheme FD = FD_1ST; + static const DScheme BD = BD_1ST; + + template + struct ISStencil { + using StencilType = SevenPointStencil; + }; +}; + +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_1ST; + static const DScheme BD = BD_1ST; + + template + struct ISStencil { + using StencilType = SevenPointStencil; + }; +}; + +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_2ND; + static const DScheme BD = BD_2ND; + + template + struct ISStencil { + using StencilType = ThirteenPointStencil; + }; +}; +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_3RD; + static const DScheme BD = BD_3RD; + + template + struct ISStencil { + using StencilType = NineteenPointStencil; + }; +}; +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_WENO5; + static const DScheme BD = BD_WENO5; + + template + struct ISStencil { + using StencilType = NineteenPointStencil; + }; +}; +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_HJWENO5; + static const DScheme BD = BD_HJWENO5; + + template + struct ISStencil { + using StencilType = NineteenPointStencil; + }; +}; + + +//@{ +/// @brief Biased Gradient Operators, using upwinding defined by the @c Vec3Bias input + +template +struct ISGradientBiased +{ + static const DScheme FD = BIAS_SCHEME::FD; + static const DScheme BD = BIAS_SCHEME::BD; + + // random access version + template + static Vec3 + result(const Accessor& grid, const Coord& ijk, const Vec3Bias& V) + { + using ValueType = typename Accessor::ValueType; + using Vec3Type = Vec3; + + return Vec3Type(V[0]<0 ? D1::inX(grid,ijk) : D1::inX(grid,ijk), + V[1]<0 ? D1::inY(grid,ijk) : D1::inY(grid,ijk), + V[2]<0 ? D1::inZ(grid,ijk) : D1::inZ(grid,ijk) ); + } + + // stencil access version + template + static Vec3 + result(const StencilT& stencil, const Vec3Bias& V) + { + using ValueType = typename StencilT::ValueType; + using Vec3Type = Vec3; + + return Vec3Type(V[0]<0 ? D1::inX(stencil) : D1::inX(stencil), + V[1]<0 ? D1::inY(stencil) : D1::inY(stencil), + V[2]<0 ? D1::inZ(stencil) : D1::inZ(stencil) ); + } +}; + + +template +struct ISGradientNormSqrd +{ + static const DScheme FD = BIAS_SCHEME::FD; + static const DScheme BD = BIAS_SCHEME::BD; + + + // random access version + template + static typename Accessor::ValueType + result(const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + using Vec3Type = math::Vec3; + + Vec3Type up = ISGradient::result(grid, ijk); + Vec3Type down = ISGradient::result(grid, ijk); + return math::GodunovsNormSqrd(grid.getValue(ijk)>0, down, up); + } + + // stencil access version + template + static typename StencilT::ValueType + result(const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + using Vec3Type = math::Vec3; + + Vec3Type up = ISGradient::result(stencil); + Vec3Type down = ISGradient::result(stencil); + return math::GodunovsNormSqrd(stencil.template getValue<0, 0, 0>()>0, down, up); + } +}; + +#ifdef DWA_OPENVDB // for SIMD - note will do the computations in float +template<> +struct ISGradientNormSqrd +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) + { + struct GetValue + { + const Accessor& acc; + GetValue(const Accessor& acc_): acc(acc_) {} + // Return the grid value at ijk converted to simd::Float4::value_type (= float). + inline simd::Float4::value_type operator()(const Coord& ijk_) { + return static_cast(acc.getValue(ijk_)); + } + } + valueAt(grid); + + // SSE optimized + const simd::Float4 + v1(valueAt(ijk.offsetBy(-2, 0, 0)) - valueAt(ijk.offsetBy(-3, 0, 0)), + valueAt(ijk.offsetBy( 0,-2, 0)) - valueAt(ijk.offsetBy( 0,-3, 0)), + valueAt(ijk.offsetBy( 0, 0,-2)) - valueAt(ijk.offsetBy( 0, 0,-3)), 0), + v2(valueAt(ijk.offsetBy(-1, 0, 0)) - valueAt(ijk.offsetBy(-2, 0, 0)), + valueAt(ijk.offsetBy( 0,-1, 0)) - valueAt(ijk.offsetBy( 0,-2, 0)), + valueAt(ijk.offsetBy( 0, 0,-1)) - valueAt(ijk.offsetBy( 0, 0,-2)), 0), + v3(valueAt(ijk ) - valueAt(ijk.offsetBy(-1, 0, 0)), + valueAt(ijk ) - valueAt(ijk.offsetBy( 0,-1, 0)), + valueAt(ijk ) - valueAt(ijk.offsetBy( 0, 0,-1)), 0), + v4(valueAt(ijk.offsetBy( 1, 0, 0)) - valueAt(ijk ), + valueAt(ijk.offsetBy( 0, 1, 0)) - valueAt(ijk ), + valueAt(ijk.offsetBy( 0, 0, 1)) - valueAt(ijk ), 0), + v5(valueAt(ijk.offsetBy( 2, 0, 0)) - valueAt(ijk.offsetBy( 1, 0, 0)), + valueAt(ijk.offsetBy( 0, 2, 0)) - valueAt(ijk.offsetBy( 0, 1, 0)), + valueAt(ijk.offsetBy( 0, 0, 2)) - valueAt(ijk.offsetBy( 0, 0, 1)), 0), + v6(valueAt(ijk.offsetBy( 3, 0, 0)) - valueAt(ijk.offsetBy( 2, 0, 0)), + valueAt(ijk.offsetBy( 0, 3, 0)) - valueAt(ijk.offsetBy( 0, 2, 0)), + valueAt(ijk.offsetBy( 0, 0, 3)) - valueAt(ijk.offsetBy( 0, 0, 2)), 0), + down = math::WENO5(v1, v2, v3, v4, v5), + up = math::WENO5(v6, v5, v4, v3, v2); + + return math::GodunovsNormSqrd(grid.getValue(ijk)>0, down, up); + } + + // stencil access version + template + static typename StencilT::ValueType result(const StencilT& s) + { + using F4Val = simd::Float4::value_type; + + // SSE optimized + const simd::Float4 + v1(F4Val(s.template getValue<-2, 0, 0>()) - F4Val(s.template getValue<-3, 0, 0>()), + F4Val(s.template getValue< 0,-2, 0>()) - F4Val(s.template getValue< 0,-3, 0>()), + F4Val(s.template getValue< 0, 0,-2>()) - F4Val(s.template getValue< 0, 0,-3>()), 0), + v2(F4Val(s.template getValue<-1, 0, 0>()) - F4Val(s.template getValue<-2, 0, 0>()), + F4Val(s.template getValue< 0,-1, 0>()) - F4Val(s.template getValue< 0,-2, 0>()), + F4Val(s.template getValue< 0, 0,-1>()) - F4Val(s.template getValue< 0, 0,-2>()), 0), + v3(F4Val(s.template getValue< 0, 0, 0>()) - F4Val(s.template getValue<-1, 0, 0>()), + F4Val(s.template getValue< 0, 0, 0>()) - F4Val(s.template getValue< 0,-1, 0>()), + F4Val(s.template getValue< 0, 0, 0>()) - F4Val(s.template getValue< 0, 0,-1>()), 0), + v4(F4Val(s.template getValue< 1, 0, 0>()) - F4Val(s.template getValue< 0, 0, 0>()), + F4Val(s.template getValue< 0, 1, 0>()) - F4Val(s.template getValue< 0, 0, 0>()), + F4Val(s.template getValue< 0, 0, 1>()) - F4Val(s.template getValue< 0, 0, 0>()), 0), + v5(F4Val(s.template getValue< 2, 0, 0>()) - F4Val(s.template getValue< 1, 0, 0>()), + F4Val(s.template getValue< 0, 2, 0>()) - F4Val(s.template getValue< 0, 1, 0>()), + F4Val(s.template getValue< 0, 0, 2>()) - F4Val(s.template getValue< 0, 0, 1>()), 0), + v6(F4Val(s.template getValue< 3, 0, 0>()) - F4Val(s.template getValue< 2, 0, 0>()), + F4Val(s.template getValue< 0, 3, 0>()) - F4Val(s.template getValue< 0, 2, 0>()), + F4Val(s.template getValue< 0, 0, 3>()) - F4Val(s.template getValue< 0, 0, 2>()), 0), + down = math::WENO5(v1, v2, v3, v4, v5), + up = math::WENO5(v6, v5, v4, v3, v2); + + return math::GodunovsNormSqrd(s.template getValue<0, 0, 0>()>0, down, up); + } +}; +#endif //DWA_OPENVDB // for SIMD - note will do the computations in float +//@} + + +//@{ +/// @brief Laplacian defined in index space, using various center-difference stencils +template +struct ISLaplacian +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk); + + // stencil access version + template + static typename StencilT::ValueType result(const StencilT& stencil); +}; + + +template<> +struct ISLaplacian +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) + { + return grid.getValue(ijk.offsetBy(1,0,0)) + grid.getValue(ijk.offsetBy(-1, 0, 0)) + + grid.getValue(ijk.offsetBy(0,1,0)) + grid.getValue(ijk.offsetBy(0, -1, 0)) + + grid.getValue(ijk.offsetBy(0,0,1)) + grid.getValue(ijk.offsetBy(0, 0,-1)) + - 6*grid.getValue(ijk); + } + + // stencil access version + template + static typename StencilT::ValueType result(const StencilT& stencil) + { + return stencil.template getValue< 1, 0, 0>() + stencil.template getValue<-1, 0, 0>() + + stencil.template getValue< 0, 1, 0>() + stencil.template getValue< 0,-1, 0>() + + stencil.template getValue< 0, 0, 1>() + stencil.template getValue< 0, 0,-1>() + - 6*stencil.template getValue< 0, 0, 0>(); + } +}; + +template<> +struct ISLaplacian +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) + { + using ValueT = typename Accessor::ValueType; + return static_cast( + (-1./12.)*( + grid.getValue(ijk.offsetBy(2,0,0)) + grid.getValue(ijk.offsetBy(-2, 0, 0)) + + grid.getValue(ijk.offsetBy(0,2,0)) + grid.getValue(ijk.offsetBy( 0,-2, 0)) + + grid.getValue(ijk.offsetBy(0,0,2)) + grid.getValue(ijk.offsetBy( 0, 0,-2)) ) + + (4./3.)*( + grid.getValue(ijk.offsetBy(1,0,0)) + grid.getValue(ijk.offsetBy(-1, 0, 0)) + + grid.getValue(ijk.offsetBy(0,1,0)) + grid.getValue(ijk.offsetBy( 0,-1, 0)) + + grid.getValue(ijk.offsetBy(0,0,1)) + grid.getValue(ijk.offsetBy( 0, 0,-1)) ) + - 7.5*grid.getValue(ijk)); + } + + // stencil access version + template + static typename StencilT::ValueType result(const StencilT& stencil) + { + using ValueT = typename StencilT::ValueType; + return static_cast( + (-1./12.)*( + stencil.template getValue< 2, 0, 0>() + stencil.template getValue<-2, 0, 0>() + + stencil.template getValue< 0, 2, 0>() + stencil.template getValue< 0,-2, 0>() + + stencil.template getValue< 0, 0, 2>() + stencil.template getValue< 0, 0,-2>() ) + + (4./3.)*( + stencil.template getValue< 1, 0, 0>() + stencil.template getValue<-1, 0, 0>() + + stencil.template getValue< 0, 1, 0>() + stencil.template getValue< 0,-1, 0>() + + stencil.template getValue< 0, 0, 1>() + stencil.template getValue< 0, 0,-1>() ) + - 7.5*stencil.template getValue< 0, 0, 0>()); + } +}; + +template<> +struct ISLaplacian +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) + { + using ValueT = typename Accessor::ValueType; + return static_cast( + (1./90.)*( + grid.getValue(ijk.offsetBy(3,0,0)) + grid.getValue(ijk.offsetBy(-3, 0, 0)) + + grid.getValue(ijk.offsetBy(0,3,0)) + grid.getValue(ijk.offsetBy( 0,-3, 0)) + + grid.getValue(ijk.offsetBy(0,0,3)) + grid.getValue(ijk.offsetBy( 0, 0,-3)) ) + - (3./20.)*( + grid.getValue(ijk.offsetBy(2,0,0)) + grid.getValue(ijk.offsetBy(-2, 0, 0)) + + grid.getValue(ijk.offsetBy(0,2,0)) + grid.getValue(ijk.offsetBy( 0,-2, 0)) + + grid.getValue(ijk.offsetBy(0,0,2)) + grid.getValue(ijk.offsetBy( 0, 0,-2)) ) + + 1.5 *( + grid.getValue(ijk.offsetBy(1,0,0)) + grid.getValue(ijk.offsetBy(-1, 0, 0)) + + grid.getValue(ijk.offsetBy(0,1,0)) + grid.getValue(ijk.offsetBy( 0,-1, 0)) + + grid.getValue(ijk.offsetBy(0,0,1)) + grid.getValue(ijk.offsetBy( 0, 0,-1)) ) + - (3*49/18.)*grid.getValue(ijk)); + } + + // stencil access version + template + static typename StencilT::ValueType result(const StencilT& stencil) + { + using ValueT = typename StencilT::ValueType; + return static_cast( + (1./90.)*( + stencil.template getValue< 3, 0, 0>() + stencil.template getValue<-3, 0, 0>() + + stencil.template getValue< 0, 3, 0>() + stencil.template getValue< 0,-3, 0>() + + stencil.template getValue< 0, 0, 3>() + stencil.template getValue< 0, 0,-3>() ) + - (3./20.)*( + stencil.template getValue< 2, 0, 0>() + stencil.template getValue<-2, 0, 0>() + + stencil.template getValue< 0, 2, 0>() + stencil.template getValue< 0,-2, 0>() + + stencil.template getValue< 0, 0, 2>() + stencil.template getValue< 0, 0,-2>() ) + + 1.5 *( + stencil.template getValue< 1, 0, 0>() + stencil.template getValue<-1, 0, 0>() + + stencil.template getValue< 0, 1, 0>() + stencil.template getValue< 0,-1, 0>() + + stencil.template getValue< 0, 0, 1>() + stencil.template getValue< 0, 0,-1>() ) + - (3*49/18.)*stencil.template getValue< 0, 0, 0>()); + } +}; +//@} + + +//@{ +/// Divergence operator defined in index space using various first derivative schemes +template +struct ISDivergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const Accessor& grid, const Coord& ijk) + { + return D1Vec::inX(grid, ijk, 0) + + D1Vec::inY(grid, ijk, 1) + + D1Vec::inZ(grid, ijk, 2); + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const StencilT& stencil) + { + return D1Vec::inX(stencil, 0) + + D1Vec::inY(stencil, 1) + + D1Vec::inZ(stencil, 2); + } +}; +//@} + + +//@{ +/// Curl operator defined in index space using various first derivative schemes +template +struct ISCurl +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) + { + using Vec3Type = typename Accessor::ValueType; + return Vec3Type( D1Vec::inY(grid, ijk, 2) - //dw/dy - dv/dz + D1Vec::inZ(grid, ijk, 1), + D1Vec::inZ(grid, ijk, 0) - //du/dz - dw/dx + D1Vec::inX(grid, ijk, 2), + D1Vec::inX(grid, ijk, 1) - //dv/dx - du/dy + D1Vec::inY(grid, ijk, 0) ); + } + + // stencil access version + template + static typename StencilT::ValueType result(const StencilT& stencil) + { + using Vec3Type = typename StencilT::ValueType; + return Vec3Type( D1Vec::inY(stencil, 2) - //dw/dy - dv/dz + D1Vec::inZ(stencil, 1), + D1Vec::inZ(stencil, 0) - //du/dz - dw/dx + D1Vec::inX(stencil, 2), + D1Vec::inX(stencil, 1) - //dv/dx - du/dy + D1Vec::inY(stencil, 0) ); + } +}; +//@} + + +//@{ +/// Compute the mean curvature in index space +template +struct ISMeanCurvature +{ + /// @brief Random access version + /// @return @c true if the gradient is nonzero, in which case the mean curvature + /// is returned in two parts, @a alpha and @a beta, where @a alpha is the numerator + /// in ∇ · (∇Φ / |∇Φ|) and @a beta is |∇Φ|. + template + static bool result(const Accessor& grid, const Coord& ijk, + typename Accessor::ValueType& alpha, + typename Accessor::ValueType& beta) + { + using ValueType = typename Accessor::ValueType; + + const ValueType Dx = D1::inX(grid, ijk); + const ValueType Dy = D1::inY(grid, ijk); + const ValueType Dz = D1::inZ(grid, ijk); + + const ValueType Dx2 = Dx*Dx; + const ValueType Dy2 = Dy*Dy; + const ValueType Dz2 = Dz*Dz; + const ValueType normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance::value()) { + alpha = beta = 0; + return false; + } + + const ValueType Dxx = D2::inX(grid, ijk); + const ValueType Dyy = D2::inY(grid, ijk); + const ValueType Dzz = D2::inZ(grid, ijk); + + const ValueType Dxy = D2::inXandY(grid, ijk); + const ValueType Dyz = D2::inYandZ(grid, ijk); + const ValueType Dxz = D2::inXandZ(grid, ijk); + + // for return + alpha = (Dx2*(Dyy+Dzz)+Dy2*(Dxx+Dzz)+Dz2*(Dxx+Dyy)-2*(Dx*(Dy*Dxy+Dz*Dxz)+Dy*Dz*Dyz)); + beta = ValueType(std::sqrt(double(normGrad))); // * 1/dx + return true; + } + + /// @brief Stencil access version + /// @return @c true if the gradient is nonzero, in which case the mean curvature + /// is returned in two parts, @a alpha and @a beta, where @a alpha is the numerator + /// in ∇ · (∇Φ / |∇Φ|) and @a beta is |∇Φ|. + template + static bool result(const StencilT& stencil, + typename StencilT::ValueType& alpha, + typename StencilT::ValueType& beta) + { + using ValueType = typename StencilT::ValueType; + const ValueType Dx = D1::inX(stencil); + const ValueType Dy = D1::inY(stencil); + const ValueType Dz = D1::inZ(stencil); + + const ValueType Dx2 = Dx*Dx; + const ValueType Dy2 = Dy*Dy; + const ValueType Dz2 = Dz*Dz; + const ValueType normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance::value()) { + alpha = beta = 0; + return false; + } + + const ValueType Dxx = D2::inX(stencil); + const ValueType Dyy = D2::inY(stencil); + const ValueType Dzz = D2::inZ(stencil); + + const ValueType Dxy = D2::inXandY(stencil); + const ValueType Dyz = D2::inYandZ(stencil); + const ValueType Dxz = D2::inXandZ(stencil); + + // for return + alpha = (Dx2*(Dyy+Dzz)+Dy2*(Dxx+Dzz)+Dz2*(Dxx+Dyy)-2*(Dx*(Dy*Dxy+Dz*Dxz)+Dy*Dz*Dyz)); + beta = ValueType(std::sqrt(double(normGrad))); // * 1/dx + return true; + } +}; + +//////////////////////////////////////////////////////// + +// --- Operators defined in the Range of a given map + +//@{ +/// @brief Center difference gradient operators, defined with respect to +/// the range-space of the @c map +/// @note This will need to be divided by two in the case of CD_2NDT +template +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3d iGradient( ISGradient::result(grid, ijk) ); + return Vec3Type(map.applyIJT(iGradient, ijk.asVec3d())); + } + + // stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const MapType& map, const StencilT& stencil) + { + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3d iGradient( ISGradient::result(stencil) ); + return Vec3Type(map.applyIJT(iGradient, stencil.getCenterCoord().asVec3d())); + } +}; + +// Partial template specialization of Gradient +// translation, any order +template +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const TranslationMap&, const Accessor& grid, const Coord& ijk) + { + return ISGradient::result(grid, ijk); + } + + // stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const TranslationMap&, const StencilT& stencil) + { + return ISGradient::result(stencil); + } +}; + +/// Full template specialization of Gradient +/// uniform scale, 2nd order +template<> +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename internal::ReturnValue::ValueType; + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3Type iGradient( ISGradient::result(grid, ijk) ); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return iGradient * inv2dx; + } + + // stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const UniformScaleMap& map, const StencilT& stencil) + { + using ValueType = typename internal::ReturnValue::ValueType; + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3Type iGradient( ISGradient::result(stencil) ); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return iGradient * inv2dx; + } +}; + +/// Full template specialization of Gradient +/// uniform scale translate, 2nd order +template<> +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename internal::ReturnValue::ValueType; + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3Type iGradient( ISGradient::result(grid, ijk) ); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return iGradient * inv2dx; + } + + // stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename internal::ReturnValue::ValueType; + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3Type iGradient( ISGradient::result(stencil) ); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return iGradient * inv2dx; + } +}; + +/// Full template specialization of Gradient +/// scale, 2nd order +template<> +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const ScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename internal::ReturnValue::ValueType; + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3Type iGradient( ISGradient::result(grid, ijk) ); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto gradient0 = iGradient[0] * map.getInvTwiceScale()[0]; + const auto gradient1 = iGradient[1] * map.getInvTwiceScale()[1]; + const auto gradient2 = iGradient[2] * map.getInvTwiceScale()[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return Vec3Type(ValueType(gradient0), + ValueType(gradient1), + ValueType(gradient2)); + } + + // stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const ScaleMap& map, const StencilT& stencil) + { + using ValueType = typename internal::ReturnValue::ValueType; + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3Type iGradient( ISGradient::result(stencil) ); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto gradient0 = iGradient[0] * map.getInvTwiceScale()[0]; + const auto gradient1 = iGradient[1] * map.getInvTwiceScale()[1]; + const auto gradient2 = iGradient[2] * map.getInvTwiceScale()[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return Vec3Type(ValueType(gradient0), + ValueType(gradient1), + ValueType(gradient2)); + } +}; + +/// Full template specialization of Gradient +/// scale translate, 2nd order +template<> +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename internal::ReturnValue::ValueType; + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3Type iGradient( ISGradient::result(grid, ijk) ); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto gradient0 = iGradient[0] * map.getInvTwiceScale()[0]; + const auto gradient1 = iGradient[1] * map.getInvTwiceScale()[1]; + const auto gradient2 = iGradient[2] * map.getInvTwiceScale()[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return Vec3Type(ValueType(gradient0), + ValueType(gradient1), + ValueType(gradient2)); + } + + // Stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const ScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename internal::ReturnValue::ValueType; + using Vec3Type = typename internal::ReturnValue::Vec3Type; + + Vec3Type iGradient( ISGradient::result(stencil) ); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto gradient0 = iGradient[0] * map.getInvTwiceScale()[0]; + const auto gradient1 = iGradient[1] * map.getInvTwiceScale()[1]; + const auto gradient2 = iGradient[2] * map.getInvTwiceScale()[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return Vec3Type(ValueType(gradient0), + ValueType(gradient1), + ValueType(gradient2)); + } +}; +//@} + + +//@{ +/// @brief Biased gradient operators, defined with respect to the range-space of the map +/// @note This will need to be divided by two in the case of CD_2NDT +template +struct GradientBiased +{ + // random access version + template static math::Vec3 + result(const MapType& map, const Accessor& grid, const Coord& ijk, + const Vec3& V) + { + using ValueType = typename Accessor::ValueType; + using Vec3Type = math::Vec3; + + Vec3d iGradient( ISGradientBiased::result(grid, ijk, V) ); + return Vec3Type(map.applyIJT(iGradient, ijk.asVec3d())); + } + + // stencil access version + template static math::Vec3 + result(const MapType& map, const StencilT& stencil, + const Vec3& V) + { + using ValueType = typename StencilT::ValueType; + using Vec3Type = math::Vec3; + + Vec3d iGradient( ISGradientBiased::result(stencil, V) ); + return Vec3Type(map.applyIJT(iGradient, stencil.getCenterCoord().asVec3d())); + } +}; +//@} + + +//////////////////////////////////////////////////////// + +// Computes |Grad[Phi]| using upwinding +template +struct GradientNormSqrd +{ + static const DScheme FD = BIAS_SCHEME::FD; + static const DScheme BD = BIAS_SCHEME::BD; + + + // random access version + template + static typename Accessor::ValueType + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + using Vec3Type = math::Vec3; + + Vec3Type up = Gradient::result(map, grid, ijk); + Vec3Type down = Gradient::result(map, grid, ijk); + return math::GodunovsNormSqrd(grid.getValue(ijk)>0, down, up); + } + + // stencil access version + template + static typename StencilT::ValueType + result(const MapType& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + using Vec3Type = math::Vec3; + + Vec3Type up = Gradient::result(map, stencil); + Vec3Type down = Gradient::result(map, stencil); + return math::GodunovsNormSqrd(stencil.template getValue<0, 0, 0>()>0, down, up); + } +}; + +/// Partial template specialization of GradientNormSqrd +template +struct GradientNormSqrd +{ + // random access version + template + static typename Accessor::ValueType + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return invdxdx * ISGradientNormSqrd::result(grid, ijk); + } + + // stencil access version + template + static typename StencilT::ValueType + result(const UniformScaleMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return invdxdx * ISGradientNormSqrd::result(stencil); + } +}; + +/// Partial template specialization of GradientNormSqrd +template +struct GradientNormSqrd +{ + // random access version + template + static typename Accessor::ValueType + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return invdxdx * ISGradientNormSqrd::result(grid, ijk); + } + + // stencil access version + template + static typename StencilT::ValueType + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return invdxdx * ISGradientNormSqrd::result(stencil); + } +}; + + +//@{ +/// @brief Compute the divergence of a vector-valued grid using differencing +/// of various orders, the result defined with respect to the range-space of the map. +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div(0); + for (int i=0; i < 3; i++) { + Vec3d vec( D1Vec::inX(grid, ijk, i), + D1Vec::inY(grid, ijk, i), + D1Vec::inZ(grid, ijk, i) ); + div += ValueType(map.applyIJT(vec, ijk.asVec3d())[i]); + } + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const MapType& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div(0); + for (int i=0; i < 3; i++) { + Vec3d vec( D1Vec::inX(stencil, i), + D1Vec::inY(stencil, i), + D1Vec::inZ(stencil, i) ); + div += ValueType(map.applyIJT(vec, stencil.getCenterCoord().asVec3d())[i]); + } + return div; + } +}; + +/// Partial template specialization of Divergence +/// translation, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const TranslationMap&, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div(0); + div =ISDivergence::result(grid, ijk); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const TranslationMap&, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div(0); + div =ISDivergence::result(stencil); + return div; + } +}; + +/// Partial template specialization of Divergence +/// uniform scale, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div(0); + + div =ISDivergence::result(grid, ijk); + ValueType invdx = ValueType(map.getInvScale()[0]); + return div * invdx; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const UniformScaleMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div(0); + + div =ISDivergence::result(stencil); + ValueType invdx = ValueType(map.getInvScale()[0]); + return div * invdx; + } +}; + +/// Partial template specialization of Divergence +/// uniform scale and translation, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div(0); + + div =ISDivergence::result(grid, ijk); + ValueType invdx = ValueType(map.getInvScale()[0]); + return div * invdx; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div(0); + + div =ISDivergence::result(stencil); + ValueType invdx = ValueType(map.getInvScale()[0]); + return div * invdx; + } +}; + +/// Full template specialization of Divergence +/// uniform scale 2nd order +template<> +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div(0); + div =ISDivergence::result(grid, ijk); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return div * inv2dx; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const UniformScaleMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div(0); + div =ISDivergence::result(stencil); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return div * inv2dx; + } +}; + +/// Full template specialization of Divergence +/// uniform scale translate 2nd order +template<> +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div(0); + + div =ISDivergence::result(grid, ijk); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return div * inv2dx; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div(0); + + div =ISDivergence::result(stencil); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return div * inv2dx; + } +}; + +/// Partial template specialization of Divergence +/// scale, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const ScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div = ValueType( + D1Vec::inX(grid, ijk, 0) * (map.getInvScale()[0]) + + D1Vec::inY(grid, ijk, 1) * (map.getInvScale()[1]) + + D1Vec::inZ(grid, ijk, 2) * (map.getInvScale()[2])); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const ScaleMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div(0); + div = ValueType( + D1Vec::inX(stencil, 0) * (map.getInvScale()[0]) + + D1Vec::inY(stencil, 1) * (map.getInvScale()[1]) + + D1Vec::inZ(stencil, 2) * (map.getInvScale()[2]) ); + return div; + } +}; + +/// Partial template specialization of Divergence +/// scale translate, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div = ValueType( + D1Vec::inX(grid, ijk, 0) * (map.getInvScale()[0]) + + D1Vec::inY(grid, ijk, 1) * (map.getInvScale()[1]) + + D1Vec::inZ(grid, ijk, 2) * (map.getInvScale()[2])); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const ScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div(0); + div = ValueType( + D1Vec::inX(stencil, 0) * (map.getInvScale()[0]) + + D1Vec::inY(stencil, 1) * (map.getInvScale()[1]) + + D1Vec::inZ(stencil, 2) * (map.getInvScale()[2]) ); + return div; + } +}; + +/// Full template specialization Divergence +/// scale 2nd order +template<> +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const ScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div = ValueType( + D1Vec::inX(grid, ijk, 0) * (map.getInvTwiceScale()[0]) + + D1Vec::inY(grid, ijk, 1) * (map.getInvTwiceScale()[1]) + + D1Vec::inZ(grid, ijk, 2) * (map.getInvTwiceScale()[2]) ); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const ScaleMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div = ValueType( + D1Vec::inX(stencil, 0) * (map.getInvTwiceScale()[0]) + + D1Vec::inY(stencil, 1) * (map.getInvTwiceScale()[1]) + + D1Vec::inZ(stencil, 2) * (map.getInvTwiceScale()[2]) ); + return div; + } +}; + +/// Full template specialization of Divergence +/// scale and translate, 2nd order +template<> +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType::value_type; + + ValueType div = ValueType( + D1Vec::inX(grid, ijk, 0) * (map.getInvTwiceScale()[0]) + + D1Vec::inY(grid, ijk, 1) * (map.getInvTwiceScale()[1]) + + D1Vec::inZ(grid, ijk, 2) * (map.getInvTwiceScale()[2]) ); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const ScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType::value_type; + + ValueType div = ValueType( + D1Vec::inX(stencil, 0) * (map.getInvTwiceScale()[0]) + + D1Vec::inY(stencil, 1) * (map.getInvTwiceScale()[1]) + + D1Vec::inZ(stencil, 2) * (map.getInvTwiceScale()[2]) ); + return div; + } +}; +//@} + + +//@{ +/// @brief Compute the curl of a vector-valued grid using differencing +/// of various orders in the space defined by the range of the map. +template +struct Curl +{ + // random access version + template static typename Accessor::ValueType + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + using Vec3Type = typename Accessor::ValueType; + Vec3Type mat[3]; + for (int i = 0; i < 3; i++) { + Vec3d vec( + D1Vec::inX(grid, ijk, i), + D1Vec::inY(grid, ijk, i), + D1Vec::inZ(grid, ijk, i)); + // dF_i/dx_j (x_1 = x, x_2 = y, x_3 = z) + mat[i] = Vec3Type(map.applyIJT(vec, ijk.asVec3d())); + } + return Vec3Type(mat[2][1] - mat[1][2], // dF_3/dx_2 - dF_2/dx_3 + mat[0][2] - mat[2][0], // dF_1/dx_3 - dF_3/dx_1 + mat[1][0] - mat[0][1]); // dF_2/dx_1 - dF_1/dx_2 + } + + // stencil access version + template static typename StencilT::ValueType + result(const MapType& map, const StencilT& stencil) + { + using Vec3Type = typename StencilT::ValueType; + Vec3Type mat[3]; + for (int i = 0; i < 3; i++) { + Vec3d vec( + D1Vec::inX(stencil, i), + D1Vec::inY(stencil, i), + D1Vec::inZ(stencil, i)); + // dF_i/dx_j (x_1 = x, x_2 = y, x_3 = z) + mat[i] = Vec3Type(map.applyIJT(vec, stencil.getCenterCoord().asVec3d())); + } + return Vec3Type(mat[2][1] - mat[1][2], // dF_3/dx_2 - dF_2/dx_3 + mat[0][2] - mat[2][0], // dF_1/dx_3 - dF_3/dx_1 + mat[1][0] - mat[0][1]); // dF_2/dx_1 - dF_1/dx_2 + } +}; + +/// Partial template specialization of Curl +template +struct Curl +{ + // random access version + template static typename Accessor::ValueType + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using Vec3Type = typename Accessor::ValueType; + using ValueType = typename Vec3Type::value_type; + return ISCurl::result(grid, ijk) * ValueType(map.getInvScale()[0]); + } + + // Stencil access version + template static typename StencilT::ValueType + result(const UniformScaleMap& map, const StencilT& stencil) + { + using Vec3Type = typename StencilT::ValueType; + using ValueType = typename Vec3Type::value_type; + return ISCurl::result(stencil) * ValueType(map.getInvScale()[0]); + } +}; + +/// Partial template specialization of Curl +template +struct Curl +{ + // random access version + template static typename Accessor::ValueType + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using Vec3Type = typename Accessor::ValueType; + using ValueType = typename Vec3Type::value_type; + + return ISCurl::result(grid, ijk) * ValueType(map.getInvScale()[0]); + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + using Vec3Type = typename StencilT::ValueType; + using ValueType = typename Vec3Type::value_type; + + return ISCurl::result(stencil) * ValueType(map.getInvScale()[0]); + } +}; + +/// Full template specialization of Curl +template<> +struct Curl +{ + // random access version + template static typename Accessor::ValueType + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using Vec3Type = typename Accessor::ValueType; + using ValueType = typename Vec3Type::value_type; + + return ISCurl::result(grid, ijk) * ValueType(map.getInvTwiceScale()[0]); + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleMap& map, const StencilT& stencil) + { + using Vec3Type = typename StencilT::ValueType; + using ValueType = typename Vec3Type::value_type; + + return ISCurl::result(stencil) * ValueType(map.getInvTwiceScale()[0]); + } +}; + +/// Full template specialization of Curl +template<> +struct Curl +{ + // random access version + template static typename Accessor::ValueType + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using Vec3Type = typename Accessor::ValueType; + using ValueType = typename Vec3Type::value_type; + + return ISCurl::result(grid, ijk) * ValueType(map.getInvTwiceScale()[0]); + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + using Vec3Type = typename StencilT::ValueType; + using ValueType = typename Vec3Type::value_type; + + return ISCurl::result(stencil) * ValueType(map.getInvTwiceScale()[0]); + } +}; +//@} + + +//@{ +/// @brief Compute the Laplacian at a given location in a grid using finite differencing +/// of various orders. The result is defined in the range of the map. +template +struct Laplacian +{ + // random access version + template + static typename Accessor::ValueType result(const MapType& map, + const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + // all the second derivatives in index space + ValueType iddx = D2::inX(grid, ijk); + ValueType iddy = D2::inY(grid, ijk); + ValueType iddz = D2::inZ(grid, ijk); + + ValueType iddxy = D2::inXandY(grid, ijk); + ValueType iddyz = D2::inYandZ(grid, ijk); + ValueType iddxz = D2::inXandZ(grid, ijk); + + // second derivatives in index space + Mat3d d2_is(iddx, iddxy, iddxz, + iddxy, iddy, iddyz, + iddxz, iddyz, iddz); + + Mat3d d2_rs; // to hold the second derivative matrix in range space + if (is_linear::value) { + d2_rs = map.applyIJC(d2_is); + } else { + // compute the first derivatives with 2nd order accuracy. + Vec3d d1_is(static_cast(D1::inX(grid, ijk)), + static_cast(D1::inY(grid, ijk)), + static_cast(D1::inZ(grid, ijk))); + + d2_rs = map.applyIJC(d2_is, d1_is, ijk.asVec3d()); + } + + // the trace of the second derivative (range space) matrix is laplacian + return ValueType(d2_rs(0,0) + d2_rs(1,1) + d2_rs(2,2)); + } + + // stencil access version + template + static typename StencilT::ValueType result(const MapType& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + // all the second derivatives in index space + ValueType iddx = D2::inX(stencil); + ValueType iddy = D2::inY(stencil); + ValueType iddz = D2::inZ(stencil); + + ValueType iddxy = D2::inXandY(stencil); + ValueType iddyz = D2::inYandZ(stencil); + ValueType iddxz = D2::inXandZ(stencil); + + // second derivatives in index space + Mat3d d2_is(iddx, iddxy, iddxz, + iddxy, iddy, iddyz, + iddxz, iddyz, iddz); + + Mat3d d2_rs; // to hold the second derivative matrix in range space + if (is_linear::value) { + d2_rs = map.applyIJC(d2_is); + } else { + // compute the first derivatives with 2nd order accuracy. + Vec3d d1_is(D1::inX(stencil), + D1::inY(stencil), + D1::inZ(stencil) ); + + d2_rs = map.applyIJC(d2_is, d1_is, stencil.getCenterCoord().asVec3d()); + } + + // the trace of the second derivative (range space) matrix is laplacian + return ValueType(d2_rs(0,0) + d2_rs(1,1) + d2_rs(2,2)); + } +}; + + +template +struct Laplacian +{ + // random access version + template + static typename Accessor::ValueType result(const TranslationMap&, + const Accessor& grid, const Coord& ijk) + { + return ISLaplacian::result(grid, ijk); + } + + // stencil access version + template + static typename StencilT::ValueType result(const TranslationMap&, const StencilT& stencil) + { + return ISLaplacian::result(stencil); + } +}; + + +// The Laplacian is invariant to rotation or reflection. +template +struct Laplacian +{ + // random access version + template + static typename Accessor::ValueType result(const UnitaryMap&, + const Accessor& grid, const Coord& ijk) + { + return ISLaplacian::result(grid, ijk); + } + + // stencil access version + template + static typename StencilT::ValueType result(const UnitaryMap&, const StencilT& stencil) + { + return ISLaplacian::result(stencil); + } +}; + + +template +struct Laplacian +{ + // random access version + template static typename Accessor::ValueType + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ISLaplacian::result(grid, ijk) * invdxdx; + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ISLaplacian::result(stencil) * invdxdx; + } +}; + + +template +struct Laplacian +{ + // random access version + template static typename Accessor::ValueType + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ISLaplacian::result(grid, ijk) * invdxdx; + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ISLaplacian::result(stencil) * invdxdx; + } +}; + + +template +struct Laplacian +{ + // random access version + template static typename Accessor::ValueType + result(const ScaleMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + + // compute the second derivatives in index space + ValueType iddx = D2::inX(grid, ijk); + ValueType iddy = D2::inY(grid, ijk); + ValueType iddz = D2::inZ(grid, ijk); + const Vec3d& invScaleSqr = map.getInvScaleSqr(); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + // scale them by the appropriate 1/dx^2, 1/dy^2, 1/dz^2 and sum + const ValueType value = iddx * invScaleSqr[0] + iddy * invScaleSqr[1] + iddz * invScaleSqr[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return value; + } + + // stencil access version + template static typename StencilT::ValueType + result(const ScaleMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + + // compute the second derivatives in index space + ValueType iddx = D2::inX(stencil); + ValueType iddy = D2::inY(stencil); + ValueType iddz = D2::inZ(stencil); + const Vec3d& invScaleSqr = map.getInvScaleSqr(); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + // scale them by the appropriate 1/dx^2, 1/dy^2, 1/dz^2 and sum + const ValueType value = iddx * invScaleSqr[0] + iddy * invScaleSqr[1] + iddz * invScaleSqr[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return value; + } +}; + + +template +struct Laplacian +{ + // random access version + template static typename Accessor::ValueType + result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + // compute the second derivatives in index space + ValueType iddx = D2::inX(grid, ijk); + ValueType iddy = D2::inY(grid, ijk); + ValueType iddz = D2::inZ(grid, ijk); + const Vec3d& invScaleSqr = map.getInvScaleSqr(); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + // scale them by the appropriate 1/dx^2, 1/dy^2, 1/dz^2 and sum + const ValueType value = iddx * invScaleSqr[0] + iddy * invScaleSqr[1] + iddz * invScaleSqr[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return value; + } + + // stencil access version + template static typename StencilT::ValueType + result(const ScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + // compute the second derivatives in index space + ValueType iddx = D2::inX(stencil); + ValueType iddy = D2::inY(stencil); + ValueType iddz = D2::inZ(stencil); + const Vec3d& invScaleSqr = map.getInvScaleSqr(); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + // scale them by the appropriate 1/dx^2, 1/dy^2, 1/dz^2 and sum + const ValueType value = iddx * invScaleSqr[0] + iddy * invScaleSqr[1] + iddz * invScaleSqr[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return value; + } +}; + + +/// @brief Compute the closest-point transform to a level set. +/// @return the closest point to the surface from which the level set was derived, +/// in the domain space of the map (e.g., voxel space). +template +struct CPT +{ + // random access version + template static math::Vec3 + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + using Vec3Type = Vec3; + + // current distance + ValueType d = grid.getValue(ijk); + // compute gradient in physical space where it is a unit normal + // since the grid holds a distance level set. + Vec3d vectorFromSurface(d*Gradient::result(map, grid, ijk)); + if (is_linear::value) { + Vec3d result = ijk.asVec3d() - map.applyInverseMap(vectorFromSurface); + return Vec3Type(result); + } else { + Vec3d location = map.applyMap(ijk.asVec3d()); + Vec3d result = map.applyInverseMap(location - vectorFromSurface); + return Vec3Type(result); + } + } + + // stencil access version + template static math::Vec3 + result(const MapType& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + using Vec3Type = Vec3; + + // current distance + ValueType d = stencil.template getValue<0, 0, 0>(); + // compute gradient in physical space where it is a unit normal + // since the grid holds a distance level set. + Vec3d vectorFromSurface(d*Gradient::result(map, stencil)); + if (is_linear::value) { + Vec3d result = stencil.getCenterCoord().asVec3d() + - map.applyInverseMap(vectorFromSurface); + return Vec3Type(result); + } else { + Vec3d location = map.applyMap(stencil.getCenterCoord().asVec3d()); + Vec3d result = map.applyInverseMap(location - vectorFromSurface); + return Vec3Type(result); + } + } +}; + + +/// @brief Compute the closest-point transform to a level set. +/// @return the closest point to the surface from which the level set was derived, +/// in the range space of the map (e.g., in world space) +template +struct CPT_RANGE +{ + // random access version + template static Vec3 + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + using Vec3Type = Vec3; + // current distance + ValueType d = grid.getValue(ijk); + // compute gradient in physical space where it is a unit normal + // since the grid holds a distance level set. + Vec3Type vectorFromSurface = + d*Gradient::result(map, grid, ijk); + Vec3d result = map.applyMap(ijk.asVec3d()) - vectorFromSurface; + + return Vec3Type(result); + } + + // stencil access version + template static Vec3 + result(const MapType& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + using Vec3Type = Vec3; + // current distance + ValueType d = stencil.template getValue<0, 0, 0>(); + // compute gradient in physical space where it is a unit normal + // since the grid holds a distance level set. + Vec3Type vectorFromSurface = + d*Gradient::result(map, stencil); + Vec3d result = map.applyMap(stencil.getCenterCoord().asVec3d()) - vectorFromSurface; + + return Vec3Type(result); + } +}; + + +/// @brief Compute the mean curvature. +/// @details The mean curvature is returned in two parts, @a alpha and @a beta, +/// where @a alpha is the numerator in ∇ · (∇Φ / |∇Φ|) +/// and @a beta is |∇Φ|. +template +struct MeanCurvature +{ + /// @brief Random access version + /// @return @c true if the gradient is nonzero, in which case the mean curvature + /// is returned in two parts, @a alpha and @a beta, where @a alpha is the numerator + /// in ∇ · (∇Φ / |∇Φ|) and @a beta is |∇Φ|. + template + static bool compute(const MapType& map, const Accessor& grid, const Coord& ijk, + double& alpha, double& beta) + { + using ValueType = typename Accessor::ValueType; + + // compute the gradient in index and world space + Vec3d d1_is(static_cast(D1::inX(grid, ijk)), + static_cast(D1::inY(grid, ijk)), + static_cast(D1::inZ(grid, ijk))), d1_ws; + if (is_linear::value) {//resolved at compiletime + d1_ws = map.applyIJT(d1_is); + } else { + d1_ws = map.applyIJT(d1_is, ijk.asVec3d()); + } + const double Dx2 = d1_ws(0)*d1_ws(0); + const double Dy2 = d1_ws(1)*d1_ws(1); + const double Dz2 = d1_ws(2)*d1_ws(2); + const double normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance::value()) { + alpha = beta = 0; + return false; + } + + // all the second derivatives in index space + ValueType iddx = D2::inX(grid, ijk); + ValueType iddy = D2::inY(grid, ijk); + ValueType iddz = D2::inZ(grid, ijk); + + ValueType iddxy = D2::inXandY(grid, ijk); + ValueType iddyz = D2::inYandZ(grid, ijk); + ValueType iddxz = D2::inXandZ(grid, ijk); + + // second derivatives in index space + Mat3d d2_is(iddx, iddxy, iddxz, + iddxy, iddy, iddyz, + iddxz, iddyz, iddz); + + // convert second derivatives to world space + Mat3d d2_ws; + if (is_linear::value) {//resolved at compiletime + d2_ws = map.applyIJC(d2_is); + } else { + d2_ws = map.applyIJC(d2_is, d1_is, ijk.asVec3d()); + } + + // assemble the nominator and denominator for mean curvature + alpha = (Dx2*(d2_ws(1,1)+d2_ws(2,2))+Dy2*(d2_ws(0,0)+d2_ws(2,2)) + +Dz2*(d2_ws(0,0)+d2_ws(1,1)) + -2*(d1_ws(0)*(d1_ws(1)*d2_ws(0,1)+d1_ws(2)*d2_ws(0,2)) + +d1_ws(1)*d1_ws(2)*d2_ws(1,2))); + beta = std::sqrt(normGrad); // * 1/dx + return true; + } + + template + static typename Accessor::ValueType result(const MapType& map, + const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + double alpha, beta; + return compute(map, grid, ijk, alpha, beta) ? + ValueType(alpha/(2. *math::Pow3(beta))) : 0; + } + + template + static typename Accessor::ValueType normGrad(const MapType& map, + const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + double alpha, beta; + return compute(map, grid, ijk, alpha, beta) ? + ValueType(alpha/(2. *math::Pow2(beta))) : 0; + } + + /// @brief Stencil access version + /// @return @c true if the gradient is nonzero, in which case the mean curvature + /// is returned in two parts, @a alpha and @a beta, where @a alpha is the numerator + /// in ∇ · (∇Φ / |∇Φ|) and @a beta is |∇Φ|. + template + static bool compute(const MapType& map, const StencilT& stencil, + double& alpha, double& beta) + { + using ValueType = typename StencilT::ValueType; + + // compute the gradient in index and world space + Vec3d d1_is(D1::inX(stencil), + D1::inY(stencil), + D1::inZ(stencil) ), d1_ws; + if (is_linear::value) {//resolved at compiletime + d1_ws = map.applyIJT(d1_is); + } else { + d1_ws = map.applyIJT(d1_is, stencil.getCenterCoord().asVec3d()); + } + const double Dx2 = d1_ws(0)*d1_ws(0); + const double Dy2 = d1_ws(1)*d1_ws(1); + const double Dz2 = d1_ws(2)*d1_ws(2); + const double normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance::value()) { + alpha = beta = 0; + return false; + } + + // all the second derivatives in index space + ValueType iddx = D2::inX(stencil); + ValueType iddy = D2::inY(stencil); + ValueType iddz = D2::inZ(stencil); + + ValueType iddxy = D2::inXandY(stencil); + ValueType iddyz = D2::inYandZ(stencil); + ValueType iddxz = D2::inXandZ(stencil); + + // second derivatives in index space + Mat3d d2_is(iddx, iddxy, iddxz, + iddxy, iddy, iddyz, + iddxz, iddyz, iddz); + + // convert second derivatives to world space + Mat3d d2_ws; + if (is_linear::value) {//resolved at compiletime + d2_ws = map.applyIJC(d2_is); + } else { + d2_ws = map.applyIJC(d2_is, d1_is, stencil.getCenterCoord().asVec3d()); + } + + // for return + alpha = (Dx2*(d2_ws(1,1)+d2_ws(2,2))+Dy2*(d2_ws(0,0)+d2_ws(2,2)) + +Dz2*(d2_ws(0,0)+d2_ws(1,1)) + -2*(d1_ws(0)*(d1_ws(1)*d2_ws(0,1)+d1_ws(2)*d2_ws(0,2)) + +d1_ws(1)*d1_ws(2)*d2_ws(1,2))); + beta = std::sqrt(normGrad); // * 1/dx + return true; + } + + template + static typename StencilT::ValueType + result(const MapType& map, const StencilT stencil) + { + using ValueType = typename StencilT::ValueType; + double alpha, beta; + return compute(map, stencil, alpha, beta) ? + ValueType(alpha/(2*math::Pow3(beta))) : 0; + } + + template + static typename StencilT::ValueType normGrad(const MapType& map, const StencilT stencil) + { + using ValueType = typename StencilT::ValueType; + double alpha, beta; + return compute(map, stencil, alpha, beta) ? + ValueType(alpha/(2*math::Pow2(beta))) : 0; + } +}; + + +template +struct MeanCurvature +{ + // random access version + template + static typename Accessor::ValueType result(const TranslationMap&, + const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + + ValueType alpha, beta; + return ISMeanCurvature::result(grid, ijk, alpha, beta) ? + ValueType(alpha /(2*math::Pow3(beta))) : 0; + } + + template + static typename Accessor::ValueType normGrad(const TranslationMap&, + const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + + ValueType alpha, beta; + return ISMeanCurvature::result(grid, ijk, alpha, beta) ? + ValueType(alpha/(2*math::Pow2(beta))) : 0; + } + + // stencil access version + template + static typename StencilT::ValueType result(const TranslationMap&, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + + ValueType alpha, beta; + return ISMeanCurvature::result(stencil, alpha, beta) ? + ValueType(alpha /(2*math::Pow3(beta))) : 0; + } + + template + static typename StencilT::ValueType normGrad(const TranslationMap&, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + + ValueType alpha, beta; + return ISMeanCurvature::result(stencil, alpha, beta) ? + ValueType(alpha/(2*math::Pow2(beta))) : 0; + } +}; + + +template +struct MeanCurvature +{ + // random access version + template + static typename Accessor::ValueType result(const UniformScaleMap& map, + const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(grid, ijk, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; + } + + template + static typename Accessor::ValueType normGrad(const UniformScaleMap& map, + const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(grid, ijk, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; + } + + // stencil access version + template + static typename StencilT::ValueType result(const UniformScaleMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(stencil, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; + } + + template + static typename StencilT::ValueType normGrad(const UniformScaleMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(stencil, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; + } +}; + + +template +struct MeanCurvature +{ + // random access version + template static typename Accessor::ValueType + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(grid, ijk, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; + } + + template static typename Accessor::ValueType + normGrad(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + using ValueType = typename Accessor::ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(grid, ijk, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(stencil, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; + } + + template static typename StencilT::ValueType + normGrad(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + using ValueType = typename StencilT::ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(stencil, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; + } +}; + + +/// @brief A wrapper that holds a MapBase::ConstPtr and exposes a reduced set +/// of functionality needed by the mathematical operators +/// @details This may be used in some Map-templated code, when the overhead of +/// actually resolving the @c Map type is large compared to the map work to be done. +class GenericMap +{ +public: + template + GenericMap(const GridType& g): mMap(g.transform().baseMap()) {} + + GenericMap(const Transform& t): mMap(t.baseMap()) {} + GenericMap(MapBase::Ptr map): mMap(ConstPtrCast(map)) {} + GenericMap(MapBase::ConstPtr map): mMap(map) {} + ~GenericMap() {} + + Vec3d applyMap(const Vec3d& in) const { return mMap->applyMap(in); } + Vec3d applyInverseMap(const Vec3d& in) const { return mMap->applyInverseMap(in); } + + Vec3d applyIJT(const Vec3d& in) const { return mMap->applyIJT(in); } + Vec3d applyIJT(const Vec3d& in, const Vec3d& pos) const { return mMap->applyIJT(in, pos); } + Mat3d applyIJC(const Mat3d& m) const { return mMap->applyIJC(m); } + Mat3d applyIJC(const Mat3d& m, const Vec3d& v, const Vec3d& pos) const + { return mMap->applyIJC(m,v,pos); } + + double determinant() const { return mMap->determinant(); } + double determinant(const Vec3d& in) const { return mMap->determinant(in); } + + Vec3d voxelSize() const { return mMap->voxelSize(); } + Vec3d voxelSize(const Vec3d&v) const { return mMap->voxelSize(v); } + +private: + MapBase::ConstPtr mMap; +}; + +} // end math namespace +} // namespace OPENVDB_VERSION_NAME +} // end openvdb namespace + +#endif // OPENVDB_MATH_OPERATORS_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Proximity.cc b/openvdb/math/Proximity.cc new file mode 100644 index 00000000..d52bdc37 --- /dev/null +++ b/openvdb/math/Proximity.cc @@ -0,0 +1,131 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Proximity.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + + +OPENVDB_API Vec3d +closestPointOnTriangleToPoint( + const Vec3d& a, const Vec3d& b, const Vec3d& c, const Vec3d& p, Vec3d& uvw) +{ + uvw.setZero(); + + // degenerate triangle, singular + if ((isApproxEqual(a, b) && isApproxEqual(a, c))) { + uvw[0] = 1.0; + return a; + } + + Vec3d ab = b - a, ac = c - a, ap = p - a; + double d1 = ab.dot(ap), d2 = ac.dot(ap); + + // degenerate triangle edges + if (isApproxEqual(a, b)) { + + double t = 0.0; + Vec3d cp = closestPointOnSegmentToPoint(a, c, p, t); + + uvw[0] = 1.0 - t; + uvw[2] = t; + + return cp; + + } else if (isApproxEqual(a, c) || isApproxEqual(b, c)) { + + double t = 0.0; + Vec3d cp = closestPointOnSegmentToPoint(a, b, p, t); + uvw[0] = 1.0 - t; + uvw[1] = t; + return cp; + } + + if (d1 <= 0.0 && d2 <= 0.0) { + uvw[0] = 1.0; + return a; // barycentric coordinates (1,0,0) + } + + // Check if P in vertex region outside B + Vec3d bp = p - b; + double d3 = ab.dot(bp), d4 = ac.dot(bp); + if (d3 >= 0.0 && d4 <= d3) { + uvw[1] = 1.0; + return b; // barycentric coordinates (0,1,0) + } + + // Check if P in edge region of AB, if so return projection of P onto AB + double vc = d1 * d4 - d3 * d2; + if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) { + uvw[1] = d1 / (d1 - d3); + uvw[0] = 1.0 - uvw[1]; + return a + uvw[1] * ab; // barycentric coordinates (1-v,v,0) + } + + // Check if P in vertex region outside C + Vec3d cp = p - c; + double d5 = ab.dot(cp), d6 = ac.dot(cp); + if (d6 >= 0.0 && d5 <= d6) { + uvw[2] = 1.0; + return c; // barycentric coordinates (0,0,1) + } + + // Check if P in edge region of AC, if so return projection of P onto AC + double vb = d5 * d2 - d1 * d6; + if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) { + uvw[2] = d2 / (d2 - d6); + uvw[0] = 1.0 - uvw[2]; + return a + uvw[2] * ac; // barycentric coordinates (1-w,0,w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + double va = d3*d6 - d5*d4; + if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) { + uvw[2] = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + uvw[1] = 1.0 - uvw[2]; + return b + uvw[2] * (c - b); // barycentric coordinates (0,1-w,w) + } + + // P inside face region. Compute Q through its barycentric coordinates (u,v,w) + double denom = 1.0 / (va + vb + vc); + uvw[2] = vc * denom; + uvw[1] = vb * denom; + uvw[0] = 1.0 - uvw[1] - uvw[2]; + + return a + ab*uvw[1] + ac*uvw[2]; // = u*a + v*b + w*c , u= va*denom = 1.0-v-w +} + + +OPENVDB_API Vec3d +closestPointOnSegmentToPoint(const Vec3d& a, const Vec3d& b, const Vec3d& p, double& t) +{ + Vec3d ab = b - a; + t = (p - a).dot(ab); + + if (t <= 0.0) { + // c projects outside the [a,b] interval, on the a side. + t = 0.0; + return a; + } else { + + // always nonnegative since denom = ||ab||^2 + double denom = ab.dot(ab); + + if (t >= denom) { + // c projects outside the [a,b] interval, on the b side. + t = 1.0; + return b; + } else { + // c projects inside the [a,b] interval. + t = t / denom; + return a + (ab * t); + } + } +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/math/Proximity.h b/openvdb/math/Proximity.h new file mode 100644 index 00000000..8206d4fc --- /dev/null +++ b/openvdb/math/Proximity.h @@ -0,0 +1,48 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_PROXIMITY_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_PROXIMITY_HAS_BEEN_INCLUDED + +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief Closest Point on Triangle to Point. Given a triangle @c abc and a point @c p, +/// return the point on @c abc closest to @c p and the corresponding barycentric coordinates. +/// +/// @details Algorithms from "Real-Time Collision Detection" pg 136 to 142 by Christer Ericson. +/// The closest point is obtained by first determining which of the triangles' +/// Voronoi feature regions @c p is in and then computing the orthogonal projection +/// of @c p onto the corresponding feature. +/// +/// @param a The triangle's first vertex point. +/// @param b The triangle's second vertex point. +/// @param c The triangle's third vertex point. +/// @param p Point to compute the closest point on @c abc for. +/// @param uvw Barycentric coordinates, computed and returned. +OPENVDB_API Vec3d +closestPointOnTriangleToPoint( + const Vec3d& a, const Vec3d& b, const Vec3d& c, const Vec3d& p, Vec3d& uvw); + + +/// @brief Closest Point on Line Segment to Point. Given segment @c ab and point @c p, +/// return the point on @c ab closest to @c p and @c t the parametric distance to @c b. +/// +/// @param a The segment's first vertex point. +/// @param b The segment's second vertex point. +/// @param p Point to compute the closest point on @c ab for. +/// @param t Parametric distance to @c b. +OPENVDB_API Vec3d +closestPointOnSegmentToPoint( + const Vec3d& a, const Vec3d& b, const Vec3d& p, double& t); + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MESH_TO_VOLUME_UTIL_HAS_BEEN_INCLUDED diff --git a/openvdb/math/QuantizedUnitVec.cc b/openvdb/math/QuantizedUnitVec.cc new file mode 100644 index 00000000..b3b36b6e --- /dev/null +++ b/openvdb/math/QuantizedUnitVec.cc @@ -0,0 +1,2082 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "QuantizedUnitVec.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +// The table below is generated as follows: +// +// uint16_t xbits, ybits; +// double x, y, z, w; +// for (uint16_t b = 0; b < 8192; ++b) { +// xbits = uint16_t((b & MASK_XSLOT) >> 7); +// ybits = b & MASK_YSLOT; +// if ((xbits + ybits) > 126) { +// xbits = uint16_t(127 - xbits); +// ybits = uint16_t(127 - ybits); +// } +// x = double(xbits); +// y = double(ybits); +// z = double(126 - xbits - ybits); +// w = 1.0 / std::sqrt(x*x + y*y + z*z); +// sNormalizationWeights[b] = float(w); +// } + +float QuantizedUnitVec::sNormalizationWeights[8192] = { + 0.007936508395f, 0.007999744266f, 0.008063467219f, 0.008127664216f, + 0.008192319423f, 0.008257416077f, 0.008322936483f, 0.008388860151f, + 0.008455166593f, 0.008521833457f, 0.008588834666f, 0.008656143211f, + 0.008723732084f, 0.00879156962f, 0.008859624155f, 0.008927859366f, + 0.008996240795f, 0.009064726532f, 0.009133277461f, 0.009201847948f, + 0.00927039329f, 0.009338863194f, 0.009407208301f, 0.009475374594f, + 0.009543305263f, 0.009610942565f, 0.009678225033f, 0.009745089337f, + 0.009811468422f, 0.009877296165f, 0.009942499921f, 0.01000700705f, + 0.01007074397f, 0.01013363153f, 0.01019559242f, 0.01025654469f, + 0.01031640731f, 0.01037509646f, 0.01043252647f, 0.01048861258f, + 0.01054326911f, 0.01059640758f, 0.01064794231f, 0.01069778763f, + 0.01074585691f, 0.01079206541f, 0.01083632838f, 0.01087856572f, + 0.01091869641f, 0.01095664315f, 0.01099232957f, 0.01102568675f, + 0.01105664484f, 0.01108513959f, 0.01111111138f, 0.01113450434f, + 0.01115526911f, 0.01117335912f, 0.01118873432f, 0.01120136213f, + 0.01121121366f, 0.01121826563f, 0.01122250315f, 0.0112239169f, + 0.01122250315f, 0.01121826563f, 0.01121121366f, 0.01120136213f, + 0.01118873432f, 0.01117335912f, 0.01115526911f, 0.01113450434f, + 0.01111111138f, 0.01108513959f, 0.01105664484f, 0.01102568675f, + 0.01099232957f, 0.01095664315f, 0.01091869641f, 0.01087856572f, + 0.01083632838f, 0.01079206541f, 0.01074585691f, 0.01069778763f, + 0.01064794231f, 0.01059640758f, 0.01054326911f, 0.01048861258f, + 0.01043252647f, 0.01037509646f, 0.01031640731f, 0.01025654469f, + 0.01019559242f, 0.01013363153f, 0.01007074397f, 0.01000700705f, + 0.009942499921f, 0.009877296165f, 0.009811468422f, 0.009745089337f, + 0.009678225033f, 0.009610942565f, 0.009543305263f, 0.009475374594f, + 0.009407208301f, 0.009338863194f, 0.00927039329f, 0.009201847948f, + 0.009133277461f, 0.009064726532f, 0.008996240795f, 0.008927859366f, + 0.008859624155f, 0.00879156962f, 0.008723732084f, 0.008656143211f, + 0.008588834666f, 0.008521833457f, 0.008455166593f, 0.008388860151f, + 0.008322936483f, 0.008257416077f, 0.008192319423f, 0.008127664216f, + 0.008063467219f, 0.007999744266f, 0.007936508395f, 0.007873771712f, + 0.007999744266f, 0.008063991554f, 0.008128738031f, 0.008193968795f, + 0.008259668946f, 0.008325820789f, 0.008392404765f, 0.008459401317f, + 0.008526788093f, 0.00859454181f, 0.008662636392f, 0.008731043898f, + 0.008799735457f, 0.008868678473f, 0.008937839419f, 0.009007181972f, + 0.00907666795f, 0.009146256372f, 0.009215905331f, 0.009285567328f, + 0.009355195798f, 0.009424740449f, 0.009494146332f, 0.009563359432f, + 0.009632320143f, 0.009700968862f, 0.009769240394f, 0.00983707048f, + 0.00990438927f, 0.009971125983f, 0.01003720704f, 0.01010255609f, + 0.01016709674f, 0.01023074798f, 0.01029342785f, 0.01035505254f, + 0.01041553635f, 0.01047479361f, 0.01053273678f, 0.01058927551f, + 0.01064432319f, 0.01069778763f, 0.01074958127f, 0.01079961471f, + 0.01084779948f, 0.01089404803f, 0.01093827467f, 0.0109803956f, + 0.01102032885f, 0.01105799619f, 0.01109332126f, 0.0111262314f, + 0.01115665678f, 0.01118453499f, 0.01120980456f, 0.01123241056f, + 0.01125230361f, 0.01126943901f, 0.01128377859f, 0.01129528973f, + 0.01130394638f, 0.01130972803f, 0.01131262258f, 0.01131262258f, + 0.01130972803f, 0.01130394638f, 0.01129528973f, 0.01128377859f, + 0.01126943901f, 0.01125230361f, 0.01123241056f, 0.01120980456f, + 0.01118453499f, 0.01115665678f, 0.0111262314f, 0.01109332126f, + 0.01105799619f, 0.01102032885f, 0.0109803956f, 0.01093827467f, + 0.01089404803f, 0.01084779948f, 0.01079961471f, 0.01074958127f, + 0.01069778763f, 0.01064432319f, 0.01058927551f, 0.01053273678f, + 0.01047479361f, 0.01041553635f, 0.01035505254f, 0.01029342785f, + 0.01023074798f, 0.01016709674f, 0.01010255609f, 0.01003720704f, + 0.009971125983f, 0.00990438927f, 0.00983707048f, 0.009769240394f, + 0.009700968862f, 0.009632320143f, 0.009563359432f, 0.009494146332f, + 0.009424740449f, 0.009355195798f, 0.009285567328f, 0.009215905331f, + 0.009146256372f, 0.00907666795f, 0.009007181972f, 0.008937839419f, + 0.008868678473f, 0.008799735457f, 0.008731043898f, 0.008662636392f, + 0.00859454181f, 0.008526788093f, 0.008459401317f, 0.008392404765f, + 0.008325820789f, 0.008259668946f, 0.008193968795f, 0.008128738031f, + 0.008063991554f, 0.007999744266f, 0.007936008275f, 0.007936508395f, + 0.008063467219f, 0.008128738031f, 0.008194519207f, 0.008260795847f, + 0.008327552117f, 0.008394770324f, 0.008462429978f, 0.008530510589f, + 0.008598989807f, 0.008667841554f, 0.008737040684f, 0.008806557395f, + 0.008876360953f, 0.008946419694f, 0.009016696364f, 0.009087154642f, + 0.009157754481f, 0.009228453971f, 0.009299207479f, 0.009369968437f, + 0.009440686554f, 0.009511308745f, 0.009581780061f, 0.00965204183f, + 0.009722034447f, 0.009791694582f, 0.009860955179f, 0.009929747321f, + 0.009998000227f, 0.01006564032f, 0.01013259124f, 0.01019877382f, + 0.01026410609f, 0.01032850612f, 0.01039188914f, 0.01045416761f, + 0.01051525306f, 0.01057505608f, 0.01063348539f, 0.01069044974f, + 0.01074585691f, 0.01079961471f, 0.01085163094f, 0.0109018134f, + 0.01095007174f, 0.01099631656f, 0.01104046032f, 0.0110824164f, + 0.01112210099f, 0.01115943585f, 0.01119434182f, 0.01122674625f, + 0.01125658024f, 0.01128377859f, 0.01130828168f, 0.01133003552f, + 0.01134899072f, 0.01136510447f, 0.01137833856f, 0.01138866507f, + 0.0113960579f, 0.01140050031f, 0.01140198205f, 0.01140050031f, + 0.0113960579f, 0.01138866507f, 0.01137833856f, 0.01136510447f, + 0.01134899072f, 0.01133003552f, 0.01130828168f, 0.01128377859f, + 0.01125658024f, 0.01122674625f, 0.01119434182f, 0.01115943585f, + 0.01112210099f, 0.0110824164f, 0.01104046032f, 0.01099631656f, + 0.01095007174f, 0.0109018134f, 0.01085163094f, 0.01079961471f, + 0.01074585691f, 0.01069044974f, 0.01063348539f, 0.01057505608f, + 0.01051525306f, 0.01045416761f, 0.01039188914f, 0.01032850612f, + 0.01026410609f, 0.01019877382f, 0.01013259124f, 0.01006564032f, + 0.009998000227f, 0.009929747321f, 0.009860955179f, 0.009791694582f, + 0.009722034447f, 0.00965204183f, 0.009581780061f, 0.009511308745f, + 0.009440686554f, 0.009369968437f, 0.009299207479f, 0.009228453971f, + 0.009157754481f, 0.009087154642f, 0.009016696364f, 0.008946419694f, + 0.008876360953f, 0.008806557395f, 0.008737040684f, 0.008667841554f, + 0.008598989807f, 0.008530510589f, 0.008462429978f, 0.008394770324f, + 0.008327552117f, 0.008260795847f, 0.008194519207f, 0.008128738031f, + 0.008063467219f, 0.007998720743f, 0.007999744266f, 0.007999744266f, + 0.008127664216f, 0.008193968795f, 0.008260795847f, 0.008328129537f, + 0.008395953104f, 0.008464248851f, 0.008532994427f, 0.008602170274f, + 0.008671752177f, 0.008741713129f, 0.008812026121f, 0.008882662281f, + 0.008953588083f, 0.00902477093f, 0.009096172638f, 0.009167755023f, + 0.009239477105f, 0.009311294183f, 0.009383158758f, 0.009455023333f, + 0.009526834823f, 0.00959853828f, 0.009670076892f, 0.009741389193f, + 0.009812413715f, 0.009883082472f, 0.009953328408f, 0.01002307981f, + 0.01009226125f, 0.01016079728f, 0.01022860687f, 0.01029560994f, + 0.01036172081f, 0.01042685378f, 0.01049092133f, 0.01055383217f, + 0.01061549596f, 0.01067581866f, 0.01073470619f, 0.01079206541f, + 0.01084779948f, 0.0109018134f, 0.01095401309f, 0.01100430358f, + 0.01105259173f, 0.01109878626f, 0.01114279591f, 0.01118453499f, + 0.0112239169f, 0.01126086153f, 0.01129528973f, 0.01132712793f, + 0.01135630626f, 0.01138276048f, 0.01140643191f, 0.01142726559f, + 0.01144521404f, 0.01146023627f, 0.01147229597f, 0.01148136612f, + 0.0114874253f, 0.01149045862f, 0.01149045862f, 0.0114874253f, + 0.01148136612f, 0.01147229597f, 0.01146023627f, 0.01144521404f, + 0.01142726559f, 0.01140643191f, 0.01138276048f, 0.01135630626f, + 0.01132712793f, 0.01129528973f, 0.01126086153f, 0.0112239169f, + 0.01118453499f, 0.01114279591f, 0.01109878626f, 0.01105259173f, + 0.01100430358f, 0.01095401309f, 0.0109018134f, 0.01084779948f, + 0.01079206541f, 0.01073470619f, 0.01067581866f, 0.01061549596f, + 0.01055383217f, 0.01049092133f, 0.01042685378f, 0.01036172081f, + 0.01029560994f, 0.01022860687f, 0.01016079728f, 0.01009226125f, + 0.01002307981f, 0.009953328408f, 0.009883082472f, 0.009812413715f, + 0.009741389193f, 0.009670076892f, 0.00959853828f, 0.009526834823f, + 0.009455023333f, 0.009383158758f, 0.009311294183f, 0.009239477105f, + 0.009167755023f, 0.009096172638f, 0.00902477093f, 0.008953588083f, + 0.008882662281f, 0.008812026121f, 0.008741713129f, 0.008671752177f, + 0.008602170274f, 0.008532994427f, 0.008464248851f, 0.008395953104f, + 0.008328129537f, 0.008260795847f, 0.008193968795f, 0.008127664216f, + 0.008061895147f, 0.008063467219f, 0.008063991554f, 0.008063467219f, + 0.008192319423f, 0.008259668946f, 0.008327552117f, 0.008395953104f, + 0.008464855142f, 0.008534237742f, 0.008604080416f, 0.008674361743f, + 0.008745054714f, 0.008816135116f, 0.008887572214f, 0.008959336206f, + 0.009031393565f, 0.009103707969f, 0.009176243097f, 0.009248957038f, + 0.009321806021f, 0.009394746274f, 0.009467727505f, 0.009540699422f, + 0.009613607079f, 0.009686394595f, 0.009759000503f, 0.009831363335f, + 0.0099034179f, 0.009975093417f, 0.01004632004f, 0.01011702232f, + 0.0101871239f, 0.01025654469f, 0.01032520272f, 0.01039301138f, + 0.010459885f, 0.0105257323f, 0.01059046388f, 0.0106539838f, + 0.01071619987f, 0.01077701338f, 0.01083632838f, 0.01089404803f, + 0.01095007174f, 0.01100430358f, 0.01105664484f, 0.01110699773f, + 0.01115526911f, 0.01120136213f, 0.01124518644f, 0.01128665265f, + 0.01132567506f, 0.01136216894f, 0.0113960579f, 0.01142726559f, + 0.01145572308f, 0.01148136612f, 0.01150413603f, 0.01152398065f, + 0.01154085156f, 0.0115547115f, 0.01156552508f, 0.0115732681f, + 0.01157792099f, 0.0115794735f, 0.01157792099f, 0.0115732681f, + 0.01156552508f, 0.0115547115f, 0.01154085156f, 0.01152398065f, + 0.01150413603f, 0.01148136612f, 0.01145572308f, 0.01142726559f, + 0.0113960579f, 0.01136216894f, 0.01132567506f, 0.01128665265f, + 0.01124518644f, 0.01120136213f, 0.01115526911f, 0.01110699773f, + 0.01105664484f, 0.01100430358f, 0.01095007174f, 0.01089404803f, + 0.01083632838f, 0.01077701338f, 0.01071619987f, 0.0106539838f, + 0.01059046388f, 0.0105257323f, 0.010459885f, 0.01039301138f, + 0.01032520272f, 0.01025654469f, 0.0101871239f, 0.01011702232f, + 0.01004632004f, 0.009975093417f, 0.0099034179f, 0.009831363335f, + 0.009759000503f, 0.009686394595f, 0.009613607079f, 0.009540699422f, + 0.009467727505f, 0.009394746274f, 0.009321806021f, 0.009248957038f, + 0.009176243097f, 0.009103707969f, 0.009031393565f, 0.008959336206f, + 0.008887572214f, 0.008816135116f, 0.008745054714f, 0.008674361743f, + 0.008604080416f, 0.008534237742f, 0.008464855142f, 0.008395953104f, + 0.008327552117f, 0.008259668946f, 0.008192319423f, 0.008125517517f, + 0.008127664216f, 0.008128738031f, 0.008128738031f, 0.008127664216f, + 0.008257416077f, 0.008325820789f, 0.008394770324f, 0.008464248851f, + 0.008534237742f, 0.008604717441f, 0.008675667457f, 0.008747061715f, + 0.00881887693f, 0.008891084231f, 0.008963654749f, 0.009036554955f, + 0.00910975039f, 0.009183204733f, 0.009256878868f, 0.009330729023f, + 0.009404712357f, 0.00947877951f, 0.00955288019f, 0.009626962245f, + 0.009700968862f, 0.009774839506f, 0.009848512709f, 0.009921924211f, + 0.009995004162f, 0.01006768085f, 0.01013988163f, 0.01021152735f, + 0.01028253883f, 0.01035283227f, 0.0104223229f, 0.01049092133f, + 0.01055853814f, 0.01062507927f, 0.01069044974f, 0.0107545536f, + 0.01081729215f, 0.01087856572f, 0.01093827467f, 0.01099631656f, + 0.01105259173f, 0.01110699773f, 0.01115943585f, 0.01120980456f, + 0.01125800703f, 0.01130394638f, 0.01134752948f, 0.01138866507f, + 0.01142726559f, 0.01146324724f, 0.01149653178f, 0.01152704284f, + 0.0115547115f, 0.0115794735f, 0.01160127111f, 0.01162005402f, + 0.01163577568f, 0.01164839976f, 0.01165789459f, 0.0116642369f, + 0.01166741177f, 0.01166741177f, 0.0116642369f, 0.01165789459f, + 0.01164839976f, 0.01163577568f, 0.01162005402f, 0.01160127111f, + 0.0115794735f, 0.0115547115f, 0.01152704284f, 0.01149653178f, + 0.01146324724f, 0.01142726559f, 0.01138866507f, 0.01134752948f, + 0.01130394638f, 0.01125800703f, 0.01120980456f, 0.01115943585f, + 0.01110699773f, 0.01105259173f, 0.01099631656f, 0.01093827467f, + 0.01087856572f, 0.01081729215f, 0.0107545536f, 0.01069044974f, + 0.01062507927f, 0.01055853814f, 0.01049092133f, 0.0104223229f, + 0.01035283227f, 0.01028253883f, 0.01021152735f, 0.01013988163f, + 0.01006768085f, 0.009995004162f, 0.009921924211f, 0.009848512709f, + 0.009774839506f, 0.009700968862f, 0.009626962245f, 0.00955288019f, + 0.00947877951f, 0.009404712357f, 0.009330729023f, 0.009256878868f, + 0.009183204733f, 0.00910975039f, 0.009036554955f, 0.008963654749f, + 0.008891084231f, 0.00881887693f, 0.008747061715f, 0.008675667457f, + 0.008604717441f, 0.008534237742f, 0.008464248851f, 0.008394770324f, + 0.008325820789f, 0.008257416077f, 0.00818957109f, 0.008192319423f, + 0.008193968795f, 0.008194519207f, 0.008193968795f, 0.008192319423f, + 0.008322936483f, 0.008392404765f, 0.008462429978f, 0.008532994427f, + 0.008604080416f, 0.008675667457f, 0.008747731335f, 0.008820248768f, + 0.008893193677f, 0.008966536261f, 0.009040246718f, 0.009114289656f, + 0.009188630618f, 0.009263231419f, 0.009338049218f, 0.009413041174f, + 0.009488161653f, 0.009563359432f, 0.009638582356f, 0.009713775478f, + 0.009788879193f, 0.009863832965f, 0.009938570671f, 0.01001302525f, + 0.01008712593f, 0.01016079728f, 0.01023396198f, 0.01030653995f, + 0.01037844829f, 0.01044960041f, 0.01051990688f, 0.01058927551f, + 0.0106576141f, 0.01072482392f, 0.01079080813f, 0.01085546613f, + 0.01091869641f, 0.0109803956f, 0.01104046032f, 0.01109878626f, + 0.01115526911f, 0.01120980456f, 0.01126228925f, 0.01131262258f, + 0.01136070304f, 0.01140643191f, 0.01144971419f, 0.01149045862f, + 0.01152857486f, 0.01156397816f, 0.01159659028f, 0.01162633486f, + 0.01165314391f, 0.01167695317f, 0.01169770677f, 0.0117153544f, + 0.0117298523f, 0.011741166f, 0.01174926758f, 0.0117541356f, + 0.01175575983f, 0.0117541356f, 0.01174926758f, 0.011741166f, + 0.0117298523f, 0.0117153544f, 0.01169770677f, 0.01167695317f, + 0.01165314391f, 0.01162633486f, 0.01159659028f, 0.01156397816f, + 0.01152857486f, 0.01149045862f, 0.01144971419f, 0.01140643191f, + 0.01136070304f, 0.01131262258f, 0.01126228925f, 0.01120980456f, + 0.01115526911f, 0.01109878626f, 0.01104046032f, 0.0109803956f, + 0.01091869641f, 0.01085546613f, 0.01079080813f, 0.01072482392f, + 0.0106576141f, 0.01058927551f, 0.01051990688f, 0.01044960041f, + 0.01037844829f, 0.01030653995f, 0.01023396198f, 0.01016079728f, + 0.01008712593f, 0.01001302525f, 0.009938570671f, 0.009863832965f, + 0.009788879193f, 0.009713775478f, 0.009638582356f, 0.009563359432f, + 0.009488161653f, 0.009413041174f, 0.009338049218f, 0.009263231419f, + 0.009188630618f, 0.009114289656f, 0.009040246718f, 0.008966536261f, + 0.008893193677f, 0.008820248768f, 0.008747731335f, 0.008675667457f, + 0.008604080416f, 0.008532994427f, 0.008462429978f, 0.008392404765f, + 0.008322936483f, 0.008254040033f, 0.008257416077f, 0.008259668946f, + 0.008260795847f, 0.008260795847f, 0.008259668946f, 0.008257416077f, + 0.008388860151f, 0.008459401317f, 0.008530510589f, 0.008602170274f, + 0.008674361743f, 0.008747061715f, 0.008820248768f, 0.008893896826f, + 0.00896797888f, 0.009042463265f, 0.00911732018f, 0.009192512371f, + 0.009268003516f, 0.0093437545f, 0.00941972062f, 0.009495858103f, + 0.009572117589f, 0.009648446925f, 0.009724793024f, 0.009801096283f, + 0.009877296165f, 0.009953328408f, 0.01002912689f, 0.01010461897f, + 0.01017973199f, 0.01025438774f, 0.01032850612f, 0.01040200423f, + 0.01047479361f, 0.01054678671f, 0.01061788946f, 0.01068800688f, + 0.0107570421f, 0.01082489453f, 0.01089146268f, 0.01095664315f, + 0.01102032885f, 0.0110824164f, 0.01114279591f, 0.01120136213f, + 0.01125800703f, 0.01131262258f, 0.01136510447f, 0.01141534653f, + 0.01146324724f, 0.01150870696f, 0.01155162696f, 0.01159191411f, + 0.01162947994f, 0.0116642369f, 0.01169610675f, 0.01172501314f, + 0.01175088901f, 0.01177367195f, 0.0117933061f, 0.01180974208f, + 0.01182294171f, 0.01183286961f, 0.01183950249f, 0.01184282266f, + 0.01184282266f, 0.01183950249f, 0.01183286961f, 0.01182294171f, + 0.01180974208f, 0.0117933061f, 0.01177367195f, 0.01175088901f, + 0.01172501314f, 0.01169610675f, 0.0116642369f, 0.01162947994f, + 0.01159191411f, 0.01155162696f, 0.01150870696f, 0.01146324724f, + 0.01141534653f, 0.01136510447f, 0.01131262258f, 0.01125800703f, + 0.01120136213f, 0.01114279591f, 0.0110824164f, 0.01102032885f, + 0.01095664315f, 0.01089146268f, 0.01082489453f, 0.0107570421f, + 0.01068800688f, 0.01061788946f, 0.01054678671f, 0.01047479361f, + 0.01040200423f, 0.01032850612f, 0.01025438774f, 0.01017973199f, + 0.01010461897f, 0.01002912689f, 0.009953328408f, 0.009877296165f, + 0.009801096283f, 0.009724793024f, 0.009648446925f, 0.009572117589f, + 0.009495858103f, 0.00941972062f, 0.0093437545f, 0.009268003516f, + 0.009192512371f, 0.00911732018f, 0.009042463265f, 0.00896797888f, + 0.008893896826f, 0.008820248768f, 0.008747061715f, 0.008674361743f, + 0.008602170274f, 0.008530510589f, 0.008459401317f, 0.008388860151f, + 0.008318902925f, 0.008322936483f, 0.008325820789f, 0.008327552117f, + 0.008328129537f, 0.008327552117f, 0.008325820789f, 0.008322936483f, + 0.008455166593f, 0.008526788093f, 0.008598989807f, 0.008671752177f, + 0.008745054714f, 0.00881887693f, 0.008893193677f, 0.00896797888f, + 0.009043202735f, 0.009118835442f, 0.009194843471f, 0.00927118957f, + 0.009347835556f, 0.009424740449f, 0.009501857683f, 0.009579141624f, + 0.009656541049f, 0.009734002873f, 0.009811468422f, 0.009888879955f, + 0.009966172278f, 0.01004327927f, 0.01012013014f, 0.01019665226f, + 0.01027276739f, 0.01034839638f, 0.01042345539f, 0.01049785595f, + 0.0105715096f, 0.01064432319f, 0.01071619987f, 0.01078704093f, + 0.01085674576f, 0.01092521101f, 0.01099232957f, 0.01105799619f, + 0.01112210099f, 0.01118453499f, 0.01124518644f, 0.01130394638f, + 0.01136070304f, 0.01141534653f, 0.01146776881f, 0.01151786372f, + 0.01156552508f, 0.01161065139f, 0.01165314391f, 0.01169290766f, + 0.0117298523f, 0.01176389214f, 0.01179494616f, 0.01182294171f, + 0.01184780896f, 0.01186948828f, 0.01188792568f, 0.0119030755f, + 0.01191489771f, 0.01192336436f, 0.01192845311f, 0.01193015091f, + 0.01192845311f, 0.01192336436f, 0.01191489771f, 0.0119030755f, + 0.01188792568f, 0.01186948828f, 0.01184780896f, 0.01182294171f, + 0.01179494616f, 0.01176389214f, 0.0117298523f, 0.01169290766f, + 0.01165314391f, 0.01161065139f, 0.01156552508f, 0.01151786372f, + 0.01146776881f, 0.01141534653f, 0.01136070304f, 0.01130394638f, + 0.01124518644f, 0.01118453499f, 0.01112210099f, 0.01105799619f, + 0.01099232957f, 0.01092521101f, 0.01085674576f, 0.01078704093f, + 0.01071619987f, 0.01064432319f, 0.0105715096f, 0.01049785595f, + 0.01042345539f, 0.01034839638f, 0.01027276739f, 0.01019665226f, + 0.01012013014f, 0.01004327927f, 0.009966172278f, 0.009888879955f, + 0.009811468422f, 0.009734002873f, 0.009656541049f, 0.009579141624f, + 0.009501857683f, 0.009424740449f, 0.009347835556f, 0.00927118957f, + 0.009194843471f, 0.009118835442f, 0.009043202735f, 0.00896797888f, + 0.008893193677f, 0.00881887693f, 0.008745054714f, 0.008671752177f, + 0.008598989807f, 0.008526788093f, 0.008455166593f, 0.00838414114f, + 0.008388860151f, 0.008392404765f, 0.008394770324f, 0.008395953104f, + 0.008395953104f, 0.008394770324f, 0.008392404765f, 0.008388860151f, + 0.008521833457f, 0.00859454181f, 0.008667841554f, 0.008741713129f, + 0.008816135116f, 0.008891084231f, 0.008966536261f, 0.009042463265f, + 0.009118835442f, 0.009195621125f, 0.009272783995f, 0.009350287728f, + 0.009428090416f, 0.009506150149f, 0.009584420361f, 0.00966285076f, + 0.009741389193f, 0.009819980711f, 0.009898564778f, 0.009977078997f, + 0.01005545817f, 0.01013363153f, 0.01021152735f, 0.0102890674f, + 0.01036617346f, 0.01044276077f, 0.01051874273f, 0.01059402898f, + 0.01066852547f, 0.01074213628f, 0.01081476174f, 0.01088629849f, + 0.01095664315f, 0.01102568675f, 0.01109332126f, 0.01115943585f, + 0.0112239169f, 0.01128665265f, 0.01134752948f, 0.01140643191f, + 0.01146324724f, 0.01151786372f, 0.01157016866f, 0.01162005402f, + 0.01166741177f, 0.01171213947f, 0.0117541356f, 0.0117933061f, + 0.0118295569f, 0.01186280511f, 0.01189296879f, 0.01191997528f, + 0.01194375753f, 0.01196425594f, 0.01198141929f, 0.01199520286f, + 0.01200557221f, 0.01201249938f, 0.01201596763f, 0.01201596763f, + 0.01201249938f, 0.01200557221f, 0.01199520286f, 0.01198141929f, + 0.01196425594f, 0.01194375753f, 0.01191997528f, 0.01189296879f, + 0.01186280511f, 0.0118295569f, 0.0117933061f, 0.0117541356f, + 0.01171213947f, 0.01166741177f, 0.01162005402f, 0.01157016866f, + 0.01151786372f, 0.01146324724f, 0.01140643191f, 0.01134752948f, + 0.01128665265f, 0.0112239169f, 0.01115943585f, 0.01109332126f, + 0.01102568675f, 0.01095664315f, 0.01088629849f, 0.01081476174f, + 0.01074213628f, 0.01066852547f, 0.01059402898f, 0.01051874273f, + 0.01044276077f, 0.01036617346f, 0.0102890674f, 0.01021152735f, + 0.01013363153f, 0.01005545817f, 0.009977078997f, 0.009898564778f, + 0.009819980711f, 0.009741389193f, 0.00966285076f, 0.009584420361f, + 0.009506150149f, 0.009428090416f, 0.009350287728f, 0.009272783995f, + 0.009195621125f, 0.009118835442f, 0.009042463265f, 0.008966536261f, + 0.008891084231f, 0.008816135116f, 0.008741713129f, 0.008667841554f, + 0.00859454181f, 0.008521833457f, 0.008449732326f, 0.008455166593f, + 0.008459401317f, 0.008462429978f, 0.008464248851f, 0.008464855142f, + 0.008464248851f, 0.008462429978f, 0.008459401317f, 0.008455166593f, + 0.008588834666f, 0.008662636392f, 0.008737040684f, 0.008812026121f, + 0.008887572214f, 0.008963654749f, 0.009040246718f, 0.00911732018f, + 0.009194843471f, 0.009272783995f, 0.009351104498f, 0.009429766797f, + 0.00950872805f, 0.009587943554f, 0.00966736488f, 0.009746940807f, + 0.009826615453f, 0.00990633294f, 0.009986029007f, 0.01006564032f, + 0.01014509797f, 0.01022432931f, 0.01030325703f, 0.01038180385f, + 0.010459885f, 0.01053741388f, 0.01061430015f, 0.01069044974f, + 0.01076576579f, 0.01084014867f, 0.01091349311f, 0.01098569483f, + 0.01105664484f, 0.0111262314f, 0.01119434182f, 0.01126086153f, + 0.01132567506f, 0.01138866507f, 0.01144971419f, 0.01150870696f, + 0.01156552508f, 0.01162005402f, 0.01167218015f, 0.01172179077f, + 0.01176877879f, 0.01181303803f, 0.01185446698f, 0.01189296879f, + 0.01192845311f, 0.0119608324f, 0.01199002843f, 0.01201596763f, + 0.01203858573f, 0.01205782313f, 0.01207363233f, 0.01208597142f, + 0.01209480781f, 0.01210011914f, 0.01210189145f, 0.01210011914f, + 0.01209480781f, 0.01208597142f, 0.01207363233f, 0.01205782313f, + 0.01203858573f, 0.01201596763f, 0.01199002843f, 0.0119608324f, + 0.01192845311f, 0.01189296879f, 0.01185446698f, 0.01181303803f, + 0.01176877879f, 0.01172179077f, 0.01167218015f, 0.01162005402f, + 0.01156552508f, 0.01150870696f, 0.01144971419f, 0.01138866507f, + 0.01132567506f, 0.01126086153f, 0.01119434182f, 0.0111262314f, + 0.01105664484f, 0.01098569483f, 0.01091349311f, 0.01084014867f, + 0.01076576579f, 0.01069044974f, 0.01061430015f, 0.01053741388f, + 0.010459885f, 0.01038180385f, 0.01030325703f, 0.01022432931f, + 0.01014509797f, 0.01006564032f, 0.009986029007f, 0.00990633294f, + 0.009826615453f, 0.009746940807f, 0.00966736488f, 0.009587943554f, + 0.00950872805f, 0.009429766797f, 0.009351104498f, 0.009272783995f, + 0.009194843471f, 0.00911732018f, 0.009040246718f, 0.008963654749f, + 0.008887572214f, 0.008812026121f, 0.008737040684f, 0.008662636392f, + 0.008588834666f, 0.008515651338f, 0.008521833457f, 0.008526788093f, + 0.008530510589f, 0.008532994427f, 0.008534237742f, 0.008534237742f, + 0.008532994427f, 0.008530510589f, 0.008526788093f, 0.008521833457f, + 0.008656143211f, 0.008731043898f, 0.008806557395f, 0.008882662281f, + 0.008959336206f, 0.009036554955f, 0.009114289656f, 0.009192512371f, + 0.00927118957f, 0.009350287728f, 0.009429766797f, 0.00950958766f, + 0.009589706548f, 0.009670076892f, 0.009750646539f, 0.009831363335f, + 0.00991217047f, 0.009993007407f, 0.01007380895f, 0.01015450899f, + 0.01023503393f, 0.01031531021f, 0.01039525773f, 0.01047479361f, + 0.01055383217f, 0.01063228305f, 0.01071005128f, 0.01078704093f, + 0.01086314954f, 0.01093827467f, 0.01101230737f, 0.01108513959f, + 0.01115665678f, 0.01122674625f, 0.01129528973f, 0.01136216894f, + 0.01142726559f, 0.01149045862f, 0.01155162696f, 0.01161065139f, + 0.01166741177f, 0.01172179077f, 0.01177367195f, 0.01182294171f, + 0.01186948828f, 0.01191320643f, 0.0119539937f, 0.01199175231f, + 0.01202639099f, 0.01205782313f, 0.01208597142f, 0.01211076323f, + 0.01213213522f, 0.01215003151f, 0.01216440555f, 0.01217522006f, + 0.0121824462f, 0.01218606345f, 0.01218606345f, 0.0121824462f, + 0.01217522006f, 0.01216440555f, 0.01215003151f, 0.01213213522f, + 0.01211076323f, 0.01208597142f, 0.01205782313f, 0.01202639099f, + 0.01199175231f, 0.0119539937f, 0.01191320643f, 0.01186948828f, + 0.01182294171f, 0.01177367195f, 0.01172179077f, 0.01166741177f, + 0.01161065139f, 0.01155162696f, 0.01149045862f, 0.01142726559f, + 0.01136216894f, 0.01129528973f, 0.01122674625f, 0.01115665678f, + 0.01108513959f, 0.01101230737f, 0.01093827467f, 0.01086314954f, + 0.01078704093f, 0.01071005128f, 0.01063228305f, 0.01055383217f, + 0.01047479361f, 0.01039525773f, 0.01031531021f, 0.01023503393f, + 0.01015450899f, 0.01007380895f, 0.009993007407f, 0.00991217047f, + 0.009831363335f, 0.009750646539f, 0.009670076892f, 0.009589706548f, + 0.00950958766f, 0.009429766797f, 0.009350287728f, 0.00927118957f, + 0.009192512371f, 0.009114289656f, 0.009036554955f, 0.008959336206f, + 0.008882662281f, 0.008806557395f, 0.008731043898f, 0.008656143211f, + 0.008581873029f, 0.008588834666f, 0.00859454181f, 0.008598989807f, + 0.008602170274f, 0.008604080416f, 0.008604717441f, 0.008604080416f, + 0.008602170274f, 0.008598989807f, 0.00859454181f, 0.008588834666f, + 0.008723732084f, 0.008799735457f, 0.008876360953f, 0.008953588083f, + 0.009031393565f, 0.00910975039f, 0.009188630618f, 0.009268003516f, + 0.009347835556f, 0.009428090416f, 0.00950872805f, 0.009589706548f, + 0.009670981206f, 0.009752500802f, 0.009834215976f, 0.009916068986f, + 0.009998000227f, 0.01007994823f, 0.01016184594f, 0.01024362259f, + 0.01032520272f, 0.01040650904f, 0.0104874596f, 0.01056796685f, + 0.01064794231f, 0.01072729193f, 0.0108059179f, 0.01088371873f, + 0.01096059103f, 0.0110364249f, 0.01111111138f, 0.01118453499f, + 0.01125658024f, 0.01132712793f, 0.0113960579f, 0.01146324724f, + 0.01152857486f, 0.01159191411f, 0.01165314391f, 0.01171213947f, + 0.01176877879f, 0.01182294171f, 0.01187450811f, 0.01192336436f, + 0.01196939778f, 0.01201249938f, 0.01205256768f, 0.01208950393f, + 0.01212321594f, 0.0121536199f, 0.0121806385f, 0.01220420003f, + 0.01222424489f, 0.01224071812f, 0.01225357689f, 0.01226278674f, + 0.01226832252f, 0.01227016933f, 0.01226832252f, 0.01226278674f, + 0.01225357689f, 0.01224071812f, 0.01222424489f, 0.01220420003f, + 0.0121806385f, 0.0121536199f, 0.01212321594f, 0.01208950393f, + 0.01205256768f, 0.01201249938f, 0.01196939778f, 0.01192336436f, + 0.01187450811f, 0.01182294171f, 0.01176877879f, 0.01171213947f, + 0.01165314391f, 0.01159191411f, 0.01152857486f, 0.01146324724f, + 0.0113960579f, 0.01132712793f, 0.01125658024f, 0.01118453499f, + 0.01111111138f, 0.0110364249f, 0.01096059103f, 0.01088371873f, + 0.0108059179f, 0.01072729193f, 0.01064794231f, 0.01056796685f, + 0.0104874596f, 0.01040650904f, 0.01032520272f, 0.01024362259f, + 0.01016184594f, 0.01007994823f, 0.009998000227f, 0.009916068986f, + 0.009834215976f, 0.009752500802f, 0.009670981206f, 0.009589706548f, + 0.00950872805f, 0.009428090416f, 0.009347835556f, 0.009268003516f, + 0.009188630618f, 0.00910975039f, 0.009031393565f, 0.008953588083f, + 0.008876360953f, 0.008799735457f, 0.008723732084f, 0.008648370393f, + 0.008656143211f, 0.008662636392f, 0.008667841554f, 0.008671752177f, + 0.008674361743f, 0.008675667457f, 0.008675667457f, 0.008674361743f, + 0.008671752177f, 0.008667841554f, 0.008662636392f, 0.008656143211f, + 0.00879156962f, 0.008868678473f, 0.008946419694f, 0.00902477093f, + 0.009103707969f, 0.009183204733f, 0.009263231419f, 0.0093437545f, + 0.009424740449f, 0.009506150149f, 0.009587943554f, 0.009670076892f, + 0.009752500802f, 0.009835166857f, 0.009918019176f, 0.01000100002f, + 0.01008404791f, 0.01016709674f, 0.01025007758f, 0.01033291686f, + 0.01041553635f, 0.01049785595f, 0.01057978906f, 0.01066124719f, + 0.01074213628f, 0.01082235854f, 0.0109018134f, 0.0109803956f, + 0.01105799619f, 0.01113450434f, 0.01120980456f, 0.01128377859f, + 0.01135630626f, 0.01142726559f, 0.01149653178f, 0.01156397816f, + 0.01162947994f, 0.01169290766f, 0.0117541356f, 0.01181303803f, + 0.01186948828f, 0.01192336436f, 0.0119745452f, 0.01202291343f, + 0.01206835546f, 0.01211076323f, 0.01215003151f, 0.01218606345f, + 0.01221876871f, 0.0122480616f, 0.01227386575f, 0.01229611505f, + 0.01231474802f, 0.01232971624f, 0.01234097779f, 0.01234850287f, + 0.01235227007f, 0.01235227007f, 0.01234850287f, 0.01234097779f, + 0.01232971624f, 0.01231474802f, 0.01229611505f, 0.01227386575f, + 0.0122480616f, 0.01221876871f, 0.01218606345f, 0.01215003151f, + 0.01211076323f, 0.01206835546f, 0.01202291343f, 0.0119745452f, + 0.01192336436f, 0.01186948828f, 0.01181303803f, 0.0117541356f, + 0.01169290766f, 0.01162947994f, 0.01156397816f, 0.01149653178f, + 0.01142726559f, 0.01135630626f, 0.01128377859f, 0.01120980456f, + 0.01113450434f, 0.01105799619f, 0.0109803956f, 0.0109018134f, + 0.01082235854f, 0.01074213628f, 0.01066124719f, 0.01057978906f, + 0.01049785595f, 0.01041553635f, 0.01033291686f, 0.01025007758f, + 0.01016709674f, 0.01008404791f, 0.01000100002f, 0.009918019176f, + 0.009835166857f, 0.009752500802f, 0.009670076892f, 0.009587943554f, + 0.009506150149f, 0.009424740449f, 0.0093437545f, 0.009263231419f, + 0.009183204733f, 0.009103707969f, 0.00902477093f, 0.008946419694f, + 0.008868678473f, 0.00879156962f, 0.008715113625f, 0.008723732084f, + 0.008731043898f, 0.008737040684f, 0.008741713129f, 0.008745054714f, + 0.008747061715f, 0.008747731335f, 0.008747061715f, 0.008745054714f, + 0.008741713129f, 0.008737040684f, 0.008731043898f, 0.008723732084f, + 0.008859624155f, 0.008937839419f, 0.009016696364f, 0.009096172638f, + 0.009176243097f, 0.009256878868f, 0.009338049218f, 0.00941972062f, + 0.009501857683f, 0.009584420361f, 0.00966736488f, 0.009750646539f, + 0.009834215976f, 0.009918019176f, 0.01000200026f, 0.01008609962f, + 0.01017025113f, 0.01025438774f, 0.01033843681f, 0.0104223229f, + 0.01050596405f, 0.01058927551f, 0.01067216974f, 0.0107545536f, + 0.01083632838f, 0.01091739535f, 0.01099764649f, 0.01107697561f, + 0.01115526911f, 0.01123241056f, 0.01130828168f, 0.01138276048f, + 0.01145572308f, 0.01152704284f, 0.01159659028f, 0.0116642369f, + 0.0117298523f, 0.0117933061f, 0.01185446698f, 0.01191320643f, + 0.01196939778f, 0.01202291343f, 0.01207363233f, 0.01212143432f, + 0.01216620672f, 0.01220783778f, 0.0122462241f, 0.01228126884f, + 0.01231288072f, 0.01234097779f, 0.01236548461f, 0.01238633506f, + 0.01240347326f, 0.01241685264f, 0.01242643595f, 0.01243219618f, + 0.01243411843f, 0.01243219618f, 0.01242643595f, 0.01241685264f, + 0.01240347326f, 0.01238633506f, 0.01236548461f, 0.01234097779f, + 0.01231288072f, 0.01228126884f, 0.0122462241f, 0.01220783778f, + 0.01216620672f, 0.01212143432f, 0.01207363233f, 0.01202291343f, + 0.01196939778f, 0.01191320643f, 0.01185446698f, 0.0117933061f, + 0.0117298523f, 0.0116642369f, 0.01159659028f, 0.01152704284f, + 0.01145572308f, 0.01138276048f, 0.01130828168f, 0.01123241056f, + 0.01115526911f, 0.01107697561f, 0.01099764649f, 0.01091739535f, + 0.01083632838f, 0.0107545536f, 0.01067216974f, 0.01058927551f, + 0.01050596405f, 0.0104223229f, 0.01033843681f, 0.01025438774f, + 0.01017025113f, 0.01008609962f, 0.01000200026f, 0.009918019176f, + 0.009834215976f, 0.009750646539f, 0.00966736488f, 0.009584420361f, + 0.009501857683f, 0.00941972062f, 0.009338049218f, 0.009256878868f, + 0.009176243097f, 0.009096172638f, 0.009016696364f, 0.008937839419f, + 0.008859624155f, 0.008782071993f, 0.00879156962f, 0.008799735457f, + 0.008806557395f, 0.008812026121f, 0.008816135116f, 0.00881887693f, + 0.008820248768f, 0.008820248768f, 0.00881887693f, 0.008816135116f, + 0.008812026121f, 0.008806557395f, 0.008799735457f, 0.00879156962f, + 0.008927859366f, 0.009007181972f, 0.009087154642f, 0.009167755023f, + 0.009248957038f, 0.009330729023f, 0.009413041174f, 0.009495858103f, + 0.009579141624f, 0.00966285076f, 0.009746940807f, 0.009831363335f, + 0.009916068986f, 0.01000100002f, 0.01008609962f, 0.01017130353f, + 0.01025654469f, 0.01034175418f, 0.01042685378f, 0.01051176619f, + 0.01059640758f, 0.01068068855f, 0.01076451875f, 0.01084779948f, + 0.01093043014f, 0.01101230737f, 0.01109332126f, 0.01117335912f, + 0.01125230361f, 0.01133003552f, 0.01140643191f, 0.01148136612f, + 0.0115547115f, 0.01162633486f, 0.01169610675f, 0.01176389214f, + 0.0118295569f, 0.01189296879f, 0.0119539937f, 0.01201249938f, + 0.01206835546f, 0.01212143432f, 0.01217161212f, 0.01221876871f, + 0.01226278674f, 0.01230355818f, 0.01234097779f, 0.01237494871f, + 0.01240538247f, 0.01243219618f, 0.01245531905f, 0.01247468684f, + 0.01249024551f, 0.01250195317f, 0.01250977721f, 0.01251369435f, + 0.01251369435f, 0.01250977721f, 0.01250195317f, 0.01249024551f, + 0.01247468684f, 0.01245531905f, 0.01243219618f, 0.01240538247f, + 0.01237494871f, 0.01234097779f, 0.01230355818f, 0.01226278674f, + 0.01221876871f, 0.01217161212f, 0.01212143432f, 0.01206835546f, + 0.01201249938f, 0.0119539937f, 0.01189296879f, 0.0118295569f, + 0.01176389214f, 0.01169610675f, 0.01162633486f, 0.0115547115f, + 0.01148136612f, 0.01140643191f, 0.01133003552f, 0.01125230361f, + 0.01117335912f, 0.01109332126f, 0.01101230737f, 0.01093043014f, + 0.01084779948f, 0.01076451875f, 0.01068068855f, 0.01059640758f, + 0.01051176619f, 0.01042685378f, 0.01034175418f, 0.01025654469f, + 0.01017130353f, 0.01008609962f, 0.01000100002f, 0.009916068986f, + 0.009831363335f, 0.009746940807f, 0.00966285076f, 0.009579141624f, + 0.009495858103f, 0.009413041174f, 0.009330729023f, 0.009248957038f, + 0.009167755023f, 0.009087154642f, 0.009007181972f, 0.008927859366f, + 0.008849211037f, 0.008859624155f, 0.008868678473f, 0.008876360953f, + 0.008882662281f, 0.008887572214f, 0.008891084231f, 0.008893193677f, + 0.008893896826f, 0.008893193677f, 0.008891084231f, 0.008887572214f, + 0.008882662281f, 0.008876360953f, 0.008868678473f, 0.008859624155f, + 0.008996240795f, 0.00907666795f, 0.009157754481f, 0.009239477105f, + 0.009321806021f, 0.009404712357f, 0.009488161653f, 0.009572117589f, + 0.009656541049f, 0.009741389193f, 0.009826615453f, 0.00991217047f, + 0.009998000227f, 0.01008404791f, 0.01017025113f, 0.01025654469f, + 0.01034285966f, 0.01042912249f, 0.01051525306f, 0.01060117036f, + 0.01068678591f, 0.01077201031f, 0.01085674576f, 0.01094089262f, + 0.01102434658f, 0.01110699773f, 0.01118873432f, 0.01126943901f, + 0.01134899072f, 0.01142726559f, 0.01150413603f, 0.0115794735f, + 0.01165314391f, 0.01172501314f, 0.01179494616f, 0.01186280511f, + 0.01192845311f, 0.01199175231f, 0.01205256768f, 0.01211076323f, + 0.01216620672f, 0.01221876871f, 0.01226832252f, 0.01231474802f, + 0.01235792786f, 0.01239775307f, 0.01243411843f, 0.01246692892f, + 0.01249609515f, 0.01252153981f, 0.0125431912f, 0.01256099064f, + 0.01257488597f, 0.01258483995f, 0.0125908237f, 0.01259282045f, + 0.0125908237f, 0.01258483995f, 0.01257488597f, 0.01256099064f, + 0.0125431912f, 0.01252153981f, 0.01249609515f, 0.01246692892f, + 0.01243411843f, 0.01239775307f, 0.01235792786f, 0.01231474802f, + 0.01226832252f, 0.01221876871f, 0.01216620672f, 0.01211076323f, + 0.01205256768f, 0.01199175231f, 0.01192845311f, 0.01186280511f, + 0.01179494616f, 0.01172501314f, 0.01165314391f, 0.0115794735f, + 0.01150413603f, 0.01142726559f, 0.01134899072f, 0.01126943901f, + 0.01118873432f, 0.01110699773f, 0.01102434658f, 0.01094089262f, + 0.01085674576f, 0.01077201031f, 0.01068678591f, 0.01060117036f, + 0.01051525306f, 0.01042912249f, 0.01034285966f, 0.01025654469f, + 0.01017025113f, 0.01008404791f, 0.009998000227f, 0.00991217047f, + 0.009826615453f, 0.009741389193f, 0.009656541049f, 0.009572117589f, + 0.009488161653f, 0.009404712357f, 0.009321806021f, 0.009239477105f, + 0.009157754481f, 0.00907666795f, 0.008996240795f, 0.008916495368f, + 0.008927859366f, 0.008937839419f, 0.008946419694f, 0.008953588083f, + 0.008959336206f, 0.008963654749f, 0.008966536261f, 0.00896797888f, + 0.00896797888f, 0.008966536261f, 0.008963654749f, 0.008959336206f, + 0.008953588083f, 0.008946419694f, 0.008937839419f, 0.008927859366f, + 0.009064726532f, 0.009146256372f, 0.009228453971f, 0.009311294183f, + 0.009394746274f, 0.00947877951f, 0.009563359432f, 0.009648446925f, + 0.009734002873f, 0.009819980711f, 0.00990633294f, 0.009993007407f, + 0.01007994823f, 0.01016709674f, 0.01025438774f, 0.01034175418f, + 0.01042912249f, 0.01051641535f, 0.01060355362f, 0.01069044974f, + 0.01077701338f, 0.01086314954f, 0.0109487595f, 0.01103373803f, + 0.01111797616f, 0.01120136213f, 0.01128377859f, 0.01136510447f, + 0.01144521404f, 0.01152398065f, 0.01160127111f, 0.01167695317f, + 0.01175088901f, 0.01182294171f, 0.01189296879f, 0.0119608324f, + 0.01202639099f, 0.01208950393f, 0.01215003151f, 0.01220783778f, + 0.01226278674f, 0.01231474802f, 0.01236359403f, 0.01240920182f, + 0.01245145593f, 0.01249024551f, 0.01252546813f, 0.01255702879f, + 0.01258483995f, 0.01260882709f, 0.01262892038f, 0.01264506485f, + 0.01265721396f, 0.0126653323f, 0.01266939752f, 0.01266939752f, + 0.0126653323f, 0.01265721396f, 0.01264506485f, 0.01262892038f, + 0.01260882709f, 0.01258483995f, 0.01255702879f, 0.01252546813f, + 0.01249024551f, 0.01245145593f, 0.01240920182f, 0.01236359403f, + 0.01231474802f, 0.01226278674f, 0.01220783778f, 0.01215003151f, + 0.01208950393f, 0.01202639099f, 0.0119608324f, 0.01189296879f, + 0.01182294171f, 0.01175088901f, 0.01167695317f, 0.01160127111f, + 0.01152398065f, 0.01144521404f, 0.01136510447f, 0.01128377859f, + 0.01120136213f, 0.01111797616f, 0.01103373803f, 0.0109487595f, + 0.01086314954f, 0.01077701338f, 0.01069044974f, 0.01060355362f, + 0.01051641535f, 0.01042912249f, 0.01034175418f, 0.01025438774f, + 0.01016709674f, 0.01007994823f, 0.009993007407f, 0.00990633294f, + 0.009819980711f, 0.009734002873f, 0.009648446925f, 0.009563359432f, + 0.00947877951f, 0.009394746274f, 0.009311294183f, 0.009228453971f, + 0.009146256372f, 0.009064726532f, 0.008983888663f, 0.008996240795f, + 0.009007181972f, 0.009016696364f, 0.00902477093f, 0.009031393565f, + 0.009036554955f, 0.009040246718f, 0.009042463265f, 0.009043202735f, + 0.009042463265f, 0.009040246718f, 0.009036554955f, 0.009031393565f, + 0.00902477093f, 0.009016696364f, 0.009007181972f, 0.008996240795f, + 0.009133277461f, 0.009215905331f, 0.009299207479f, 0.009383158758f, + 0.009467727505f, 0.00955288019f, 0.009638582356f, 0.009724793024f, + 0.009811468422f, 0.009898564778f, 0.009986029007f, 0.01007380895f, + 0.01016184594f, 0.01025007758f, 0.01033843681f, 0.01042685378f, + 0.01051525306f, 0.01060355362f, 0.01069167163f, 0.0107795177f, + 0.01086699776f, 0.01095401309f, 0.01104046032f, 0.0111262314f, + 0.01121121366f, 0.01129528973f, 0.01137833856f, 0.01146023627f, + 0.01154085156f, 0.01162005402f, 0.01169770677f, 0.01177367195f, + 0.01184780896f, 0.01191997528f, 0.01199002843f, 0.01205782313f, + 0.01212321594f, 0.01218606345f, 0.0122462241f, 0.01230355818f, + 0.01235792786f, 0.01240920182f, 0.01245725155f, 0.01250195317f, + 0.0125431912f, 0.01258085575f, 0.01261484437f, 0.01264506485f, + 0.01267143246f, 0.01269387174f, 0.01271232124f, 0.01272672601f, + 0.01273704506f, 0.0127432486f, 0.01274531893f, 0.0127432486f, + 0.01273704506f, 0.01272672601f, 0.01271232124f, 0.01269387174f, + 0.01267143246f, 0.01264506485f, 0.01261484437f, 0.01258085575f, + 0.0125431912f, 0.01250195317f, 0.01245725155f, 0.01240920182f, + 0.01235792786f, 0.01230355818f, 0.0122462241f, 0.01218606345f, + 0.01212321594f, 0.01205782313f, 0.01199002843f, 0.01191997528f, + 0.01184780896f, 0.01177367195f, 0.01169770677f, 0.01162005402f, + 0.01154085156f, 0.01146023627f, 0.01137833856f, 0.01129528973f, + 0.01121121366f, 0.0111262314f, 0.01104046032f, 0.01095401309f, + 0.01086699776f, 0.0107795177f, 0.01069167163f, 0.01060355362f, + 0.01051525306f, 0.01042685378f, 0.01033843681f, 0.01025007758f, + 0.01016184594f, 0.01007380895f, 0.009986029007f, 0.009898564778f, + 0.009811468422f, 0.009724793024f, 0.009638582356f, 0.00955288019f, + 0.009467727505f, 0.009383158758f, 0.009299207479f, 0.009215905331f, + 0.009133277461f, 0.009051349014f, 0.009064726532f, 0.00907666795f, + 0.009087154642f, 0.009096172638f, 0.009103707969f, 0.00910975039f, + 0.009114289656f, 0.00911732018f, 0.009118835442f, 0.009118835442f, + 0.00911732018f, 0.009114289656f, 0.00910975039f, 0.009103707969f, + 0.009096172638f, 0.009087154642f, 0.00907666795f, 0.009064726532f, + 0.009201847948f, 0.009285567328f, 0.009369968437f, 0.009455023333f, + 0.009540699422f, 0.009626962245f, 0.009713775478f, 0.009801096283f, + 0.009888879955f, 0.009977078997f, 0.01006564032f, 0.01015450899f, + 0.01024362259f, 0.01033291686f, 0.0104223229f, 0.01051176619f, + 0.01060117036f, 0.01069044974f, 0.0107795177f, 0.01086828113f, + 0.01095664315f, 0.01104449946f, 0.01113174483f, 0.01121826563f, + 0.01130394638f, 0.01138866507f, 0.01147229597f, 0.0115547115f, + 0.01163577568f, 0.0117153544f, 0.0117933061f, 0.01186948828f, + 0.01194375753f, 0.01201596763f, 0.01208597142f, 0.0121536199f, + 0.01221876871f, 0.01228126884f, 0.01234097779f, 0.01239775307f, + 0.01245145593f, 0.01250195317f, 0.01254911628f, 0.01259282045f, + 0.01263295114f, 0.01266939752f, 0.01270206179f, 0.01273085084f, + 0.01275568269f, 0.01277648844f, 0.01279320568f, 0.01280578785f, + 0.01281419583f, 0.01281840634f, 0.01281840634f, 0.01281419583f, + 0.01280578785f, 0.01279320568f, 0.01277648844f, 0.01275568269f, + 0.01273085084f, 0.01270206179f, 0.01266939752f, 0.01263295114f, + 0.01259282045f, 0.01254911628f, 0.01250195317f, 0.01245145593f, + 0.01239775307f, 0.01234097779f, 0.01228126884f, 0.01221876871f, + 0.0121536199f, 0.01208597142f, 0.01201596763f, 0.01194375753f, + 0.01186948828f, 0.0117933061f, 0.0117153544f, 0.01163577568f, + 0.0115547115f, 0.01147229597f, 0.01138866507f, 0.01130394638f, + 0.01121826563f, 0.01113174483f, 0.01104449946f, 0.01095664315f, + 0.01086828113f, 0.0107795177f, 0.01069044974f, 0.01060117036f, + 0.01051176619f, 0.0104223229f, 0.01033291686f, 0.01024362259f, + 0.01015450899f, 0.01006564032f, 0.009977078997f, 0.009888879955f, + 0.009801096283f, 0.009713775478f, 0.009626962245f, 0.009540699422f, + 0.009455023333f, 0.009369968437f, 0.009285567328f, 0.009201847948f, + 0.009118835442f, 0.009133277461f, 0.009146256372f, 0.009157754481f, + 0.009167755023f, 0.009176243097f, 0.009183204733f, 0.009188630618f, + 0.009192512371f, 0.009194843471f, 0.009195621125f, 0.009194843471f, + 0.009192512371f, 0.009188630618f, 0.009183204733f, 0.009176243097f, + 0.009167755023f, 0.009157754481f, 0.009146256372f, 0.009133277461f, + 0.00927039329f, 0.009355195798f, 0.009440686554f, 0.009526834823f, + 0.009613607079f, 0.009700968862f, 0.009788879193f, 0.009877296165f, + 0.009966172278f, 0.01005545817f, 0.01014509797f, 0.01023503393f, + 0.01032520272f, 0.01041553635f, 0.01050596405f, 0.01059640758f, + 0.01068678591f, 0.01077701338f, 0.01086699776f, 0.01095664315f, + 0.01104584709f, 0.01113450434f, 0.01122250315f, 0.01130972803f, + 0.0113960579f, 0.01148136612f, 0.01156552508f, 0.01164839976f, + 0.0117298523f, 0.01180974208f, 0.01188792568f, 0.01196425594f, + 0.01203858573f, 0.01211076323f, 0.0121806385f, 0.0122480616f, + 0.01231288072f, 0.01237494871f, 0.01243411843f, 0.01249024551f, + 0.0125431912f, 0.01259282045f, 0.01263900381f, 0.0126816174f, + 0.01272054669f, 0.01275568269f, 0.0127869295f, 0.01281419583f, + 0.01283740439f, 0.01285648718f, 0.01287138835f, 0.01288206317f, + 0.01288848184f, 0.01289062295f, 0.01288848184f, 0.01288206317f, + 0.01287138835f, 0.01285648718f, 0.01283740439f, 0.01281419583f, + 0.0127869295f, 0.01275568269f, 0.01272054669f, 0.0126816174f, + 0.01263900381f, 0.01259282045f, 0.0125431912f, 0.01249024551f, + 0.01243411843f, 0.01237494871f, 0.01231288072f, 0.0122480616f, + 0.0121806385f, 0.01211076323f, 0.01203858573f, 0.01196425594f, + 0.01188792568f, 0.01180974208f, 0.0117298523f, 0.01164839976f, + 0.01156552508f, 0.01148136612f, 0.0113960579f, 0.01130972803f, + 0.01122250315f, 0.01113450434f, 0.01104584709f, 0.01095664315f, + 0.01086699776f, 0.01077701338f, 0.01068678591f, 0.01059640758f, + 0.01050596405f, 0.01041553635f, 0.01032520272f, 0.01023503393f, + 0.01014509797f, 0.01005545817f, 0.009966172278f, 0.009877296165f, + 0.009788879193f, 0.009700968862f, 0.009613607079f, 0.009526834823f, + 0.009440686554f, 0.009355195798f, 0.00927039329f, 0.009186304174f, + 0.009201847948f, 0.009215905331f, 0.009228453971f, 0.009239477105f, + 0.009248957038f, 0.009256878868f, 0.009263231419f, 0.009268003516f, + 0.00927118957f, 0.009272783995f, 0.009272783995f, 0.00927118957f, + 0.009268003516f, 0.009263231419f, 0.009256878868f, 0.009248957038f, + 0.009239477105f, 0.009228453971f, 0.009215905331f, 0.009201847948f, + 0.009338863194f, 0.009424740449f, 0.009511308745f, 0.00959853828f, + 0.009686394595f, 0.009774839506f, 0.009863832965f, 0.009953328408f, + 0.01004327927f, 0.01013363153f, 0.01022432931f, 0.01031531021f, + 0.01040650904f, 0.01049785595f, 0.01058927551f, 0.01068068855f, + 0.01077201031f, 0.01086314954f, 0.01095401309f, 0.01104449946f, + 0.01113450434f, 0.0112239169f, 0.01131262258f, 0.01140050031f, + 0.0114874253f, 0.0115732681f, 0.01165789459f, 0.011741166f, + 0.01182294171f, 0.0119030755f, 0.01198141929f, 0.01205782313f, + 0.01213213522f, 0.01220420003f, 0.01227386575f, 0.01234097779f, + 0.01240538247f, 0.01246692892f, 0.01252546813f, 0.01258085575f, + 0.01263295114f, 0.0126816174f, 0.01272672601f, 0.01276815403f, + 0.01280578785f, 0.01283952035f, 0.01286925655f, 0.0128949089f, + 0.01291640475f, 0.01293367799f, 0.01294667926f, 0.01295536757f, + 0.0129597187f, 0.0129597187f, 0.01295536757f, 0.01294667926f, + 0.01293367799f, 0.01291640475f, 0.0128949089f, 0.01286925655f, + 0.01283952035f, 0.01280578785f, 0.01276815403f, 0.01272672601f, + 0.0126816174f, 0.01263295114f, 0.01258085575f, 0.01252546813f, + 0.01246692892f, 0.01240538247f, 0.01234097779f, 0.01227386575f, + 0.01220420003f, 0.01213213522f, 0.01205782313f, 0.01198141929f, + 0.0119030755f, 0.01182294171f, 0.011741166f, 0.01165789459f, + 0.0115732681f, 0.0114874253f, 0.01140050031f, 0.01131262258f, + 0.0112239169f, 0.01113450434f, 0.01104449946f, 0.01095401309f, + 0.01086314954f, 0.01077201031f, 0.01068068855f, 0.01058927551f, + 0.01049785595f, 0.01040650904f, 0.01031531021f, 0.01022432931f, + 0.01013363153f, 0.01004327927f, 0.009953328408f, 0.009863832965f, + 0.009774839506f, 0.009686394595f, 0.00959853828f, 0.009511308745f, + 0.009424740449f, 0.009338863194f, 0.009253707714f, 0.00927039329f, + 0.009285567328f, 0.009299207479f, 0.009311294183f, 0.009321806021f, + 0.009330729023f, 0.009338049218f, 0.0093437545f, 0.009347835556f, + 0.009350287728f, 0.009351104498f, 0.009350287728f, 0.009347835556f, + 0.0093437545f, 0.009338049218f, 0.009330729023f, 0.009321806021f, + 0.009311294183f, 0.009299207479f, 0.009285567328f, 0.00927039329f, + 0.009407208301f, 0.009494146332f, 0.009581780061f, 0.009670076892f, + 0.009759000503f, 0.009848512709f, 0.009938570671f, 0.01002912689f, + 0.01012013014f, 0.01021152735f, 0.01030325703f, 0.01039525773f, + 0.0104874596f, 0.01057978906f, 0.01067216974f, 0.01076451875f, + 0.01085674576f, 0.0109487595f, 0.01104046032f, 0.01113174483f, + 0.01122250315f, 0.01131262258f, 0.01140198205f, 0.01149045862f, + 0.01157792099f, 0.0116642369f, 0.01174926758f, 0.01183286961f, + 0.01191489771f, 0.01199520286f, 0.01207363233f, 0.01215003151f, + 0.01222424489f, 0.01229611505f, 0.01236548461f, 0.01243219618f, + 0.01249609515f, 0.01255702879f, 0.01261484437f, 0.01266939752f, + 0.01272054669f, 0.01276815403f, 0.01281209197f, 0.01285223942f, + 0.01288848184f, 0.01292071585f, 0.01294884924f, 0.0129727982f, + 0.01299249288f, 0.01300787181f, 0.01301889122f, 0.01302551571f, + 0.01302772667f, 0.01302551571f, 0.01301889122f, 0.01300787181f, + 0.01299249288f, 0.0129727982f, 0.01294884924f, 0.01292071585f, + 0.01288848184f, 0.01285223942f, 0.01281209197f, 0.01276815403f, + 0.01272054669f, 0.01266939752f, 0.01261484437f, 0.01255702879f, + 0.01249609515f, 0.01243219618f, 0.01236548461f, 0.01229611505f, + 0.01222424489f, 0.01215003151f, 0.01207363233f, 0.01199520286f, + 0.01191489771f, 0.01183286961f, 0.01174926758f, 0.0116642369f, + 0.01157792099f, 0.01149045862f, 0.01140198205f, 0.01131262258f, + 0.01122250315f, 0.01113174483f, 0.01104046032f, 0.0109487595f, + 0.01085674576f, 0.01076451875f, 0.01067216974f, 0.01057978906f, + 0.0104874596f, 0.01039525773f, 0.01030325703f, 0.01021152735f, + 0.01012013014f, 0.01002912689f, 0.009938570671f, 0.009848512709f, + 0.009759000503f, 0.009670076892f, 0.009581780061f, 0.009494146332f, + 0.009407208301f, 0.009320996702f, 0.009338863194f, 0.009355195798f, + 0.009369968437f, 0.009383158758f, 0.009394746274f, 0.009404712357f, + 0.009413041174f, 0.00941972062f, 0.009424740449f, 0.009428090416f, + 0.009429766797f, 0.009429766797f, 0.009428090416f, 0.009424740449f, + 0.00941972062f, 0.009413041174f, 0.009404712357f, 0.009394746274f, + 0.009383158758f, 0.009369968437f, 0.009355195798f, 0.009338863194f, + 0.009475374594f, 0.009563359432f, 0.00965204183f, 0.009741389193f, + 0.009831363335f, 0.009921924211f, 0.01001302525f, 0.01010461897f, + 0.01019665226f, 0.0102890674f, 0.01038180385f, 0.01047479361f, + 0.01056796685f, 0.01066124719f, 0.0107545536f, 0.01084779948f, + 0.01094089262f, 0.01103373803f, 0.0111262314f, 0.01121826563f, + 0.01130972803f, 0.01140050031f, 0.01149045862f, 0.0115794735f, + 0.01166741177f, 0.0117541356f, 0.01183950249f, 0.01192336436f, + 0.01200557221f, 0.01208597142f, 0.01216440555f, 0.01224071812f, + 0.01231474802f, 0.01238633506f, 0.01245531905f, 0.01252153981f, + 0.01258483995f, 0.01264506485f, 0.01270206179f, 0.01275568269f, + 0.01280578785f, 0.01285223942f, 0.0128949089f, 0.01293367799f, + 0.01296843402f, 0.01299907733f, 0.01302551571f, 0.01304767188f, + 0.01306547876f, 0.01307888143f, 0.01308783889f, 0.01309232507f, + 0.01309232507f, 0.01308783889f, 0.01307888143f, 0.01306547876f, + 0.01304767188f, 0.01302551571f, 0.01299907733f, 0.01296843402f, + 0.01293367799f, 0.0128949089f, 0.01285223942f, 0.01280578785f, + 0.01275568269f, 0.01270206179f, 0.01264506485f, 0.01258483995f, + 0.01252153981f, 0.01245531905f, 0.01238633506f, 0.01231474802f, + 0.01224071812f, 0.01216440555f, 0.01208597142f, 0.01200557221f, + 0.01192336436f, 0.01183950249f, 0.0117541356f, 0.01166741177f, + 0.0115794735f, 0.01149045862f, 0.01140050031f, 0.01130972803f, + 0.01121826563f, 0.0111262314f, 0.01103373803f, 0.01094089262f, + 0.01084779948f, 0.0107545536f, 0.01066124719f, 0.01056796685f, + 0.01047479361f, 0.01038180385f, 0.0102890674f, 0.01019665226f, + 0.01010461897f, 0.01001302525f, 0.009921924211f, 0.009831363335f, + 0.009741389193f, 0.00965204183f, 0.009563359432f, 0.009475374594f, + 0.009388119914f, 0.009407208301f, 0.009424740449f, 0.009440686554f, + 0.009455023333f, 0.009467727505f, 0.00947877951f, 0.009488161653f, + 0.009495858103f, 0.009501857683f, 0.009506150149f, 0.00950872805f, + 0.00950958766f, 0.00950872805f, 0.009506150149f, 0.009501857683f, + 0.009495858103f, 0.009488161653f, 0.00947877951f, 0.009467727505f, + 0.009455023333f, 0.009440686554f, 0.009424740449f, 0.009407208301f, + 0.009543305263f, 0.009632320143f, 0.009722034447f, 0.009812413715f, + 0.0099034179f, 0.009995004162f, 0.01008712593f, 0.01017973199f, + 0.01027276739f, 0.01036617346f, 0.010459885f, 0.01055383217f, + 0.01064794231f, 0.01074213628f, 0.01083632838f, 0.01093043014f, + 0.01102434658f, 0.01111797616f, 0.01121121366f, 0.01130394638f, + 0.0113960579f, 0.0114874253f, 0.01157792099f, 0.01166741177f, + 0.01175575983f, 0.01184282266f, 0.01192845311f, 0.01201249938f, + 0.01209480781f, 0.01217522006f, 0.01225357689f, 0.01232971624f, + 0.01240347326f, 0.01247468684f, 0.0125431912f, 0.01260882709f, + 0.01267143246f, 0.01273085084f, 0.0127869295f, 0.01283952035f, + 0.01288848184f, 0.01293367799f, 0.01297498215f, 0.01301227603f, + 0.0130454516f, 0.01307440922f, 0.01309906319f, 0.01311933808f, + 0.01313517336f, 0.01314651966f, 0.01315334067f, 0.01315561775f, + 0.01315334067f, 0.01314651966f, 0.01313517336f, 0.01311933808f, + 0.01309906319f, 0.01307440922f, 0.0130454516f, 0.01301227603f, + 0.01297498215f, 0.01293367799f, 0.01288848184f, 0.01283952035f, + 0.0127869295f, 0.01273085084f, 0.01267143246f, 0.01260882709f, + 0.0125431912f, 0.01247468684f, 0.01240347326f, 0.01232971624f, + 0.01225357689f, 0.01217522006f, 0.01209480781f, 0.01201249938f, + 0.01192845311f, 0.01184282266f, 0.01175575983f, 0.01166741177f, + 0.01157792099f, 0.0114874253f, 0.0113960579f, 0.01130394638f, + 0.01121121366f, 0.01111797616f, 0.01102434658f, 0.01093043014f, + 0.01083632838f, 0.01074213628f, 0.01064794231f, 0.01055383217f, + 0.010459885f, 0.01036617346f, 0.01027276739f, 0.01017973199f, + 0.01008712593f, 0.009995004162f, 0.0099034179f, 0.009812413715f, + 0.009722034447f, 0.009632320143f, 0.009543305263f, 0.009455023333f, + 0.009475374594f, 0.009494146332f, 0.009511308745f, 0.009526834823f, + 0.009540699422f, 0.00955288019f, 0.009563359432f, 0.009572117589f, + 0.009579141624f, 0.009584420361f, 0.009587943554f, 0.009589706548f, + 0.009589706548f, 0.009587943554f, 0.009584420361f, 0.009579141624f, + 0.009572117589f, 0.009563359432f, 0.00955288019f, 0.009540699422f, + 0.009526834823f, 0.009511308745f, 0.009494146332f, 0.009475374594f, + 0.009610942565f, 0.009700968862f, 0.009791694582f, 0.009883082472f, + 0.009975093417f, 0.01006768085f, 0.01016079728f, 0.01025438774f, + 0.01034839638f, 0.01044276077f, 0.01053741388f, 0.01063228305f, + 0.01072729193f, 0.01082235854f, 0.01091739535f, 0.01101230737f, + 0.01110699773f, 0.01120136213f, 0.01129528973f, 0.01138866507f, + 0.01148136612f, 0.0115732681f, 0.0116642369f, 0.0117541356f, + 0.01184282266f, 0.01193015091f, 0.01201596763f, 0.01210011914f, + 0.0121824462f, 0.01226278674f, 0.01234097779f, 0.01241685264f, + 0.01249024551f, 0.01256099064f, 0.01262892038f, 0.01269387174f, + 0.01275568269f, 0.01281419583f, 0.01286925655f, 0.01292071585f, + 0.01296843402f, 0.01301227603f, 0.01305211708f, 0.01308783889f, + 0.01311933808f, 0.01314651966f, 0.01316929981f, 0.01318760961f, + 0.01320139226f, 0.0132106049f, 0.01321521774f, 0.01321521774f, + 0.0132106049f, 0.01320139226f, 0.01318760961f, 0.01316929981f, + 0.01314651966f, 0.01311933808f, 0.01308783889f, 0.01305211708f, + 0.01301227603f, 0.01296843402f, 0.01292071585f, 0.01286925655f, + 0.01281419583f, 0.01275568269f, 0.01269387174f, 0.01262892038f, + 0.01256099064f, 0.01249024551f, 0.01241685264f, 0.01234097779f, + 0.01226278674f, 0.0121824462f, 0.01210011914f, 0.01201596763f, + 0.01193015091f, 0.01184282266f, 0.0117541356f, 0.0116642369f, + 0.0115732681f, 0.01148136612f, 0.01138866507f, 0.01129528973f, + 0.01120136213f, 0.01110699773f, 0.01101230737f, 0.01091739535f, + 0.01082235854f, 0.01072729193f, 0.01063228305f, 0.01053741388f, + 0.01044276077f, 0.01034839638f, 0.01025438774f, 0.01016079728f, + 0.01006768085f, 0.009975093417f, 0.009883082472f, 0.009791694582f, + 0.009700968862f, 0.009610942565f, 0.009521651082f, 0.009543305263f, + 0.009563359432f, 0.009581780061f, 0.00959853828f, 0.009613607079f, + 0.009626962245f, 0.009638582356f, 0.009648446925f, 0.009656541049f, + 0.00966285076f, 0.00966736488f, 0.009670076892f, 0.009670981206f, + 0.009670076892f, 0.00966736488f, 0.00966285076f, 0.009656541049f, + 0.009648446925f, 0.009638582356f, 0.009626962245f, 0.009613607079f, + 0.00959853828f, 0.009581780061f, 0.009563359432f, 0.009543305263f, + 0.009678225033f, 0.009769240394f, 0.009860955179f, 0.009953328408f, + 0.01004632004f, 0.01013988163f, 0.01023396198f, 0.01032850612f, + 0.01042345539f, 0.01051874273f, 0.01061430015f, 0.01071005128f, + 0.0108059179f, 0.0109018134f, 0.01099764649f, 0.01109332126f, + 0.01118873432f, 0.01128377859f, 0.01137833856f, 0.01147229597f, + 0.01156552508f, 0.01165789459f, 0.01174926758f, 0.01183950249f, + 0.01192845311f, 0.01201596763f, 0.01210189145f, 0.01218606345f, + 0.01226832252f, 0.01234850287f, 0.01242643595f, 0.01250195317f, + 0.01257488597f, 0.01264506485f, 0.01271232124f, 0.01277648844f, + 0.01283740439f, 0.0128949089f, 0.01294884924f, 0.01299907733f, + 0.0130454516f, 0.01308783889f, 0.01312611811f, 0.01316017378f, + 0.01318990346f, 0.01321521774f, 0.01323603839f, 0.01325230021f, + 0.01326395292f, 0.01327095926f, 0.01327329688f, 0.01327095926f, + 0.01326395292f, 0.01325230021f, 0.01323603839f, 0.01321521774f, + 0.01318990346f, 0.01316017378f, 0.01312611811f, 0.01308783889f, + 0.0130454516f, 0.01299907733f, 0.01294884924f, 0.0128949089f, + 0.01283740439f, 0.01277648844f, 0.01271232124f, 0.01264506485f, + 0.01257488597f, 0.01250195317f, 0.01242643595f, 0.01234850287f, + 0.01226832252f, 0.01218606345f, 0.01210189145f, 0.01201596763f, + 0.01192845311f, 0.01183950249f, 0.01174926758f, 0.01165789459f, + 0.01156552508f, 0.01147229597f, 0.01137833856f, 0.01128377859f, + 0.01118873432f, 0.01109332126f, 0.01099764649f, 0.0109018134f, + 0.0108059179f, 0.01071005128f, 0.01061430015f, 0.01051874273f, + 0.01042345539f, 0.01032850612f, 0.01023396198f, 0.01013988163f, + 0.01004632004f, 0.009953328408f, 0.009860955179f, 0.009769240394f, + 0.009678225033f, 0.009587943554f, 0.009610942565f, 0.009632320143f, + 0.00965204183f, 0.009670076892f, 0.009686394595f, 0.009700968862f, + 0.009713775478f, 0.009724793024f, 0.009734002873f, 0.009741389193f, + 0.009746940807f, 0.009750646539f, 0.009752500802f, 0.009752500802f, + 0.009750646539f, 0.009746940807f, 0.009741389193f, 0.009734002873f, + 0.009724793024f, 0.009713775478f, 0.009700968862f, 0.009686394595f, + 0.009670076892f, 0.00965204183f, 0.009632320143f, 0.009610942565f, + 0.009745089337f, 0.00983707048f, 0.009929747321f, 0.01002307981f, + 0.01011702232f, 0.01021152735f, 0.01030653995f, 0.01040200423f, + 0.01049785595f, 0.01059402898f, 0.01069044974f, 0.01078704093f, + 0.01088371873f, 0.0109803956f, 0.01107697561f, 0.01117335912f, + 0.01126943901f, 0.01136510447f, 0.01146023627f, 0.0115547115f, + 0.01164839976f, 0.011741166f, 0.01183286961f, 0.01192336436f, + 0.01201249938f, 0.01210011914f, 0.01218606345f, 0.01227016933f, + 0.01235227007f, 0.01243219618f, 0.01250977721f, 0.01258483995f, + 0.01265721396f, 0.01272672601f, 0.01279320568f, 0.01285648718f, + 0.01291640475f, 0.0129727982f, 0.01302551571f, 0.01307440922f, + 0.01311933808f, 0.01316017378f, 0.01319679338f, 0.013229087f, + 0.01325695775f, 0.01328031812f, 0.01329909544f, 0.01331323106f, + 0.01332267933f, 0.01332741138f, 0.01332741138f, 0.01332267933f, + 0.01331323106f, 0.01329909544f, 0.01328031812f, 0.01325695775f, + 0.013229087f, 0.01319679338f, 0.01316017378f, 0.01311933808f, + 0.01307440922f, 0.01302551571f, 0.0129727982f, 0.01291640475f, + 0.01285648718f, 0.01279320568f, 0.01272672601f, 0.01265721396f, + 0.01258483995f, 0.01250977721f, 0.01243219618f, 0.01235227007f, + 0.01227016933f, 0.01218606345f, 0.01210011914f, 0.01201249938f, + 0.01192336436f, 0.01183286961f, 0.011741166f, 0.01164839976f, + 0.0115547115f, 0.01146023627f, 0.01136510447f, 0.01126943901f, + 0.01117335912f, 0.01107697561f, 0.0109803956f, 0.01088371873f, + 0.01078704093f, 0.01069044974f, 0.01059402898f, 0.01049785595f, + 0.01040200423f, 0.01030653995f, 0.01021152735f, 0.01011702232f, + 0.01002307981f, 0.009929747321f, 0.00983707048f, 0.009745089337f, + 0.009653841145f, 0.009678225033f, 0.009700968862f, 0.009722034447f, + 0.009741389193f, 0.009759000503f, 0.009774839506f, 0.009788879193f, + 0.009801096283f, 0.009811468422f, 0.009819980711f, 0.009826615453f, + 0.009831363335f, 0.009834215976f, 0.009835166857f, 0.009834215976f, + 0.009831363335f, 0.009826615453f, 0.009819980711f, 0.009811468422f, + 0.009801096283f, 0.009788879193f, 0.009774839506f, 0.009759000503f, + 0.009741389193f, 0.009722034447f, 0.009700968862f, 0.009678225033f, + 0.009811468422f, 0.00990438927f, 0.009998000227f, 0.01009226125f, + 0.0101871239f, 0.01028253883f, 0.01037844829f, 0.01047479361f, + 0.0105715096f, 0.01066852547f, 0.01076576579f, 0.01086314954f, + 0.01096059103f, 0.01105799619f, 0.01115526911f, 0.01125230361f, + 0.01134899072f, 0.01144521404f, 0.01154085156f, 0.01163577568f, + 0.0117298523f, 0.01182294171f, 0.01191489771f, 0.01200557221f, + 0.01209480781f, 0.0121824462f, 0.01226832252f, 0.01235227007f, + 0.01243411843f, 0.01251369435f, 0.0125908237f, 0.0126653323f, + 0.01273704506f, 0.01280578785f, 0.01287138835f, 0.01293367799f, + 0.01299249288f, 0.01304767188f, 0.01309906319f, 0.01314651966f, + 0.01318990346f, 0.013229087f, 0.01326395292f, 0.0132943932f, + 0.01332031563f, 0.01334163733f, 0.01335829217f, 0.01337022707f, + 0.01337740291f, 0.01337979734f, 0.01337740291f, 0.01337022707f, + 0.01335829217f, 0.01334163733f, 0.01332031563f, 0.0132943932f, + 0.01326395292f, 0.013229087f, 0.01318990346f, 0.01314651966f, + 0.01309906319f, 0.01304767188f, 0.01299249288f, 0.01293367799f, + 0.01287138835f, 0.01280578785f, 0.01273704506f, 0.0126653323f, + 0.0125908237f, 0.01251369435f, 0.01243411843f, 0.01235227007f, + 0.01226832252f, 0.0121824462f, 0.01209480781f, 0.01200557221f, + 0.01191489771f, 0.01182294171f, 0.0117298523f, 0.01163577568f, + 0.01154085156f, 0.01144521404f, 0.01134899072f, 0.01125230361f, + 0.01115526911f, 0.01105799619f, 0.01096059103f, 0.01086314954f, + 0.01076576579f, 0.01066852547f, 0.0105715096f, 0.01047479361f, + 0.01037844829f, 0.01028253883f, 0.0101871239f, 0.01009226125f, + 0.009998000227f, 0.00990438927f, 0.009811468422f, 0.009719279595f, + 0.009745089337f, 0.009769240394f, 0.009791694582f, 0.009812413715f, + 0.009831363335f, 0.009848512709f, 0.009863832965f, 0.009877296165f, + 0.009888879955f, 0.009898564778f, 0.00990633294f, 0.00991217047f, + 0.009916068986f, 0.009918019176f, 0.009918019176f, 0.009916068986f, + 0.00991217047f, 0.00990633294f, 0.009898564778f, 0.009888879955f, + 0.009877296165f, 0.009863832965f, 0.009848512709f, 0.009831363335f, + 0.009812413715f, 0.009791694582f, 0.009769240394f, 0.009745089337f, + 0.009877296165f, 0.009971125983f, 0.01006564032f, 0.01016079728f, + 0.01025654469f, 0.01035283227f, 0.01044960041f, 0.01054678671f, + 0.01064432319f, 0.01074213628f, 0.01084014867f, 0.01093827467f, + 0.0110364249f, 0.01113450434f, 0.01123241056f, 0.01133003552f, + 0.01142726559f, 0.01152398065f, 0.01162005402f, 0.0117153544f, + 0.01180974208f, 0.0119030755f, 0.01199520286f, 0.01208597142f, + 0.01217522006f, 0.01226278674f, 0.01234850287f, 0.01243219618f, + 0.01251369435f, 0.01259282045f, 0.01266939752f, 0.0127432486f, + 0.01281419583f, 0.01288206317f, 0.01294667926f, 0.01300787181f, + 0.01306547876f, 0.01311933808f, 0.01316929981f, 0.01321521774f, + 0.01325695775f, 0.0132943932f, 0.01332741138f, 0.01335590892f, + 0.01337979734f, 0.01339900028f, 0.0134134572f, 0.01342312153f, + 0.01342796069f, 0.01342796069f, 0.01342312153f, 0.0134134572f, + 0.01339900028f, 0.01337979734f, 0.01335590892f, 0.01332741138f, + 0.0132943932f, 0.01325695775f, 0.01321521774f, 0.01316929981f, + 0.01311933808f, 0.01306547876f, 0.01300787181f, 0.01294667926f, + 0.01288206317f, 0.01281419583f, 0.0127432486f, 0.01266939752f, + 0.01259282045f, 0.01251369435f, 0.01243219618f, 0.01234850287f, + 0.01226278674f, 0.01217522006f, 0.01208597142f, 0.01199520286f, + 0.0119030755f, 0.01180974208f, 0.0117153544f, 0.01162005402f, + 0.01152398065f, 0.01142726559f, 0.01133003552f, 0.01123241056f, + 0.01113450434f, 0.0110364249f, 0.01093827467f, 0.01084014867f, + 0.01074213628f, 0.01064432319f, 0.01054678671f, 0.01044960041f, + 0.01035283227f, 0.01025654469f, 0.01016079728f, 0.01006564032f, + 0.009971125983f, 0.009877296165f, 0.009784192778f, 0.009811468422f, + 0.00983707048f, 0.009860955179f, 0.009883082472f, 0.0099034179f, + 0.009921924211f, 0.009938570671f, 0.009953328408f, 0.009966172278f, + 0.009977078997f, 0.009986029007f, 0.009993007407f, 0.009998000227f, + 0.01000100002f, 0.01000200026f, 0.01000100002f, 0.009998000227f, + 0.009993007407f, 0.009986029007f, 0.009977078997f, 0.009966172278f, + 0.009953328408f, 0.009938570671f, 0.009921924211f, 0.0099034179f, + 0.009883082472f, 0.009860955179f, 0.00983707048f, 0.009811468422f, + 0.009942499921f, 0.01003720704f, 0.01013259124f, 0.01022860687f, + 0.01032520272f, 0.0104223229f, 0.01051990688f, 0.01061788946f, + 0.01071619987f, 0.01081476174f, 0.01091349311f, 0.01101230737f, + 0.01111111138f, 0.01120980456f, 0.01130828168f, 0.01140643191f, + 0.01150413603f, 0.01160127111f, 0.01169770677f, 0.0117933061f, + 0.01188792568f, 0.01198141929f, 0.01207363233f, 0.01216440555f, + 0.01225357689f, 0.01234097779f, 0.01242643595f, 0.01250977721f, + 0.0125908237f, 0.01266939752f, 0.01274531893f, 0.01281840634f, + 0.01288848184f, 0.01295536757f, 0.01301889122f, 0.01307888143f, + 0.01313517336f, 0.01318760961f, 0.01323603839f, 0.01328031812f, + 0.01332031563f, 0.01335590892f, 0.01338698901f, 0.0134134572f, + 0.01343523059f, 0.0134522384f, 0.01346442662f, 0.01347175613f, + 0.01347420178f, 0.01347175613f, 0.01346442662f, 0.0134522384f, + 0.01343523059f, 0.0134134572f, 0.01338698901f, 0.01335590892f, + 0.01332031563f, 0.01328031812f, 0.01323603839f, 0.01318760961f, + 0.01313517336f, 0.01307888143f, 0.01301889122f, 0.01295536757f, + 0.01288848184f, 0.01281840634f, 0.01274531893f, 0.01266939752f, + 0.0125908237f, 0.01250977721f, 0.01242643595f, 0.01234097779f, + 0.01225357689f, 0.01216440555f, 0.01207363233f, 0.01198141929f, + 0.01188792568f, 0.0117933061f, 0.01169770677f, 0.01160127111f, + 0.01150413603f, 0.01140643191f, 0.01130828168f, 0.01120980456f, + 0.01111111138f, 0.01101230737f, 0.01091349311f, 0.01081476174f, + 0.01071619987f, 0.01061788946f, 0.01051990688f, 0.0104223229f, + 0.01032520272f, 0.01022860687f, 0.01013259124f, 0.01003720704f, + 0.009942499921f, 0.009848512709f, 0.009877296165f, 0.00990438927f, + 0.009929747321f, 0.009953328408f, 0.009975093417f, 0.009995004162f, + 0.01001302525f, 0.01002912689f, 0.01004327927f, 0.01005545817f, + 0.01006564032f, 0.01007380895f, 0.01007994823f, 0.01008404791f, + 0.01008609962f, 0.01008609962f, 0.01008404791f, 0.01007994823f, + 0.01007380895f, 0.01006564032f, 0.01005545817f, 0.01004327927f, + 0.01002912689f, 0.01001302525f, 0.009995004162f, 0.009975093417f, + 0.009953328408f, 0.009929747321f, 0.00990438927f, 0.009877296165f, + 0.01000700705f, 0.01010255609f, 0.01019877382f, 0.01029560994f, + 0.01039301138f, 0.01049092133f, 0.01058927551f, 0.01068800688f, + 0.01078704093f, 0.01088629849f, 0.01098569483f, 0.01108513959f, + 0.01118453499f, 0.01128377859f, 0.01138276048f, 0.01148136612f, + 0.0115794735f, 0.01167695317f, 0.01177367195f, 0.01186948828f, + 0.01196425594f, 0.01205782313f, 0.01215003151f, 0.01224071812f, + 0.01232971624f, 0.01241685264f, 0.01250195317f, 0.01258483995f, + 0.0126653323f, 0.0127432486f, 0.01281840634f, 0.01289062295f, + 0.0129597187f, 0.01302551571f, 0.01308783889f, 0.01314651966f, + 0.01320139226f, 0.01325230021f, 0.01329909544f, 0.01334163733f, + 0.01337979734f, 0.0134134572f, 0.01344251167f, 0.01346686855f, + 0.01348644961f, 0.01350119151f, 0.01351104677f, 0.01351598185f, + 0.01351598185f, 0.01351104677f, 0.01350119151f, 0.01348644961f, + 0.01346686855f, 0.01344251167f, 0.0134134572f, 0.01337979734f, + 0.01334163733f, 0.01329909544f, 0.01325230021f, 0.01320139226f, + 0.01314651966f, 0.01308783889f, 0.01302551571f, 0.0129597187f, + 0.01289062295f, 0.01281840634f, 0.0127432486f, 0.0126653323f, + 0.01258483995f, 0.01250195317f, 0.01241685264f, 0.01232971624f, + 0.01224071812f, 0.01215003151f, 0.01205782313f, 0.01196425594f, + 0.01186948828f, 0.01177367195f, 0.01167695317f, 0.0115794735f, + 0.01148136612f, 0.01138276048f, 0.01128377859f, 0.01118453499f, + 0.01108513959f, 0.01098569483f, 0.01088629849f, 0.01078704093f, + 0.01068800688f, 0.01058927551f, 0.01049092133f, 0.01039301138f, + 0.01029560994f, 0.01019877382f, 0.01010255609f, 0.01000700705f, + 0.00991217047f, 0.009942499921f, 0.009971125983f, 0.009998000227f, + 0.01002307981f, 0.01004632004f, 0.01006768085f, 0.01008712593f, + 0.01010461897f, 0.01012013014f, 0.01013363153f, 0.01014509797f, + 0.01015450899f, 0.01016184594f, 0.01016709674f, 0.01017025113f, + 0.01017130353f, 0.01017025113f, 0.01016709674f, 0.01016184594f, + 0.01015450899f, 0.01014509797f, 0.01013363153f, 0.01012013014f, + 0.01010461897f, 0.01008712593f, 0.01006768085f, 0.01004632004f, + 0.01002307981f, 0.009998000227f, 0.009971125983f, 0.009942499921f, + 0.01007074397f, 0.01016709674f, 0.01026410609f, 0.01036172081f, + 0.010459885f, 0.01055853814f, 0.0106576141f, 0.0107570421f, + 0.01085674576f, 0.01095664315f, 0.01105664484f, 0.01115665678f, + 0.01125658024f, 0.01135630626f, 0.01145572308f, 0.0115547115f, + 0.01165314391f, 0.01175088901f, 0.01184780896f, 0.01194375753f, + 0.01203858573f, 0.01213213522f, 0.01222424489f, 0.01231474802f, + 0.01240347326f, 0.01249024551f, 0.01257488597f, 0.01265721396f, + 0.01273704506f, 0.01281419583f, 0.01288848184f, 0.0129597187f, + 0.01302772667f, 0.01309232507f, 0.01315334067f, 0.0132106049f, + 0.01326395292f, 0.01331323106f, 0.01335829217f, 0.01339900028f, + 0.01343523059f, 0.01346686855f, 0.01349381451f, 0.01351598185f, + 0.01353329886f, 0.01354570966f, 0.01355317142f, 0.01355566178f, + 0.01355317142f, 0.01354570966f, 0.01353329886f, 0.01351598185f, + 0.01349381451f, 0.01346686855f, 0.01343523059f, 0.01339900028f, + 0.01335829217f, 0.01331323106f, 0.01326395292f, 0.0132106049f, + 0.01315334067f, 0.01309232507f, 0.01302772667f, 0.0129597187f, + 0.01288848184f, 0.01281419583f, 0.01273704506f, 0.01265721396f, + 0.01257488597f, 0.01249024551f, 0.01240347326f, 0.01231474802f, + 0.01222424489f, 0.01213213522f, 0.01203858573f, 0.01194375753f, + 0.01184780896f, 0.01175088901f, 0.01165314391f, 0.0115547115f, + 0.01145572308f, 0.01135630626f, 0.01125658024f, 0.01115665678f, + 0.01105664484f, 0.01095664315f, 0.01085674576f, 0.0107570421f, + 0.0106576141f, 0.01055853814f, 0.010459885f, 0.01036172081f, + 0.01026410609f, 0.01016709674f, 0.01007074397f, 0.009975093417f, + 0.01000700705f, 0.01003720704f, 0.01006564032f, 0.01009226125f, + 0.01011702232f, 0.01013988163f, 0.01016079728f, 0.01017973199f, + 0.01019665226f, 0.01021152735f, 0.01022432931f, 0.01023503393f, + 0.01024362259f, 0.01025007758f, 0.01025438774f, 0.01025654469f, + 0.01025654469f, 0.01025438774f, 0.01025007758f, 0.01024362259f, + 0.01023503393f, 0.01022432931f, 0.01021152735f, 0.01019665226f, + 0.01017973199f, 0.01016079728f, 0.01013988163f, 0.01011702232f, + 0.01009226125f, 0.01006564032f, 0.01003720704f, 0.01000700705f, + 0.01013363153f, 0.01023074798f, 0.01032850612f, 0.01042685378f, + 0.0105257323f, 0.01062507927f, 0.01072482392f, 0.01082489453f, + 0.01092521101f, 0.01102568675f, 0.0111262314f, 0.01122674625f, + 0.01132712793f, 0.01142726559f, 0.01152704284f, 0.01162633486f, + 0.01172501314f, 0.01182294171f, 0.01191997528f, 0.01201596763f, + 0.01211076323f, 0.01220420003f, 0.01229611505f, 0.01238633506f, + 0.01247468684f, 0.01256099064f, 0.01264506485f, 0.01272672601f, + 0.01280578785f, 0.01288206317f, 0.01295536757f, 0.01302551571f, + 0.01309232507f, 0.01315561775f, 0.01321521774f, 0.01327095926f, + 0.01332267933f, 0.01337022707f, 0.0134134572f, 0.0134522384f, + 0.01348644961f, 0.01351598185f, 0.01354074106f, 0.01356064621f, + 0.01357563306f, 0.01358565222f, 0.01359067019f, 0.01359067019f, + 0.01358565222f, 0.01357563306f, 0.01356064621f, 0.01354074106f, + 0.01351598185f, 0.01348644961f, 0.0134522384f, 0.0134134572f, + 0.01337022707f, 0.01332267933f, 0.01327095926f, 0.01321521774f, + 0.01315561775f, 0.01309232507f, 0.01302551571f, 0.01295536757f, + 0.01288206317f, 0.01280578785f, 0.01272672601f, 0.01264506485f, + 0.01256099064f, 0.01247468684f, 0.01238633506f, 0.01229611505f, + 0.01220420003f, 0.01211076323f, 0.01201596763f, 0.01191997528f, + 0.01182294171f, 0.01172501314f, 0.01162633486f, 0.01152704284f, + 0.01142726559f, 0.01132712793f, 0.01122674625f, 0.0111262314f, + 0.01102568675f, 0.01092521101f, 0.01082489453f, 0.01072482392f, + 0.01062507927f, 0.0105257323f, 0.01042685378f, 0.01032850612f, + 0.01023074798f, 0.01013363153f, 0.01003720704f, 0.01007074397f, + 0.01010255609f, 0.01013259124f, 0.01016079728f, 0.0101871239f, + 0.01021152735f, 0.01023396198f, 0.01025438774f, 0.01027276739f, + 0.0102890674f, 0.01030325703f, 0.01031531021f, 0.01032520272f, + 0.01033291686f, 0.01033843681f, 0.01034175418f, 0.01034285966f, + 0.01034175418f, 0.01033843681f, 0.01033291686f, 0.01032520272f, + 0.01031531021f, 0.01030325703f, 0.0102890674f, 0.01027276739f, + 0.01025438774f, 0.01023396198f, 0.01021152735f, 0.0101871239f, + 0.01016079728f, 0.01013259124f, 0.01010255609f, 0.01007074397f, + 0.01019559242f, 0.01029342785f, 0.01039188914f, 0.01049092133f, + 0.01059046388f, 0.01069044974f, 0.01079080813f, 0.01089146268f, + 0.01099232957f, 0.01109332126f, 0.01119434182f, 0.01129528973f, + 0.0113960579f, 0.01149653178f, 0.01159659028f, 0.01169610675f, + 0.01179494616f, 0.01189296879f, 0.01199002843f, 0.01208597142f, + 0.0121806385f, 0.01227386575f, 0.01236548461f, 0.01245531905f, + 0.0125431912f, 0.01262892038f, 0.01271232124f, 0.01279320568f, + 0.01287138835f, 0.01294667926f, 0.01301889122f, 0.01308783889f, + 0.01315334067f, 0.01321521774f, 0.01327329688f, 0.01332741138f, + 0.01337740291f, 0.01342312153f, 0.01346442662f, 0.01350119151f, + 0.01353329886f, 0.01356064621f, 0.01358314604f, 0.01360072289f, + 0.01361331902f, 0.0136208944f, 0.01362342201f, 0.0136208944f, + 0.01361331902f, 0.01360072289f, 0.01358314604f, 0.01356064621f, + 0.01353329886f, 0.01350119151f, 0.01346442662f, 0.01342312153f, + 0.01337740291f, 0.01332741138f, 0.01327329688f, 0.01321521774f, + 0.01315334067f, 0.01308783889f, 0.01301889122f, 0.01294667926f, + 0.01287138835f, 0.01279320568f, 0.01271232124f, 0.01262892038f, + 0.0125431912f, 0.01245531905f, 0.01236548461f, 0.01227386575f, + 0.0121806385f, 0.01208597142f, 0.01199002843f, 0.01189296879f, + 0.01179494616f, 0.01169610675f, 0.01159659028f, 0.01149653178f, + 0.0113960579f, 0.01129528973f, 0.01119434182f, 0.01109332126f, + 0.01099232957f, 0.01089146268f, 0.01079080813f, 0.01069044974f, + 0.01059046388f, 0.01049092133f, 0.01039188914f, 0.01029342785f, + 0.01019559242f, 0.01009843498f, 0.01013363153f, 0.01016709674f, + 0.01019877382f, 0.01022860687f, 0.01025654469f, 0.01028253883f, + 0.01030653995f, 0.01032850612f, 0.01034839638f, 0.01036617346f, + 0.01038180385f, 0.01039525773f, 0.01040650904f, 0.01041553635f, + 0.0104223229f, 0.01042685378f, 0.01042912249f, 0.01042912249f, + 0.01042685378f, 0.0104223229f, 0.01041553635f, 0.01040650904f, + 0.01039525773f, 0.01038180385f, 0.01036617346f, 0.01034839638f, + 0.01032850612f, 0.01030653995f, 0.01028253883f, 0.01025654469f, + 0.01022860687f, 0.01019877382f, 0.01016709674f, 0.01013363153f, + 0.01025654469f, 0.01035505254f, 0.01045416761f, 0.01055383217f, + 0.0106539838f, 0.0107545536f, 0.01085546613f, 0.01095664315f, + 0.01105799619f, 0.01115943585f, 0.01126086153f, 0.01136216894f, + 0.01146324724f, 0.01156397816f, 0.0116642369f, 0.01176389214f, + 0.01186280511f, 0.0119608324f, 0.01205782313f, 0.0121536199f, + 0.0122480616f, 0.01234097779f, 0.01243219618f, 0.01252153981f, + 0.01260882709f, 0.01269387174f, 0.01277648844f, 0.01285648718f, + 0.01293367799f, 0.01300787181f, 0.01307888143f, 0.01314651966f, + 0.0132106049f, 0.01327095926f, 0.01332741138f, 0.01337979734f, + 0.01342796069f, 0.01347175613f, 0.01351104677f, 0.01354570966f, + 0.01357563306f, 0.01360072289f, 0.0136208944f, 0.01363608148f, + 0.01364623569f, 0.01365132071f, 0.01365132071f, 0.01364623569f, + 0.01363608148f, 0.0136208944f, 0.01360072289f, 0.01357563306f, + 0.01354570966f, 0.01351104677f, 0.01347175613f, 0.01342796069f, + 0.01337979734f, 0.01332741138f, 0.01327095926f, 0.0132106049f, + 0.01314651966f, 0.01307888143f, 0.01300787181f, 0.01293367799f, + 0.01285648718f, 0.01277648844f, 0.01269387174f, 0.01260882709f, + 0.01252153981f, 0.01243219618f, 0.01234097779f, 0.0122480616f, + 0.0121536199f, 0.01205782313f, 0.0119608324f, 0.01186280511f, + 0.01176389214f, 0.0116642369f, 0.01156397816f, 0.01146324724f, + 0.01136216894f, 0.01126086153f, 0.01115943585f, 0.01105799619f, + 0.01095664315f, 0.01085546613f, 0.0107545536f, 0.0106539838f, + 0.01055383217f, 0.01045416761f, 0.01035505254f, 0.01025654469f, + 0.01015869901f, 0.01019559242f, 0.01023074798f, 0.01026410609f, + 0.01029560994f, 0.01032520272f, 0.01035283227f, 0.01037844829f, + 0.01040200423f, 0.01042345539f, 0.01044276077f, 0.010459885f, + 0.01047479361f, 0.0104874596f, 0.01049785595f, 0.01050596405f, + 0.01051176619f, 0.01051525306f, 0.01051641535f, 0.01051525306f, + 0.01051176619f, 0.01050596405f, 0.01049785595f, 0.0104874596f, + 0.01047479361f, 0.010459885f, 0.01044276077f, 0.01042345539f, + 0.01040200423f, 0.01037844829f, 0.01035283227f, 0.01032520272f, + 0.01029560994f, 0.01026410609f, 0.01023074798f, 0.01019559242f, + 0.01031640731f, 0.01041553635f, 0.01051525306f, 0.01061549596f, + 0.01071619987f, 0.01081729215f, 0.01091869641f, 0.01102032885f, + 0.01112210099f, 0.0112239169f, 0.01132567506f, 0.01142726559f, + 0.01152857486f, 0.01162947994f, 0.0117298523f, 0.0118295569f, + 0.01192845311f, 0.01202639099f, 0.01212321594f, 0.01221876871f, + 0.01231288072f, 0.01240538247f, 0.01249609515f, 0.01258483995f, + 0.01267143246f, 0.01275568269f, 0.01283740439f, 0.01291640475f, + 0.01299249288f, 0.01306547876f, 0.01313517336f, 0.01320139226f, + 0.01326395292f, 0.01332267933f, 0.01337740291f, 0.01342796069f, + 0.01347420178f, 0.01351598185f, 0.01355317142f, 0.01358565222f, + 0.01361331902f, 0.01363608148f, 0.01365386508f, 0.01366661023f, + 0.01367427502f, 0.01367683243f, 0.01367427502f, 0.01366661023f, + 0.01365386508f, 0.01363608148f, 0.01361331902f, 0.01358565222f, + 0.01355317142f, 0.01351598185f, 0.01347420178f, 0.01342796069f, + 0.01337740291f, 0.01332267933f, 0.01326395292f, 0.01320139226f, + 0.01313517336f, 0.01306547876f, 0.01299249288f, 0.01291640475f, + 0.01283740439f, 0.01275568269f, 0.01267143246f, 0.01258483995f, + 0.01249609515f, 0.01240538247f, 0.01231288072f, 0.01221876871f, + 0.01212321594f, 0.01202639099f, 0.01192845311f, 0.0118295569f, + 0.0117298523f, 0.01162947994f, 0.01152857486f, 0.01142726559f, + 0.01132567506f, 0.0112239169f, 0.01112210099f, 0.01102032885f, + 0.01091869641f, 0.01081729215f, 0.01071619987f, 0.01061549596f, + 0.01051525306f, 0.01041553635f, 0.01031640731f, 0.01021792181f, + 0.01025654469f, 0.01029342785f, 0.01032850612f, 0.01036172081f, + 0.01039301138f, 0.0104223229f, 0.01044960041f, 0.01047479361f, + 0.01049785595f, 0.01051874273f, 0.01053741388f, 0.01055383217f, + 0.01056796685f, 0.01057978906f, 0.01058927551f, 0.01059640758f, + 0.01060117036f, 0.01060355362f, 0.01060355362f, 0.01060117036f, + 0.01059640758f, 0.01058927551f, 0.01057978906f, 0.01056796685f, + 0.01055383217f, 0.01053741388f, 0.01051874273f, 0.01049785595f, + 0.01047479361f, 0.01044960041f, 0.0104223229f, 0.01039301138f, + 0.01036172081f, 0.01032850612f, 0.01029342785f, 0.01025654469f, + 0.01037509646f, 0.01047479361f, 0.01057505608f, 0.01067581866f, + 0.01077701338f, 0.01087856572f, 0.0109803956f, 0.0110824164f, + 0.01118453499f, 0.01128665265f, 0.01138866507f, 0.01149045862f, + 0.01159191411f, 0.01169290766f, 0.0117933061f, 0.01189296879f, + 0.01199175231f, 0.01208950393f, 0.01218606345f, 0.01228126884f, + 0.01237494871f, 0.01246692892f, 0.01255702879f, 0.01264506485f, + 0.01273085084f, 0.01281419583f, 0.0128949089f, 0.0129727982f, + 0.01304767188f, 0.01311933808f, 0.01318760961f, 0.01325230021f, + 0.01331323106f, 0.01337022707f, 0.01342312153f, 0.01347175613f, + 0.01351598185f, 0.01355566178f, 0.01359067019f, 0.0136208944f, + 0.01364623569f, 0.01366661023f, 0.01368195191f, 0.01369220857f, + 0.01369734481f, 0.01369734481f, 0.01369220857f, 0.01368195191f, + 0.01366661023f, 0.01364623569f, 0.0136208944f, 0.01359067019f, + 0.01355566178f, 0.01351598185f, 0.01347175613f, 0.01342312153f, + 0.01337022707f, 0.01331323106f, 0.01325230021f, 0.01318760961f, + 0.01311933808f, 0.01304767188f, 0.0129727982f, 0.0128949089f, + 0.01281419583f, 0.01273085084f, 0.01264506485f, 0.01255702879f, + 0.01246692892f, 0.01237494871f, 0.01228126884f, 0.01218606345f, + 0.01208950393f, 0.01199175231f, 0.01189296879f, 0.0117933061f, + 0.01169290766f, 0.01159191411f, 0.01149045862f, 0.01138866507f, + 0.01128665265f, 0.01118453499f, 0.0110824164f, 0.0109803956f, + 0.01087856572f, 0.01077701338f, 0.01067581866f, 0.01057505608f, + 0.01047479361f, 0.01037509646f, 0.01027602144f, 0.01031640731f, + 0.01035505254f, 0.01039188914f, 0.01042685378f, 0.010459885f, + 0.01049092133f, 0.01051990688f, 0.01054678671f, 0.0105715096f, + 0.01059402898f, 0.01061430015f, 0.01063228305f, 0.01064794231f, + 0.01066124719f, 0.01067216974f, 0.01068068855f, 0.01068678591f, + 0.01069044974f, 0.01069167163f, 0.01069044974f, 0.01068678591f, + 0.01068068855f, 0.01067216974f, 0.01066124719f, 0.01064794231f, + 0.01063228305f, 0.01061430015f, 0.01059402898f, 0.0105715096f, + 0.01054678671f, 0.01051990688f, 0.01049092133f, 0.010459885f, + 0.01042685378f, 0.01039188914f, 0.01035505254f, 0.01031640731f, + 0.01043252647f, 0.01053273678f, 0.01063348539f, 0.01073470619f, + 0.01083632838f, 0.01093827467f, 0.01104046032f, 0.01114279591f, + 0.01124518644f, 0.01134752948f, 0.01144971419f, 0.01155162696f, + 0.01165314391f, 0.0117541356f, 0.01185446698f, 0.0119539937f, + 0.01205256768f, 0.01215003151f, 0.0122462241f, 0.01234097779f, + 0.01243411843f, 0.01252546813f, 0.01261484437f, 0.01270206179f, + 0.0127869295f, 0.01286925655f, 0.01294884924f, 0.01302551571f, + 0.01309906319f, 0.01316929981f, 0.01323603839f, 0.01329909544f, + 0.01335829217f, 0.0134134572f, 0.01346442662f, 0.01351104677f, + 0.01355317142f, 0.01359067019f, 0.01362342201f, 0.01365132071f, + 0.01367427502f, 0.01369220857f, 0.01370506082f, 0.01371278986f, + 0.01371536963f, 0.01371278986f, 0.01370506082f, 0.01369220857f, + 0.01367427502f, 0.01365132071f, 0.01362342201f, 0.01359067019f, + 0.01355317142f, 0.01351104677f, 0.01346442662f, 0.0134134572f, + 0.01335829217f, 0.01329909544f, 0.01323603839f, 0.01316929981f, + 0.01309906319f, 0.01302551571f, 0.01294884924f, 0.01286925655f, + 0.0127869295f, 0.01270206179f, 0.01261484437f, 0.01252546813f, + 0.01243411843f, 0.01234097779f, 0.0122462241f, 0.01215003151f, + 0.01205256768f, 0.0119539937f, 0.01185446698f, 0.0117541356f, + 0.01165314391f, 0.01155162696f, 0.01144971419f, 0.01134752948f, + 0.01124518644f, 0.01114279591f, 0.01104046032f, 0.01093827467f, + 0.01083632838f, 0.01073470619f, 0.01063348539f, 0.01053273678f, + 0.01043252647f, 0.01033291686f, 0.01037509646f, 0.01041553635f, + 0.01045416761f, 0.01049092133f, 0.0105257323f, 0.01055853814f, + 0.01058927551f, 0.01061788946f, 0.01064432319f, 0.01066852547f, + 0.01069044974f, 0.01071005128f, 0.01072729193f, 0.01074213628f, + 0.0107545536f, 0.01076451875f, 0.01077201031f, 0.01077701338f, + 0.0107795177f, 0.0107795177f, 0.01077701338f, 0.01077201031f, + 0.01076451875f, 0.0107545536f, 0.01074213628f, 0.01072729193f, + 0.01071005128f, 0.01069044974f, 0.01066852547f, 0.01064432319f, + 0.01061788946f, 0.01058927551f, 0.01055853814f, 0.0105257323f, + 0.01049092133f, 0.01045416761f, 0.01041553635f, 0.01037509646f, + 0.01048861258f, 0.01058927551f, 0.01069044974f, 0.01079206541f, + 0.01089404803f, 0.01099631656f, 0.01109878626f, 0.01120136213f, + 0.01130394638f, 0.01140643191f, 0.01150870696f, 0.01161065139f, + 0.01171213947f, 0.01181303803f, 0.01191320643f, 0.01201249938f, + 0.01211076323f, 0.01220783778f, 0.01230355818f, 0.01239775307f, + 0.01249024551f, 0.01258085575f, 0.01266939752f, 0.01275568269f, + 0.01283952035f, 0.01292071585f, 0.01299907733f, 0.01307440922f, + 0.01314651966f, 0.01321521774f, 0.01328031812f, 0.01334163733f, + 0.01339900028f, 0.0134522384f, 0.01350119151f, 0.01354570966f, + 0.01358565222f, 0.0136208944f, 0.01365132071f, 0.01367683243f, + 0.01369734481f, 0.01371278986f, 0.01372311637f, 0.013728288f, + 0.013728288f, 0.01372311637f, 0.01371278986f, 0.01369734481f, + 0.01367683243f, 0.01365132071f, 0.0136208944f, 0.01358565222f, + 0.01354570966f, 0.01350119151f, 0.0134522384f, 0.01339900028f, + 0.01334163733f, 0.01328031812f, 0.01321521774f, 0.01314651966f, + 0.01307440922f, 0.01299907733f, 0.01292071585f, 0.01283952035f, + 0.01275568269f, 0.01266939752f, 0.01258085575f, 0.01249024551f, + 0.01239775307f, 0.01230355818f, 0.01220783778f, 0.01211076323f, + 0.01201249938f, 0.01191320643f, 0.01181303803f, 0.01171213947f, + 0.01161065139f, 0.01150870696f, 0.01140643191f, 0.01130394638f, + 0.01120136213f, 0.01109878626f, 0.01099631656f, 0.01089404803f, + 0.01079206541f, 0.01069044974f, 0.01058927551f, 0.01048861258f, + 0.01038852427f, 0.01043252647f, 0.01047479361f, 0.01051525306f, + 0.01055383217f, 0.01059046388f, 0.01062507927f, 0.0106576141f, + 0.01068800688f, 0.01071619987f, 0.01074213628f, 0.01076576579f, + 0.01078704093f, 0.0108059179f, 0.01082235854f, 0.01083632838f, + 0.01084779948f, 0.01085674576f, 0.01086314954f, 0.01086699776f, + 0.01086828113f, 0.01086699776f, 0.01086314954f, 0.01085674576f, + 0.01084779948f, 0.01083632838f, 0.01082235854f, 0.0108059179f, + 0.01078704093f, 0.01076576579f, 0.01074213628f, 0.01071619987f, + 0.01068800688f, 0.0106576141f, 0.01062507927f, 0.01059046388f, + 0.01055383217f, 0.01051525306f, 0.01047479361f, 0.01043252647f, + 0.01054326911f, 0.01064432319f, 0.01074585691f, 0.01084779948f, + 0.01095007174f, 0.01105259173f, 0.01115526911f, 0.01125800703f, + 0.01136070304f, 0.01146324724f, 0.01156552508f, 0.01166741177f, + 0.01176877879f, 0.01186948828f, 0.01196939778f, 0.01206835546f, + 0.01216620672f, 0.01226278674f, 0.01235792786f, 0.01245145593f, + 0.0125431912f, 0.01263295114f, 0.01272054669f, 0.01280578785f, + 0.01288848184f, 0.01296843402f, 0.0130454516f, 0.01311933808f, + 0.01318990346f, 0.01325695775f, 0.01332031563f, 0.01337979734f, + 0.01343523059f, 0.01348644961f, 0.01353329886f, 0.01357563306f, + 0.01361331902f, 0.01364623569f, 0.01367427502f, 0.01369734481f, + 0.01371536963f, 0.013728288f, 0.01373605616f, 0.01373864897f, + 0.01373605616f, 0.013728288f, 0.01371536963f, 0.01369734481f, + 0.01367427502f, 0.01364623569f, 0.01361331902f, 0.01357563306f, + 0.01353329886f, 0.01348644961f, 0.01343523059f, 0.01337979734f, + 0.01332031563f, 0.01325695775f, 0.01318990346f, 0.01311933808f, + 0.0130454516f, 0.01296843402f, 0.01288848184f, 0.01280578785f, + 0.01272054669f, 0.01263295114f, 0.0125431912f, 0.01245145593f, + 0.01235792786f, 0.01226278674f, 0.01216620672f, 0.01206835546f, + 0.01196939778f, 0.01186948828f, 0.01176877879f, 0.01166741177f, + 0.01156552508f, 0.01146324724f, 0.01136070304f, 0.01125800703f, + 0.01115526911f, 0.01105259173f, 0.01095007174f, 0.01084779948f, + 0.01074585691f, 0.01064432319f, 0.01054326911f, 0.01044276077f, + 0.01048861258f, 0.01053273678f, 0.01057505608f, 0.01061549596f, + 0.0106539838f, 0.01069044974f, 0.01072482392f, 0.0107570421f, + 0.01078704093f, 0.01081476174f, 0.01084014867f, 0.01086314954f, + 0.01088371873f, 0.0109018134f, 0.01091739535f, 0.01093043014f, + 0.01094089262f, 0.0109487595f, 0.01095401309f, 0.01095664315f, + 0.01095664315f, 0.01095401309f, 0.0109487595f, 0.01094089262f, + 0.01093043014f, 0.01091739535f, 0.0109018134f, 0.01088371873f, + 0.01086314954f, 0.01084014867f, 0.01081476174f, 0.01078704093f, + 0.0107570421f, 0.01072482392f, 0.01069044974f, 0.0106539838f, + 0.01061549596f, 0.01057505608f, 0.01053273678f, 0.01048861258f, + 0.01059640758f, 0.01069778763f, 0.01079961471f, 0.0109018134f, + 0.01100430358f, 0.01110699773f, 0.01120980456f, 0.01131262258f, + 0.01141534653f, 0.01151786372f, 0.01162005402f, 0.01172179077f, + 0.01182294171f, 0.01192336436f, 0.01202291343f, 0.01212143432f, + 0.01221876871f, 0.01231474802f, 0.01240920182f, 0.01250195317f, + 0.01259282045f, 0.0126816174f, 0.01276815403f, 0.01285223942f, + 0.01293367799f, 0.01301227603f, 0.01308783889f, 0.01316017378f, + 0.013229087f, 0.0132943932f, 0.01335590892f, 0.0134134572f, + 0.01346686855f, 0.01351598185f, 0.01356064621f, 0.01360072289f, + 0.01363608148f, 0.01366661023f, 0.01369220857f, 0.01371278986f, + 0.013728288f, 0.01373864897f, 0.0137438383f, 0.0137438383f, + 0.01373864897f, 0.013728288f, 0.01371278986f, 0.01369220857f, + 0.01366661023f, 0.01363608148f, 0.01360072289f, 0.01356064621f, + 0.01351598185f, 0.01346686855f, 0.0134134572f, 0.01335590892f, + 0.0132943932f, 0.013229087f, 0.01316017378f, 0.01308783889f, + 0.01301227603f, 0.01293367799f, 0.01285223942f, 0.01276815403f, + 0.0126816174f, 0.01259282045f, 0.01250195317f, 0.01240920182f, + 0.01231474802f, 0.01221876871f, 0.01212143432f, 0.01202291343f, + 0.01192336436f, 0.01182294171f, 0.01172179077f, 0.01162005402f, + 0.01151786372f, 0.01141534653f, 0.01131262258f, 0.01120980456f, + 0.01110699773f, 0.01100430358f, 0.0109018134f, 0.01079961471f, + 0.01069778763f, 0.01059640758f, 0.01049554255f, 0.01054326911f, + 0.01058927551f, 0.01063348539f, 0.01067581866f, 0.01071619987f, + 0.0107545536f, 0.01079080813f, 0.01082489453f, 0.01085674576f, + 0.01088629849f, 0.01091349311f, 0.01093827467f, 0.01096059103f, + 0.0109803956f, 0.01099764649f, 0.01101230737f, 0.01102434658f, + 0.01103373803f, 0.01104046032f, 0.01104449946f, 0.01104584709f, + 0.01104449946f, 0.01104046032f, 0.01103373803f, 0.01102434658f, + 0.01101230737f, 0.01099764649f, 0.0109803956f, 0.01096059103f, + 0.01093827467f, 0.01091349311f, 0.01088629849f, 0.01085674576f, + 0.01082489453f, 0.01079080813f, 0.0107545536f, 0.01071619987f, + 0.01067581866f, 0.01063348539f, 0.01058927551f, 0.01054326911f, + 0.01064794231f, 0.01074958127f, 0.01085163094f, 0.01095401309f, + 0.01105664484f, 0.01115943585f, 0.01126228925f, 0.01136510447f, + 0.01146776881f, 0.01157016866f, 0.01167218015f, 0.01177367195f, + 0.01187450811f, 0.0119745452f, 0.01207363233f, 0.01217161212f, + 0.01226832252f, 0.01236359403f, 0.01245725155f, 0.01254911628f, + 0.01263900381f, 0.01272672601f, 0.01281209197f, 0.0128949089f, + 0.01297498215f, 0.01305211708f, 0.01312611811f, 0.01319679338f, + 0.01326395292f, 0.01332741138f, 0.01338698901f, 0.01344251167f, + 0.01349381451f, 0.01354074106f, 0.01358314604f, 0.0136208944f, + 0.01365386508f, 0.01368195191f, 0.01370506082f, 0.01372311637f, + 0.01373605616f, 0.0137438383f, 0.01374643482f, 0.0137438383f, + 0.01373605616f, 0.01372311637f, 0.01370506082f, 0.01368195191f, + 0.01365386508f, 0.0136208944f, 0.01358314604f, 0.01354074106f, + 0.01349381451f, 0.01344251167f, 0.01338698901f, 0.01332741138f, + 0.01326395292f, 0.01319679338f, 0.01312611811f, 0.01305211708f, + 0.01297498215f, 0.0128949089f, 0.01281209197f, 0.01272672601f, + 0.01263900381f, 0.01254911628f, 0.01245725155f, 0.01236359403f, + 0.01226832252f, 0.01217161212f, 0.01207363233f, 0.0119745452f, + 0.01187450811f, 0.01177367195f, 0.01167218015f, 0.01157016866f, + 0.01146776881f, 0.01136510447f, 0.01126228925f, 0.01115943585f, + 0.01105664484f, 0.01095401309f, 0.01085163094f, 0.01074958127f, + 0.01064794231f, 0.01054678671f, 0.01059640758f, 0.01064432319f, + 0.01069044974f, 0.01073470619f, 0.01077701338f, 0.01081729215f, + 0.01085546613f, 0.01089146268f, 0.01092521101f, 0.01095664315f, + 0.01098569483f, 0.01101230737f, 0.0110364249f, 0.01105799619f, + 0.01107697561f, 0.01109332126f, 0.01110699773f, 0.01111797616f, + 0.0111262314f, 0.01113174483f, 0.01113450434f, 0.01113450434f, + 0.01113174483f, 0.0111262314f, 0.01111797616f, 0.01110699773f, + 0.01109332126f, 0.01107697561f, 0.01105799619f, 0.0110364249f, + 0.01101230737f, 0.01098569483f, 0.01095664315f, 0.01092521101f, + 0.01089146268f, 0.01085546613f, 0.01081729215f, 0.01077701338f, + 0.01073470619f, 0.01069044974f, 0.01064432319f, 0.01059640758f, + 0.01069778763f, 0.01079961471f, 0.0109018134f, 0.01100430358f, + 0.01110699773f, 0.01120980456f, 0.01131262258f, 0.01141534653f, + 0.01151786372f, 0.01162005402f, 0.01172179077f, 0.01182294171f, + 0.01192336436f, 0.01202291343f, 0.01212143432f, 0.01221876871f, + 0.01231474802f, 0.01240920182f, 0.01250195317f, 0.01259282045f, + 0.0126816174f, 0.01276815403f, 0.01285223942f, 0.01293367799f, + 0.01301227603f, 0.01308783889f, 0.01316017378f, 0.013229087f, + 0.0132943932f, 0.01335590892f, 0.0134134572f, 0.01346686855f, + 0.01351598185f, 0.01356064621f, 0.01360072289f, 0.01363608148f, + 0.01366661023f, 0.01369220857f, 0.01371278986f, 0.013728288f, + 0.01373864897f, 0.0137438383f, 0.0137438383f, 0.01373864897f, + 0.013728288f, 0.01371278986f, 0.01369220857f, 0.01366661023f, + 0.01363608148f, 0.01360072289f, 0.01356064621f, 0.01351598185f, + 0.01346686855f, 0.0134134572f, 0.01335590892f, 0.0132943932f, + 0.013229087f, 0.01316017378f, 0.01308783889f, 0.01301227603f, + 0.01293367799f, 0.01285223942f, 0.01276815403f, 0.0126816174f, + 0.01259282045f, 0.01250195317f, 0.01240920182f, 0.01231474802f, + 0.01221876871f, 0.01212143432f, 0.01202291343f, 0.01192336436f, + 0.01182294171f, 0.01172179077f, 0.01162005402f, 0.01151786372f, + 0.01141534653f, 0.01131262258f, 0.01120980456f, 0.01110699773f, + 0.01100430358f, 0.0109018134f, 0.01079961471f, 0.01069778763f, + 0.01059640758f, 0.01064794231f, 0.01069778763f, 0.01074585691f, + 0.01079206541f, 0.01083632838f, 0.01087856572f, 0.01091869641f, + 0.01095664315f, 0.01099232957f, 0.01102568675f, 0.01105664484f, + 0.01108513959f, 0.01111111138f, 0.01113450434f, 0.01115526911f, + 0.01117335912f, 0.01118873432f, 0.01120136213f, 0.01121121366f, + 0.01121826563f, 0.01122250315f, 0.0112239169f, 0.01122250315f, + 0.01121826563f, 0.01121121366f, 0.01120136213f, 0.01118873432f, + 0.01117335912f, 0.01115526911f, 0.01113450434f, 0.01111111138f, + 0.01108513959f, 0.01105664484f, 0.01102568675f, 0.01099232957f, + 0.01095664315f, 0.01091869641f, 0.01087856572f, 0.01083632838f, + 0.01079206541f, 0.01074585691f, 0.01069778763f, 0.01064794231f, + 0.01074585691f, 0.01084779948f, 0.01095007174f, 0.01105259173f, + 0.01115526911f, 0.01125800703f, 0.01136070304f, 0.01146324724f, + 0.01156552508f, 0.01166741177f, 0.01176877879f, 0.01186948828f, + 0.01196939778f, 0.01206835546f, 0.01216620672f, 0.01226278674f, + 0.01235792786f, 0.01245145593f, 0.0125431912f, 0.01263295114f, + 0.01272054669f, 0.01280578785f, 0.01288848184f, 0.01296843402f, + 0.0130454516f, 0.01311933808f, 0.01318990346f, 0.01325695775f, + 0.01332031563f, 0.01337979734f, 0.01343523059f, 0.01348644961f, + 0.01353329886f, 0.01357563306f, 0.01361331902f, 0.01364623569f, + 0.01367427502f, 0.01369734481f, 0.01371536963f, 0.013728288f, + 0.01373605616f, 0.01373864897f, 0.01373605616f, 0.013728288f, + 0.01371536963f, 0.01369734481f, 0.01367427502f, 0.01364623569f, + 0.01361331902f, 0.01357563306f, 0.01353329886f, 0.01348644961f, + 0.01343523059f, 0.01337979734f, 0.01332031563f, 0.01325695775f, + 0.01318990346f, 0.01311933808f, 0.0130454516f, 0.01296843402f, + 0.01288848184f, 0.01280578785f, 0.01272054669f, 0.01263295114f, + 0.0125431912f, 0.01245145593f, 0.01235792786f, 0.01226278674f, + 0.01216620672f, 0.01206835546f, 0.01196939778f, 0.01186948828f, + 0.01176877879f, 0.01166741177f, 0.01156552508f, 0.01146324724f, + 0.01136070304f, 0.01125800703f, 0.01115526911f, 0.01105259173f, + 0.01095007174f, 0.01084779948f, 0.01074585691f, 0.01064432319f, + 0.01069778763f, 0.01074958127f, 0.01079961471f, 0.01084779948f, + 0.01089404803f, 0.01093827467f, 0.0109803956f, 0.01102032885f, + 0.01105799619f, 0.01109332126f, 0.0111262314f, 0.01115665678f, + 0.01118453499f, 0.01120980456f, 0.01123241056f, 0.01125230361f, + 0.01126943901f, 0.01128377859f, 0.01129528973f, 0.01130394638f, + 0.01130972803f, 0.01131262258f, 0.01131262258f, 0.01130972803f, + 0.01130394638f, 0.01129528973f, 0.01128377859f, 0.01126943901f, + 0.01125230361f, 0.01123241056f, 0.01120980456f, 0.01118453499f, + 0.01115665678f, 0.0111262314f, 0.01109332126f, 0.01105799619f, + 0.01102032885f, 0.0109803956f, 0.01093827467f, 0.01089404803f, + 0.01084779948f, 0.01079961471f, 0.01074958127f, 0.01069778763f, + 0.01079206541f, 0.01089404803f, 0.01099631656f, 0.01109878626f, + 0.01120136213f, 0.01130394638f, 0.01140643191f, 0.01150870696f, + 0.01161065139f, 0.01171213947f, 0.01181303803f, 0.01191320643f, + 0.01201249938f, 0.01211076323f, 0.01220783778f, 0.01230355818f, + 0.01239775307f, 0.01249024551f, 0.01258085575f, 0.01266939752f, + 0.01275568269f, 0.01283952035f, 0.01292071585f, 0.01299907733f, + 0.01307440922f, 0.01314651966f, 0.01321521774f, 0.01328031812f, + 0.01334163733f, 0.01339900028f, 0.0134522384f, 0.01350119151f, + 0.01354570966f, 0.01358565222f, 0.0136208944f, 0.01365132071f, + 0.01367683243f, 0.01369734481f, 0.01371278986f, 0.01372311637f, + 0.013728288f, 0.013728288f, 0.01372311637f, 0.01371278986f, + 0.01369734481f, 0.01367683243f, 0.01365132071f, 0.0136208944f, + 0.01358565222f, 0.01354570966f, 0.01350119151f, 0.0134522384f, + 0.01339900028f, 0.01334163733f, 0.01328031812f, 0.01321521774f, + 0.01314651966f, 0.01307440922f, 0.01299907733f, 0.01292071585f, + 0.01283952035f, 0.01275568269f, 0.01266939752f, 0.01258085575f, + 0.01249024551f, 0.01239775307f, 0.01230355818f, 0.01220783778f, + 0.01211076323f, 0.01201249938f, 0.01191320643f, 0.01181303803f, + 0.01171213947f, 0.01161065139f, 0.01150870696f, 0.01140643191f, + 0.01130394638f, 0.01120136213f, 0.01109878626f, 0.01099631656f, + 0.01089404803f, 0.01079206541f, 0.01069044974f, 0.01074585691f, + 0.01079961471f, 0.01085163094f, 0.0109018134f, 0.01095007174f, + 0.01099631656f, 0.01104046032f, 0.0110824164f, 0.01112210099f, + 0.01115943585f, 0.01119434182f, 0.01122674625f, 0.01125658024f, + 0.01128377859f, 0.01130828168f, 0.01133003552f, 0.01134899072f, + 0.01136510447f, 0.01137833856f, 0.01138866507f, 0.0113960579f, + 0.01140050031f, 0.01140198205f, 0.01140050031f, 0.0113960579f, + 0.01138866507f, 0.01137833856f, 0.01136510447f, 0.01134899072f, + 0.01133003552f, 0.01130828168f, 0.01128377859f, 0.01125658024f, + 0.01122674625f, 0.01119434182f, 0.01115943585f, 0.01112210099f, + 0.0110824164f, 0.01104046032f, 0.01099631656f, 0.01095007174f, + 0.0109018134f, 0.01085163094f, 0.01079961471f, 0.01074585691f, + 0.01083632838f, 0.01093827467f, 0.01104046032f, 0.01114279591f, + 0.01124518644f, 0.01134752948f, 0.01144971419f, 0.01155162696f, + 0.01165314391f, 0.0117541356f, 0.01185446698f, 0.0119539937f, + 0.01205256768f, 0.01215003151f, 0.0122462241f, 0.01234097779f, + 0.01243411843f, 0.01252546813f, 0.01261484437f, 0.01270206179f, + 0.0127869295f, 0.01286925655f, 0.01294884924f, 0.01302551571f, + 0.01309906319f, 0.01316929981f, 0.01323603839f, 0.01329909544f, + 0.01335829217f, 0.0134134572f, 0.01346442662f, 0.01351104677f, + 0.01355317142f, 0.01359067019f, 0.01362342201f, 0.01365132071f, + 0.01367427502f, 0.01369220857f, 0.01370506082f, 0.01371278986f, + 0.01371536963f, 0.01371278986f, 0.01370506082f, 0.01369220857f, + 0.01367427502f, 0.01365132071f, 0.01362342201f, 0.01359067019f, + 0.01355317142f, 0.01351104677f, 0.01346442662f, 0.0134134572f, + 0.01335829217f, 0.01329909544f, 0.01323603839f, 0.01316929981f, + 0.01309906319f, 0.01302551571f, 0.01294884924f, 0.01286925655f, + 0.0127869295f, 0.01270206179f, 0.01261484437f, 0.01252546813f, + 0.01243411843f, 0.01234097779f, 0.0122462241f, 0.01215003151f, + 0.01205256768f, 0.0119539937f, 0.01185446698f, 0.0117541356f, + 0.01165314391f, 0.01155162696f, 0.01144971419f, 0.01134752948f, + 0.01124518644f, 0.01114279591f, 0.01104046032f, 0.01093827467f, + 0.01083632838f, 0.01073470619f, 0.01079206541f, 0.01084779948f, + 0.0109018134f, 0.01095401309f, 0.01100430358f, 0.01105259173f, + 0.01109878626f, 0.01114279591f, 0.01118453499f, 0.0112239169f, + 0.01126086153f, 0.01129528973f, 0.01132712793f, 0.01135630626f, + 0.01138276048f, 0.01140643191f, 0.01142726559f, 0.01144521404f, + 0.01146023627f, 0.01147229597f, 0.01148136612f, 0.0114874253f, + 0.01149045862f, 0.01149045862f, 0.0114874253f, 0.01148136612f, + 0.01147229597f, 0.01146023627f, 0.01144521404f, 0.01142726559f, + 0.01140643191f, 0.01138276048f, 0.01135630626f, 0.01132712793f, + 0.01129528973f, 0.01126086153f, 0.0112239169f, 0.01118453499f, + 0.01114279591f, 0.01109878626f, 0.01105259173f, 0.01100430358f, + 0.01095401309f, 0.0109018134f, 0.01084779948f, 0.01079206541f, + 0.01087856572f, 0.0109803956f, 0.0110824164f, 0.01118453499f, + 0.01128665265f, 0.01138866507f, 0.01149045862f, 0.01159191411f, + 0.01169290766f, 0.0117933061f, 0.01189296879f, 0.01199175231f, + 0.01208950393f, 0.01218606345f, 0.01228126884f, 0.01237494871f, + 0.01246692892f, 0.01255702879f, 0.01264506485f, 0.01273085084f, + 0.01281419583f, 0.0128949089f, 0.0129727982f, 0.01304767188f, + 0.01311933808f, 0.01318760961f, 0.01325230021f, 0.01331323106f, + 0.01337022707f, 0.01342312153f, 0.01347175613f, 0.01351598185f, + 0.01355566178f, 0.01359067019f, 0.0136208944f, 0.01364623569f, + 0.01366661023f, 0.01368195191f, 0.01369220857f, 0.01369734481f, + 0.01369734481f, 0.01369220857f, 0.01368195191f, 0.01366661023f, + 0.01364623569f, 0.0136208944f, 0.01359067019f, 0.01355566178f, + 0.01351598185f, 0.01347175613f, 0.01342312153f, 0.01337022707f, + 0.01331323106f, 0.01325230021f, 0.01318760961f, 0.01311933808f, + 0.01304767188f, 0.0129727982f, 0.0128949089f, 0.01281419583f, + 0.01273085084f, 0.01264506485f, 0.01255702879f, 0.01246692892f, + 0.01237494871f, 0.01228126884f, 0.01218606345f, 0.01208950393f, + 0.01199175231f, 0.01189296879f, 0.0117933061f, 0.01169290766f, + 0.01159191411f, 0.01149045862f, 0.01138866507f, 0.01128665265f, + 0.01118453499f, 0.0110824164f, 0.0109803956f, 0.01087856572f, + 0.01077701338f, 0.01083632838f, 0.01089404803f, 0.01095007174f, + 0.01100430358f, 0.01105664484f, 0.01110699773f, 0.01115526911f, + 0.01120136213f, 0.01124518644f, 0.01128665265f, 0.01132567506f, + 0.01136216894f, 0.0113960579f, 0.01142726559f, 0.01145572308f, + 0.01148136612f, 0.01150413603f, 0.01152398065f, 0.01154085156f, + 0.0115547115f, 0.01156552508f, 0.0115732681f, 0.01157792099f, + 0.0115794735f, 0.01157792099f, 0.0115732681f, 0.01156552508f, + 0.0115547115f, 0.01154085156f, 0.01152398065f, 0.01150413603f, + 0.01148136612f, 0.01145572308f, 0.01142726559f, 0.0113960579f, + 0.01136216894f, 0.01132567506f, 0.01128665265f, 0.01124518644f, + 0.01120136213f, 0.01115526911f, 0.01110699773f, 0.01105664484f, + 0.01100430358f, 0.01095007174f, 0.01089404803f, 0.01083632838f, + 0.01091869641f, 0.01102032885f, 0.01112210099f, 0.0112239169f, + 0.01132567506f, 0.01142726559f, 0.01152857486f, 0.01162947994f, + 0.0117298523f, 0.0118295569f, 0.01192845311f, 0.01202639099f, + 0.01212321594f, 0.01221876871f, 0.01231288072f, 0.01240538247f, + 0.01249609515f, 0.01258483995f, 0.01267143246f, 0.01275568269f, + 0.01283740439f, 0.01291640475f, 0.01299249288f, 0.01306547876f, + 0.01313517336f, 0.01320139226f, 0.01326395292f, 0.01332267933f, + 0.01337740291f, 0.01342796069f, 0.01347420178f, 0.01351598185f, + 0.01355317142f, 0.01358565222f, 0.01361331902f, 0.01363608148f, + 0.01365386508f, 0.01366661023f, 0.01367427502f, 0.01367683243f, + 0.01367427502f, 0.01366661023f, 0.01365386508f, 0.01363608148f, + 0.01361331902f, 0.01358565222f, 0.01355317142f, 0.01351598185f, + 0.01347420178f, 0.01342796069f, 0.01337740291f, 0.01332267933f, + 0.01326395292f, 0.01320139226f, 0.01313517336f, 0.01306547876f, + 0.01299249288f, 0.01291640475f, 0.01283740439f, 0.01275568269f, + 0.01267143246f, 0.01258483995f, 0.01249609515f, 0.01240538247f, + 0.01231288072f, 0.01221876871f, 0.01212321594f, 0.01202639099f, + 0.01192845311f, 0.0118295569f, 0.0117298523f, 0.01162947994f, + 0.01152857486f, 0.01142726559f, 0.01132567506f, 0.0112239169f, + 0.01112210099f, 0.01102032885f, 0.01091869641f, 0.01081729215f, + 0.01087856572f, 0.01093827467f, 0.01099631656f, 0.01105259173f, + 0.01110699773f, 0.01115943585f, 0.01120980456f, 0.01125800703f, + 0.01130394638f, 0.01134752948f, 0.01138866507f, 0.01142726559f, + 0.01146324724f, 0.01149653178f, 0.01152704284f, 0.0115547115f, + 0.0115794735f, 0.01160127111f, 0.01162005402f, 0.01163577568f, + 0.01164839976f, 0.01165789459f, 0.0116642369f, 0.01166741177f, + 0.01166741177f, 0.0116642369f, 0.01165789459f, 0.01164839976f, + 0.01163577568f, 0.01162005402f, 0.01160127111f, 0.0115794735f, + 0.0115547115f, 0.01152704284f, 0.01149653178f, 0.01146324724f, + 0.01142726559f, 0.01138866507f, 0.01134752948f, 0.01130394638f, + 0.01125800703f, 0.01120980456f, 0.01115943585f, 0.01110699773f, + 0.01105259173f, 0.01099631656f, 0.01093827467f, 0.01087856572f, + 0.01095664315f, 0.01105799619f, 0.01115943585f, 0.01126086153f, + 0.01136216894f, 0.01146324724f, 0.01156397816f, 0.0116642369f, + 0.01176389214f, 0.01186280511f, 0.0119608324f, 0.01205782313f, + 0.0121536199f, 0.0122480616f, 0.01234097779f, 0.01243219618f, + 0.01252153981f, 0.01260882709f, 0.01269387174f, 0.01277648844f, + 0.01285648718f, 0.01293367799f, 0.01300787181f, 0.01307888143f, + 0.01314651966f, 0.0132106049f, 0.01327095926f, 0.01332741138f, + 0.01337979734f, 0.01342796069f, 0.01347175613f, 0.01351104677f, + 0.01354570966f, 0.01357563306f, 0.01360072289f, 0.0136208944f, + 0.01363608148f, 0.01364623569f, 0.01365132071f, 0.01365132071f, + 0.01364623569f, 0.01363608148f, 0.0136208944f, 0.01360072289f, + 0.01357563306f, 0.01354570966f, 0.01351104677f, 0.01347175613f, + 0.01342796069f, 0.01337979734f, 0.01332741138f, 0.01327095926f, + 0.0132106049f, 0.01314651966f, 0.01307888143f, 0.01300787181f, + 0.01293367799f, 0.01285648718f, 0.01277648844f, 0.01269387174f, + 0.01260882709f, 0.01252153981f, 0.01243219618f, 0.01234097779f, + 0.0122480616f, 0.0121536199f, 0.01205782313f, 0.0119608324f, + 0.01186280511f, 0.01176389214f, 0.0116642369f, 0.01156397816f, + 0.01146324724f, 0.01136216894f, 0.01126086153f, 0.01115943585f, + 0.01105799619f, 0.01095664315f, 0.01085546613f, 0.01091869641f, + 0.0109803956f, 0.01104046032f, 0.01109878626f, 0.01115526911f, + 0.01120980456f, 0.01126228925f, 0.01131262258f, 0.01136070304f, + 0.01140643191f, 0.01144971419f, 0.01149045862f, 0.01152857486f, + 0.01156397816f, 0.01159659028f, 0.01162633486f, 0.01165314391f, + 0.01167695317f, 0.01169770677f, 0.0117153544f, 0.0117298523f, + 0.011741166f, 0.01174926758f, 0.0117541356f, 0.01175575983f, + 0.0117541356f, 0.01174926758f, 0.011741166f, 0.0117298523f, + 0.0117153544f, 0.01169770677f, 0.01167695317f, 0.01165314391f, + 0.01162633486f, 0.01159659028f, 0.01156397816f, 0.01152857486f, + 0.01149045862f, 0.01144971419f, 0.01140643191f, 0.01136070304f, + 0.01131262258f, 0.01126228925f, 0.01120980456f, 0.01115526911f, + 0.01109878626f, 0.01104046032f, 0.0109803956f, 0.01091869641f, + 0.01099232957f, 0.01109332126f, 0.01119434182f, 0.01129528973f, + 0.0113960579f, 0.01149653178f, 0.01159659028f, 0.01169610675f, + 0.01179494616f, 0.01189296879f, 0.01199002843f, 0.01208597142f, + 0.0121806385f, 0.01227386575f, 0.01236548461f, 0.01245531905f, + 0.0125431912f, 0.01262892038f, 0.01271232124f, 0.01279320568f, + 0.01287138835f, 0.01294667926f, 0.01301889122f, 0.01308783889f, + 0.01315334067f, 0.01321521774f, 0.01327329688f, 0.01332741138f, + 0.01337740291f, 0.01342312153f, 0.01346442662f, 0.01350119151f, + 0.01353329886f, 0.01356064621f, 0.01358314604f, 0.01360072289f, + 0.01361331902f, 0.0136208944f, 0.01362342201f, 0.0136208944f, + 0.01361331902f, 0.01360072289f, 0.01358314604f, 0.01356064621f, + 0.01353329886f, 0.01350119151f, 0.01346442662f, 0.01342312153f, + 0.01337740291f, 0.01332741138f, 0.01327329688f, 0.01321521774f, + 0.01315334067f, 0.01308783889f, 0.01301889122f, 0.01294667926f, + 0.01287138835f, 0.01279320568f, 0.01271232124f, 0.01262892038f, + 0.0125431912f, 0.01245531905f, 0.01236548461f, 0.01227386575f, + 0.0121806385f, 0.01208597142f, 0.01199002843f, 0.01189296879f, + 0.01179494616f, 0.01169610675f, 0.01159659028f, 0.01149653178f, + 0.0113960579f, 0.01129528973f, 0.01119434182f, 0.01109332126f, + 0.01099232957f, 0.01089146268f, 0.01095664315f, 0.01102032885f, + 0.0110824164f, 0.01114279591f, 0.01120136213f, 0.01125800703f, + 0.01131262258f, 0.01136510447f, 0.01141534653f, 0.01146324724f, + 0.01150870696f, 0.01155162696f, 0.01159191411f, 0.01162947994f, + 0.0116642369f, 0.01169610675f, 0.01172501314f, 0.01175088901f, + 0.01177367195f, 0.0117933061f, 0.01180974208f, 0.01182294171f, + 0.01183286961f, 0.01183950249f, 0.01184282266f, 0.01184282266f, + 0.01183950249f, 0.01183286961f, 0.01182294171f, 0.01180974208f, + 0.0117933061f, 0.01177367195f, 0.01175088901f, 0.01172501314f, + 0.01169610675f, 0.0116642369f, 0.01162947994f, 0.01159191411f, + 0.01155162696f, 0.01150870696f, 0.01146324724f, 0.01141534653f, + 0.01136510447f, 0.01131262258f, 0.01125800703f, 0.01120136213f, + 0.01114279591f, 0.0110824164f, 0.01102032885f, 0.01095664315f, + 0.01102568675f, 0.0111262314f, 0.01122674625f, 0.01132712793f, + 0.01142726559f, 0.01152704284f, 0.01162633486f, 0.01172501314f, + 0.01182294171f, 0.01191997528f, 0.01201596763f, 0.01211076323f, + 0.01220420003f, 0.01229611505f, 0.01238633506f, 0.01247468684f, + 0.01256099064f, 0.01264506485f, 0.01272672601f, 0.01280578785f, + 0.01288206317f, 0.01295536757f, 0.01302551571f, 0.01309232507f, + 0.01315561775f, 0.01321521774f, 0.01327095926f, 0.01332267933f, + 0.01337022707f, 0.0134134572f, 0.0134522384f, 0.01348644961f, + 0.01351598185f, 0.01354074106f, 0.01356064621f, 0.01357563306f, + 0.01358565222f, 0.01359067019f, 0.01359067019f, 0.01358565222f, + 0.01357563306f, 0.01356064621f, 0.01354074106f, 0.01351598185f, + 0.01348644961f, 0.0134522384f, 0.0134134572f, 0.01337022707f, + 0.01332267933f, 0.01327095926f, 0.01321521774f, 0.01315561775f, + 0.01309232507f, 0.01302551571f, 0.01295536757f, 0.01288206317f, + 0.01280578785f, 0.01272672601f, 0.01264506485f, 0.01256099064f, + 0.01247468684f, 0.01238633506f, 0.01229611505f, 0.01220420003f, + 0.01211076323f, 0.01201596763f, 0.01191997528f, 0.01182294171f, + 0.01172501314f, 0.01162633486f, 0.01152704284f, 0.01142726559f, + 0.01132712793f, 0.01122674625f, 0.0111262314f, 0.01102568675f, + 0.01092521101f, 0.01099232957f, 0.01105799619f, 0.01112210099f, + 0.01118453499f, 0.01124518644f, 0.01130394638f, 0.01136070304f, + 0.01141534653f, 0.01146776881f, 0.01151786372f, 0.01156552508f, + 0.01161065139f, 0.01165314391f, 0.01169290766f, 0.0117298523f, + 0.01176389214f, 0.01179494616f, 0.01182294171f, 0.01184780896f, + 0.01186948828f, 0.01188792568f, 0.0119030755f, 0.01191489771f, + 0.01192336436f, 0.01192845311f, 0.01193015091f, 0.01192845311f, + 0.01192336436f, 0.01191489771f, 0.0119030755f, 0.01188792568f, + 0.01186948828f, 0.01184780896f, 0.01182294171f, 0.01179494616f, + 0.01176389214f, 0.0117298523f, 0.01169290766f, 0.01165314391f, + 0.01161065139f, 0.01156552508f, 0.01151786372f, 0.01146776881f, + 0.01141534653f, 0.01136070304f, 0.01130394638f, 0.01124518644f, + 0.01118453499f, 0.01112210099f, 0.01105799619f, 0.01099232957f, + 0.01105664484f, 0.01115665678f, 0.01125658024f, 0.01135630626f, + 0.01145572308f, 0.0115547115f, 0.01165314391f, 0.01175088901f, + 0.01184780896f, 0.01194375753f, 0.01203858573f, 0.01213213522f, + 0.01222424489f, 0.01231474802f, 0.01240347326f, 0.01249024551f, + 0.01257488597f, 0.01265721396f, 0.01273704506f, 0.01281419583f, + 0.01288848184f, 0.0129597187f, 0.01302772667f, 0.01309232507f, + 0.01315334067f, 0.0132106049f, 0.01326395292f, 0.01331323106f, + 0.01335829217f, 0.01339900028f, 0.01343523059f, 0.01346686855f, + 0.01349381451f, 0.01351598185f, 0.01353329886f, 0.01354570966f, + 0.01355317142f, 0.01355566178f, 0.01355317142f, 0.01354570966f, + 0.01353329886f, 0.01351598185f, 0.01349381451f, 0.01346686855f, + 0.01343523059f, 0.01339900028f, 0.01335829217f, 0.01331323106f, + 0.01326395292f, 0.0132106049f, 0.01315334067f, 0.01309232507f, + 0.01302772667f, 0.0129597187f, 0.01288848184f, 0.01281419583f, + 0.01273704506f, 0.01265721396f, 0.01257488597f, 0.01249024551f, + 0.01240347326f, 0.01231474802f, 0.01222424489f, 0.01213213522f, + 0.01203858573f, 0.01194375753f, 0.01184780896f, 0.01175088901f, + 0.01165314391f, 0.0115547115f, 0.01145572308f, 0.01135630626f, + 0.01125658024f, 0.01115665678f, 0.01105664484f, 0.01095664315f, + 0.01102568675f, 0.01109332126f, 0.01115943585f, 0.0112239169f, + 0.01128665265f, 0.01134752948f, 0.01140643191f, 0.01146324724f, + 0.01151786372f, 0.01157016866f, 0.01162005402f, 0.01166741177f, + 0.01171213947f, 0.0117541356f, 0.0117933061f, 0.0118295569f, + 0.01186280511f, 0.01189296879f, 0.01191997528f, 0.01194375753f, + 0.01196425594f, 0.01198141929f, 0.01199520286f, 0.01200557221f, + 0.01201249938f, 0.01201596763f, 0.01201596763f, 0.01201249938f, + 0.01200557221f, 0.01199520286f, 0.01198141929f, 0.01196425594f, + 0.01194375753f, 0.01191997528f, 0.01189296879f, 0.01186280511f, + 0.0118295569f, 0.0117933061f, 0.0117541356f, 0.01171213947f, + 0.01166741177f, 0.01162005402f, 0.01157016866f, 0.01151786372f, + 0.01146324724f, 0.01140643191f, 0.01134752948f, 0.01128665265f, + 0.0112239169f, 0.01115943585f, 0.01109332126f, 0.01102568675f, + 0.01108513959f, 0.01118453499f, 0.01128377859f, 0.01138276048f, + 0.01148136612f, 0.0115794735f, 0.01167695317f, 0.01177367195f, + 0.01186948828f, 0.01196425594f, 0.01205782313f, 0.01215003151f, + 0.01224071812f, 0.01232971624f, 0.01241685264f, 0.01250195317f, + 0.01258483995f, 0.0126653323f, 0.0127432486f, 0.01281840634f, + 0.01289062295f, 0.0129597187f, 0.01302551571f, 0.01308783889f, + 0.01314651966f, 0.01320139226f, 0.01325230021f, 0.01329909544f, + 0.01334163733f, 0.01337979734f, 0.0134134572f, 0.01344251167f, + 0.01346686855f, 0.01348644961f, 0.01350119151f, 0.01351104677f, + 0.01351598185f, 0.01351598185f, 0.01351104677f, 0.01350119151f, + 0.01348644961f, 0.01346686855f, 0.01344251167f, 0.0134134572f, + 0.01337979734f, 0.01334163733f, 0.01329909544f, 0.01325230021f, + 0.01320139226f, 0.01314651966f, 0.01308783889f, 0.01302551571f, + 0.0129597187f, 0.01289062295f, 0.01281840634f, 0.0127432486f, + 0.0126653323f, 0.01258483995f, 0.01250195317f, 0.01241685264f, + 0.01232971624f, 0.01224071812f, 0.01215003151f, 0.01205782313f, + 0.01196425594f, 0.01186948828f, 0.01177367195f, 0.01167695317f, + 0.0115794735f, 0.01148136612f, 0.01138276048f, 0.01128377859f, + 0.01118453499f, 0.01108513959f, 0.01098569483f, 0.01105664484f, + 0.0111262314f, 0.01119434182f, 0.01126086153f, 0.01132567506f, + 0.01138866507f, 0.01144971419f, 0.01150870696f, 0.01156552508f, + 0.01162005402f, 0.01167218015f, 0.01172179077f, 0.01176877879f, + 0.01181303803f, 0.01185446698f, 0.01189296879f, 0.01192845311f, + 0.0119608324f, 0.01199002843f, 0.01201596763f, 0.01203858573f, + 0.01205782313f, 0.01207363233f, 0.01208597142f, 0.01209480781f, + 0.01210011914f, 0.01210189145f, 0.01210011914f, 0.01209480781f, + 0.01208597142f, 0.01207363233f, 0.01205782313f, 0.01203858573f, + 0.01201596763f, 0.01199002843f, 0.0119608324f, 0.01192845311f, + 0.01189296879f, 0.01185446698f, 0.01181303803f, 0.01176877879f, + 0.01172179077f, 0.01167218015f, 0.01162005402f, 0.01156552508f, + 0.01150870696f, 0.01144971419f, 0.01138866507f, 0.01132567506f, + 0.01126086153f, 0.01119434182f, 0.0111262314f, 0.01105664484f, + 0.01111111138f, 0.01120980456f, 0.01130828168f, 0.01140643191f, + 0.01150413603f, 0.01160127111f, 0.01169770677f, 0.0117933061f, + 0.01188792568f, 0.01198141929f, 0.01207363233f, 0.01216440555f, + 0.01225357689f, 0.01234097779f, 0.01242643595f, 0.01250977721f, + 0.0125908237f, 0.01266939752f, 0.01274531893f, 0.01281840634f, + 0.01288848184f, 0.01295536757f, 0.01301889122f, 0.01307888143f, + 0.01313517336f, 0.01318760961f, 0.01323603839f, 0.01328031812f, + 0.01332031563f, 0.01335590892f, 0.01338698901f, 0.0134134572f, + 0.01343523059f, 0.0134522384f, 0.01346442662f, 0.01347175613f, + 0.01347420178f, 0.01347175613f, 0.01346442662f, 0.0134522384f, + 0.01343523059f, 0.0134134572f, 0.01338698901f, 0.01335590892f, + 0.01332031563f, 0.01328031812f, 0.01323603839f, 0.01318760961f, + 0.01313517336f, 0.01307888143f, 0.01301889122f, 0.01295536757f, + 0.01288848184f, 0.01281840634f, 0.01274531893f, 0.01266939752f, + 0.0125908237f, 0.01250977721f, 0.01242643595f, 0.01234097779f, + 0.01225357689f, 0.01216440555f, 0.01207363233f, 0.01198141929f, + 0.01188792568f, 0.0117933061f, 0.01169770677f, 0.01160127111f, + 0.01150413603f, 0.01140643191f, 0.01130828168f, 0.01120980456f, + 0.01111111138f, 0.01101230737f, 0.01108513959f, 0.01115665678f, + 0.01122674625f, 0.01129528973f, 0.01136216894f, 0.01142726559f, + 0.01149045862f, 0.01155162696f, 0.01161065139f, 0.01166741177f, + 0.01172179077f, 0.01177367195f, 0.01182294171f, 0.01186948828f, + 0.01191320643f, 0.0119539937f, 0.01199175231f, 0.01202639099f, + 0.01205782313f, 0.01208597142f, 0.01211076323f, 0.01213213522f, + 0.01215003151f, 0.01216440555f, 0.01217522006f, 0.0121824462f, + 0.01218606345f, 0.01218606345f, 0.0121824462f, 0.01217522006f, + 0.01216440555f, 0.01215003151f, 0.01213213522f, 0.01211076323f, + 0.01208597142f, 0.01205782313f, 0.01202639099f, 0.01199175231f, + 0.0119539937f, 0.01191320643f, 0.01186948828f, 0.01182294171f, + 0.01177367195f, 0.01172179077f, 0.01166741177f, 0.01161065139f, + 0.01155162696f, 0.01149045862f, 0.01142726559f, 0.01136216894f, + 0.01129528973f, 0.01122674625f, 0.01115665678f, 0.01108513959f, + 0.01113450434f, 0.01123241056f, 0.01133003552f, 0.01142726559f, + 0.01152398065f, 0.01162005402f, 0.0117153544f, 0.01180974208f, + 0.0119030755f, 0.01199520286f, 0.01208597142f, 0.01217522006f, + 0.01226278674f, 0.01234850287f, 0.01243219618f, 0.01251369435f, + 0.01259282045f, 0.01266939752f, 0.0127432486f, 0.01281419583f, + 0.01288206317f, 0.01294667926f, 0.01300787181f, 0.01306547876f, + 0.01311933808f, 0.01316929981f, 0.01321521774f, 0.01325695775f, + 0.0132943932f, 0.01332741138f, 0.01335590892f, 0.01337979734f, + 0.01339900028f, 0.0134134572f, 0.01342312153f, 0.01342796069f, + 0.01342796069f, 0.01342312153f, 0.0134134572f, 0.01339900028f, + 0.01337979734f, 0.01335590892f, 0.01332741138f, 0.0132943932f, + 0.01325695775f, 0.01321521774f, 0.01316929981f, 0.01311933808f, + 0.01306547876f, 0.01300787181f, 0.01294667926f, 0.01288206317f, + 0.01281419583f, 0.0127432486f, 0.01266939752f, 0.01259282045f, + 0.01251369435f, 0.01243219618f, 0.01234850287f, 0.01226278674f, + 0.01217522006f, 0.01208597142f, 0.01199520286f, 0.0119030755f, + 0.01180974208f, 0.0117153544f, 0.01162005402f, 0.01152398065f, + 0.01142726559f, 0.01133003552f, 0.01123241056f, 0.01113450434f, + 0.0110364249f, 0.01111111138f, 0.01118453499f, 0.01125658024f, + 0.01132712793f, 0.0113960579f, 0.01146324724f, 0.01152857486f, + 0.01159191411f, 0.01165314391f, 0.01171213947f, 0.01176877879f, + 0.01182294171f, 0.01187450811f, 0.01192336436f, 0.01196939778f, + 0.01201249938f, 0.01205256768f, 0.01208950393f, 0.01212321594f, + 0.0121536199f, 0.0121806385f, 0.01220420003f, 0.01222424489f, + 0.01224071812f, 0.01225357689f, 0.01226278674f, 0.01226832252f, + 0.01227016933f, 0.01226832252f, 0.01226278674f, 0.01225357689f, + 0.01224071812f, 0.01222424489f, 0.01220420003f, 0.0121806385f, + 0.0121536199f, 0.01212321594f, 0.01208950393f, 0.01205256768f, + 0.01201249938f, 0.01196939778f, 0.01192336436f, 0.01187450811f, + 0.01182294171f, 0.01176877879f, 0.01171213947f, 0.01165314391f, + 0.01159191411f, 0.01152857486f, 0.01146324724f, 0.0113960579f, + 0.01132712793f, 0.01125658024f, 0.01118453499f, 0.01111111138f, + 0.01115526911f, 0.01125230361f, 0.01134899072f, 0.01144521404f, + 0.01154085156f, 0.01163577568f, 0.0117298523f, 0.01182294171f, + 0.01191489771f, 0.01200557221f, 0.01209480781f, 0.0121824462f, + 0.01226832252f, 0.01235227007f, 0.01243411843f, 0.01251369435f, + 0.0125908237f, 0.0126653323f, 0.01273704506f, 0.01280578785f, + 0.01287138835f, 0.01293367799f, 0.01299249288f, 0.01304767188f, + 0.01309906319f, 0.01314651966f, 0.01318990346f, 0.013229087f, + 0.01326395292f, 0.0132943932f, 0.01332031563f, 0.01334163733f, + 0.01335829217f, 0.01337022707f, 0.01337740291f, 0.01337979734f, + 0.01337740291f, 0.01337022707f, 0.01335829217f, 0.01334163733f, + 0.01332031563f, 0.0132943932f, 0.01326395292f, 0.013229087f, + 0.01318990346f, 0.01314651966f, 0.01309906319f, 0.01304767188f, + 0.01299249288f, 0.01293367799f, 0.01287138835f, 0.01280578785f, + 0.01273704506f, 0.0126653323f, 0.0125908237f, 0.01251369435f, + 0.01243411843f, 0.01235227007f, 0.01226832252f, 0.0121824462f, + 0.01209480781f, 0.01200557221f, 0.01191489771f, 0.01182294171f, + 0.0117298523f, 0.01163577568f, 0.01154085156f, 0.01144521404f, + 0.01134899072f, 0.01125230361f, 0.01115526911f, 0.01105799619f, + 0.01113450434f, 0.01120980456f, 0.01128377859f, 0.01135630626f, + 0.01142726559f, 0.01149653178f, 0.01156397816f, 0.01162947994f, + 0.01169290766f, 0.0117541356f, 0.01181303803f, 0.01186948828f, + 0.01192336436f, 0.0119745452f, 0.01202291343f, 0.01206835546f, + 0.01211076323f, 0.01215003151f, 0.01218606345f, 0.01221876871f, + 0.0122480616f, 0.01227386575f, 0.01229611505f, 0.01231474802f, + 0.01232971624f, 0.01234097779f, 0.01234850287f, 0.01235227007f, + 0.01235227007f, 0.01234850287f, 0.01234097779f, 0.01232971624f, + 0.01231474802f, 0.01229611505f, 0.01227386575f, 0.0122480616f, + 0.01221876871f, 0.01218606345f, 0.01215003151f, 0.01211076323f, + 0.01206835546f, 0.01202291343f, 0.0119745452f, 0.01192336436f, + 0.01186948828f, 0.01181303803f, 0.0117541356f, 0.01169290766f, + 0.01162947994f, 0.01156397816f, 0.01149653178f, 0.01142726559f, + 0.01135630626f, 0.01128377859f, 0.01120980456f, 0.01113450434f, + 0.01117335912f, 0.01126943901f, 0.01136510447f, 0.01146023627f, + 0.0115547115f, 0.01164839976f, 0.011741166f, 0.01183286961f, + 0.01192336436f, 0.01201249938f, 0.01210011914f, 0.01218606345f, + 0.01227016933f, 0.01235227007f, 0.01243219618f, 0.01250977721f, + 0.01258483995f, 0.01265721396f, 0.01272672601f, 0.01279320568f, + 0.01285648718f, 0.01291640475f, 0.0129727982f, 0.01302551571f, + 0.01307440922f, 0.01311933808f, 0.01316017378f, 0.01319679338f, + 0.013229087f, 0.01325695775f, 0.01328031812f, 0.01329909544f, + 0.01331323106f, 0.01332267933f, 0.01332741138f, 0.01332741138f, + 0.01332267933f, 0.01331323106f, 0.01329909544f, 0.01328031812f, + 0.01325695775f, 0.013229087f, 0.01319679338f, 0.01316017378f, + 0.01311933808f, 0.01307440922f, 0.01302551571f, 0.0129727982f, + 0.01291640475f, 0.01285648718f, 0.01279320568f, 0.01272672601f, + 0.01265721396f, 0.01258483995f, 0.01250977721f, 0.01243219618f, + 0.01235227007f, 0.01227016933f, 0.01218606345f, 0.01210011914f, + 0.01201249938f, 0.01192336436f, 0.01183286961f, 0.011741166f, + 0.01164839976f, 0.0115547115f, 0.01146023627f, 0.01136510447f, + 0.01126943901f, 0.01117335912f, 0.01107697561f, 0.01115526911f, + 0.01123241056f, 0.01130828168f, 0.01138276048f, 0.01145572308f, + 0.01152704284f, 0.01159659028f, 0.0116642369f, 0.0117298523f, + 0.0117933061f, 0.01185446698f, 0.01191320643f, 0.01196939778f, + 0.01202291343f, 0.01207363233f, 0.01212143432f, 0.01216620672f, + 0.01220783778f, 0.0122462241f, 0.01228126884f, 0.01231288072f, + 0.01234097779f, 0.01236548461f, 0.01238633506f, 0.01240347326f, + 0.01241685264f, 0.01242643595f, 0.01243219618f, 0.01243411843f, + 0.01243219618f, 0.01242643595f, 0.01241685264f, 0.01240347326f, + 0.01238633506f, 0.01236548461f, 0.01234097779f, 0.01231288072f, + 0.01228126884f, 0.0122462241f, 0.01220783778f, 0.01216620672f, + 0.01212143432f, 0.01207363233f, 0.01202291343f, 0.01196939778f, + 0.01191320643f, 0.01185446698f, 0.0117933061f, 0.0117298523f, + 0.0116642369f, 0.01159659028f, 0.01152704284f, 0.01145572308f, + 0.01138276048f, 0.01130828168f, 0.01123241056f, 0.01115526911f, + 0.01118873432f, 0.01128377859f, 0.01137833856f, 0.01147229597f, + 0.01156552508f, 0.01165789459f, 0.01174926758f, 0.01183950249f, + 0.01192845311f, 0.01201596763f, 0.01210189145f, 0.01218606345f, + 0.01226832252f, 0.01234850287f, 0.01242643595f, 0.01250195317f, + 0.01257488597f, 0.01264506485f, 0.01271232124f, 0.01277648844f, + 0.01283740439f, 0.0128949089f, 0.01294884924f, 0.01299907733f, + 0.0130454516f, 0.01308783889f, 0.01312611811f, 0.01316017378f, + 0.01318990346f, 0.01321521774f, 0.01323603839f, 0.01325230021f, + 0.01326395292f, 0.01327095926f, 0.01327329688f, 0.01327095926f, + 0.01326395292f, 0.01325230021f, 0.01323603839f, 0.01321521774f, + 0.01318990346f, 0.01316017378f, 0.01312611811f, 0.01308783889f, + 0.0130454516f, 0.01299907733f, 0.01294884924f, 0.0128949089f, + 0.01283740439f, 0.01277648844f, 0.01271232124f, 0.01264506485f, + 0.01257488597f, 0.01250195317f, 0.01242643595f, 0.01234850287f, + 0.01226832252f, 0.01218606345f, 0.01210189145f, 0.01201596763f, + 0.01192845311f, 0.01183950249f, 0.01174926758f, 0.01165789459f, + 0.01156552508f, 0.01147229597f, 0.01137833856f, 0.01128377859f, + 0.01118873432f, 0.01109332126f, 0.01117335912f, 0.01125230361f, + 0.01133003552f, 0.01140643191f, 0.01148136612f, 0.0115547115f, + 0.01162633486f, 0.01169610675f, 0.01176389214f, 0.0118295569f, + 0.01189296879f, 0.0119539937f, 0.01201249938f, 0.01206835546f, + 0.01212143432f, 0.01217161212f, 0.01221876871f, 0.01226278674f, + 0.01230355818f, 0.01234097779f, 0.01237494871f, 0.01240538247f, + 0.01243219618f, 0.01245531905f, 0.01247468684f, 0.01249024551f, + 0.01250195317f, 0.01250977721f, 0.01251369435f, 0.01251369435f, + 0.01250977721f, 0.01250195317f, 0.01249024551f, 0.01247468684f, + 0.01245531905f, 0.01243219618f, 0.01240538247f, 0.01237494871f, + 0.01234097779f, 0.01230355818f, 0.01226278674f, 0.01221876871f, + 0.01217161212f, 0.01212143432f, 0.01206835546f, 0.01201249938f, + 0.0119539937f, 0.01189296879f, 0.0118295569f, 0.01176389214f, + 0.01169610675f, 0.01162633486f, 0.0115547115f, 0.01148136612f, + 0.01140643191f, 0.01133003552f, 0.01125230361f, 0.01117335912f, + 0.01120136213f, 0.01129528973f, 0.01138866507f, 0.01148136612f, + 0.0115732681f, 0.0116642369f, 0.0117541356f, 0.01184282266f, + 0.01193015091f, 0.01201596763f, 0.01210011914f, 0.0121824462f, + 0.01226278674f, 0.01234097779f, 0.01241685264f, 0.01249024551f, + 0.01256099064f, 0.01262892038f, 0.01269387174f, 0.01275568269f, + 0.01281419583f, 0.01286925655f, 0.01292071585f, 0.01296843402f, + 0.01301227603f, 0.01305211708f, 0.01308783889f, 0.01311933808f, + 0.01314651966f, 0.01316929981f, 0.01318760961f, 0.01320139226f, + 0.0132106049f, 0.01321521774f, 0.01321521774f, 0.0132106049f, + 0.01320139226f, 0.01318760961f, 0.01316929981f, 0.01314651966f, + 0.01311933808f, 0.01308783889f, 0.01305211708f, 0.01301227603f, + 0.01296843402f, 0.01292071585f, 0.01286925655f, 0.01281419583f, + 0.01275568269f, 0.01269387174f, 0.01262892038f, 0.01256099064f, + 0.01249024551f, 0.01241685264f, 0.01234097779f, 0.01226278674f, + 0.0121824462f, 0.01210011914f, 0.01201596763f, 0.01193015091f, + 0.01184282266f, 0.0117541356f, 0.0116642369f, 0.0115732681f, + 0.01148136612f, 0.01138866507f, 0.01129528973f, 0.01120136213f, + 0.01110699773f, 0.01118873432f, 0.01126943901f, 0.01134899072f, + 0.01142726559f, 0.01150413603f, 0.0115794735f, 0.01165314391f, + 0.01172501314f, 0.01179494616f, 0.01186280511f, 0.01192845311f, + 0.01199175231f, 0.01205256768f, 0.01211076323f, 0.01216620672f, + 0.01221876871f, 0.01226832252f, 0.01231474802f, 0.01235792786f, + 0.01239775307f, 0.01243411843f, 0.01246692892f, 0.01249609515f, + 0.01252153981f, 0.0125431912f, 0.01256099064f, 0.01257488597f, + 0.01258483995f, 0.0125908237f, 0.01259282045f, 0.0125908237f, + 0.01258483995f, 0.01257488597f, 0.01256099064f, 0.0125431912f, + 0.01252153981f, 0.01249609515f, 0.01246692892f, 0.01243411843f, + 0.01239775307f, 0.01235792786f, 0.01231474802f, 0.01226832252f, + 0.01221876871f, 0.01216620672f, 0.01211076323f, 0.01205256768f, + 0.01199175231f, 0.01192845311f, 0.01186280511f, 0.01179494616f, + 0.01172501314f, 0.01165314391f, 0.0115794735f, 0.01150413603f, + 0.01142726559f, 0.01134899072f, 0.01126943901f, 0.01118873432f, + 0.01121121366f, 0.01130394638f, 0.0113960579f, 0.0114874253f, + 0.01157792099f, 0.01166741177f, 0.01175575983f, 0.01184282266f, + 0.01192845311f, 0.01201249938f, 0.01209480781f, 0.01217522006f, + 0.01225357689f, 0.01232971624f, 0.01240347326f, 0.01247468684f, + 0.0125431912f, 0.01260882709f, 0.01267143246f, 0.01273085084f, + 0.0127869295f, 0.01283952035f, 0.01288848184f, 0.01293367799f, + 0.01297498215f, 0.01301227603f, 0.0130454516f, 0.01307440922f, + 0.01309906319f, 0.01311933808f, 0.01313517336f, 0.01314651966f, + 0.01315334067f, 0.01315561775f, 0.01315334067f, 0.01314651966f, + 0.01313517336f, 0.01311933808f, 0.01309906319f, 0.01307440922f, + 0.0130454516f, 0.01301227603f, 0.01297498215f, 0.01293367799f, + 0.01288848184f, 0.01283952035f, 0.0127869295f, 0.01273085084f, + 0.01267143246f, 0.01260882709f, 0.0125431912f, 0.01247468684f, + 0.01240347326f, 0.01232971624f, 0.01225357689f, 0.01217522006f, + 0.01209480781f, 0.01201249938f, 0.01192845311f, 0.01184282266f, + 0.01175575983f, 0.01166741177f, 0.01157792099f, 0.0114874253f, + 0.0113960579f, 0.01130394638f, 0.01121121366f, 0.01111797616f, + 0.01120136213f, 0.01128377859f, 0.01136510447f, 0.01144521404f, + 0.01152398065f, 0.01160127111f, 0.01167695317f, 0.01175088901f, + 0.01182294171f, 0.01189296879f, 0.0119608324f, 0.01202639099f, + 0.01208950393f, 0.01215003151f, 0.01220783778f, 0.01226278674f, + 0.01231474802f, 0.01236359403f, 0.01240920182f, 0.01245145593f, + 0.01249024551f, 0.01252546813f, 0.01255702879f, 0.01258483995f, + 0.01260882709f, 0.01262892038f, 0.01264506485f, 0.01265721396f, + 0.0126653323f, 0.01266939752f, 0.01266939752f, 0.0126653323f, + 0.01265721396f, 0.01264506485f, 0.01262892038f, 0.01260882709f, + 0.01258483995f, 0.01255702879f, 0.01252546813f, 0.01249024551f, + 0.01245145593f, 0.01240920182f, 0.01236359403f, 0.01231474802f, + 0.01226278674f, 0.01220783778f, 0.01215003151f, 0.01208950393f, + 0.01202639099f, 0.0119608324f, 0.01189296879f, 0.01182294171f, + 0.01175088901f, 0.01167695317f, 0.01160127111f, 0.01152398065f, + 0.01144521404f, 0.01136510447f, 0.01128377859f, 0.01120136213f, + 0.01121826563f, 0.01130972803f, 0.01140050031f, 0.01149045862f, + 0.0115794735f, 0.01166741177f, 0.0117541356f, 0.01183950249f, + 0.01192336436f, 0.01200557221f, 0.01208597142f, 0.01216440555f, + 0.01224071812f, 0.01231474802f, 0.01238633506f, 0.01245531905f, + 0.01252153981f, 0.01258483995f, 0.01264506485f, 0.01270206179f, + 0.01275568269f, 0.01280578785f, 0.01285223942f, 0.0128949089f, + 0.01293367799f, 0.01296843402f, 0.01299907733f, 0.01302551571f, + 0.01304767188f, 0.01306547876f, 0.01307888143f, 0.01308783889f, + 0.01309232507f, 0.01309232507f, 0.01308783889f, 0.01307888143f, + 0.01306547876f, 0.01304767188f, 0.01302551571f, 0.01299907733f, + 0.01296843402f, 0.01293367799f, 0.0128949089f, 0.01285223942f, + 0.01280578785f, 0.01275568269f, 0.01270206179f, 0.01264506485f, + 0.01258483995f, 0.01252153981f, 0.01245531905f, 0.01238633506f, + 0.01231474802f, 0.01224071812f, 0.01216440555f, 0.01208597142f, + 0.01200557221f, 0.01192336436f, 0.01183950249f, 0.0117541356f, + 0.01166741177f, 0.0115794735f, 0.01149045862f, 0.01140050031f, + 0.01130972803f, 0.01121826563f, 0.0111262314f, 0.01121121366f, + 0.01129528973f, 0.01137833856f, 0.01146023627f, 0.01154085156f, + 0.01162005402f, 0.01169770677f, 0.01177367195f, 0.01184780896f, + 0.01191997528f, 0.01199002843f, 0.01205782313f, 0.01212321594f, + 0.01218606345f, 0.0122462241f, 0.01230355818f, 0.01235792786f, + 0.01240920182f, 0.01245725155f, 0.01250195317f, 0.0125431912f, + 0.01258085575f, 0.01261484437f, 0.01264506485f, 0.01267143246f, + 0.01269387174f, 0.01271232124f, 0.01272672601f, 0.01273704506f, + 0.0127432486f, 0.01274531893f, 0.0127432486f, 0.01273704506f, + 0.01272672601f, 0.01271232124f, 0.01269387174f, 0.01267143246f, + 0.01264506485f, 0.01261484437f, 0.01258085575f, 0.0125431912f, + 0.01250195317f, 0.01245725155f, 0.01240920182f, 0.01235792786f, + 0.01230355818f, 0.0122462241f, 0.01218606345f, 0.01212321594f, + 0.01205782313f, 0.01199002843f, 0.01191997528f, 0.01184780896f, + 0.01177367195f, 0.01169770677f, 0.01162005402f, 0.01154085156f, + 0.01146023627f, 0.01137833856f, 0.01129528973f, 0.01121121366f, + 0.01122250315f, 0.01131262258f, 0.01140198205f, 0.01149045862f, + 0.01157792099f, 0.0116642369f, 0.01174926758f, 0.01183286961f, + 0.01191489771f, 0.01199520286f, 0.01207363233f, 0.01215003151f, + 0.01222424489f, 0.01229611505f, 0.01236548461f, 0.01243219618f, + 0.01249609515f, 0.01255702879f, 0.01261484437f, 0.01266939752f, + 0.01272054669f, 0.01276815403f, 0.01281209197f, 0.01285223942f, + 0.01288848184f, 0.01292071585f, 0.01294884924f, 0.0129727982f, + 0.01299249288f, 0.01300787181f, 0.01301889122f, 0.01302551571f, + 0.01302772667f, 0.01302551571f, 0.01301889122f, 0.01300787181f, + 0.01299249288f, 0.0129727982f, 0.01294884924f, 0.01292071585f, + 0.01288848184f, 0.01285223942f, 0.01281209197f, 0.01276815403f, + 0.01272054669f, 0.01266939752f, 0.01261484437f, 0.01255702879f, + 0.01249609515f, 0.01243219618f, 0.01236548461f, 0.01229611505f, + 0.01222424489f, 0.01215003151f, 0.01207363233f, 0.01199520286f, + 0.01191489771f, 0.01183286961f, 0.01174926758f, 0.0116642369f, + 0.01157792099f, 0.01149045862f, 0.01140198205f, 0.01131262258f, + 0.01122250315f, 0.01113174483f, 0.01121826563f, 0.01130394638f, + 0.01138866507f, 0.01147229597f, 0.0115547115f, 0.01163577568f, + 0.0117153544f, 0.0117933061f, 0.01186948828f, 0.01194375753f, + 0.01201596763f, 0.01208597142f, 0.0121536199f, 0.01221876871f, + 0.01228126884f, 0.01234097779f, 0.01239775307f, 0.01245145593f, + 0.01250195317f, 0.01254911628f, 0.01259282045f, 0.01263295114f, + 0.01266939752f, 0.01270206179f, 0.01273085084f, 0.01275568269f, + 0.01277648844f, 0.01279320568f, 0.01280578785f, 0.01281419583f, + 0.01281840634f, 0.01281840634f, 0.01281419583f, 0.01280578785f, + 0.01279320568f, 0.01277648844f, 0.01275568269f, 0.01273085084f, + 0.01270206179f, 0.01266939752f, 0.01263295114f, 0.01259282045f, + 0.01254911628f, 0.01250195317f, 0.01245145593f, 0.01239775307f, + 0.01234097779f, 0.01228126884f, 0.01221876871f, 0.0121536199f, + 0.01208597142f, 0.01201596763f, 0.01194375753f, 0.01186948828f, + 0.0117933061f, 0.0117153544f, 0.01163577568f, 0.0115547115f, + 0.01147229597f, 0.01138866507f, 0.01130394638f, 0.01121826563f, + 0.0112239169f, 0.01131262258f, 0.01140050031f, 0.0114874253f, + 0.0115732681f, 0.01165789459f, 0.011741166f, 0.01182294171f, + 0.0119030755f, 0.01198141929f, 0.01205782313f, 0.01213213522f, + 0.01220420003f, 0.01227386575f, 0.01234097779f, 0.01240538247f, + 0.01246692892f, 0.01252546813f, 0.01258085575f, 0.01263295114f, + 0.0126816174f, 0.01272672601f, 0.01276815403f, 0.01280578785f, + 0.01283952035f, 0.01286925655f, 0.0128949089f, 0.01291640475f, + 0.01293367799f, 0.01294667926f, 0.01295536757f, 0.0129597187f, + 0.0129597187f, 0.01295536757f, 0.01294667926f, 0.01293367799f, + 0.01291640475f, 0.0128949089f, 0.01286925655f, 0.01283952035f, + 0.01280578785f, 0.01276815403f, 0.01272672601f, 0.0126816174f, + 0.01263295114f, 0.01258085575f, 0.01252546813f, 0.01246692892f, + 0.01240538247f, 0.01234097779f, 0.01227386575f, 0.01220420003f, + 0.01213213522f, 0.01205782313f, 0.01198141929f, 0.0119030755f, + 0.01182294171f, 0.011741166f, 0.01165789459f, 0.0115732681f, + 0.0114874253f, 0.01140050031f, 0.01131262258f, 0.0112239169f, + 0.01113450434f, 0.01122250315f, 0.01130972803f, 0.0113960579f, + 0.01148136612f, 0.01156552508f, 0.01164839976f, 0.0117298523f, + 0.01180974208f, 0.01188792568f, 0.01196425594f, 0.01203858573f, + 0.01211076323f, 0.0121806385f, 0.0122480616f, 0.01231288072f, + 0.01237494871f, 0.01243411843f, 0.01249024551f, 0.0125431912f, + 0.01259282045f, 0.01263900381f, 0.0126816174f, 0.01272054669f, + 0.01275568269f, 0.0127869295f, 0.01281419583f, 0.01283740439f, + 0.01285648718f, 0.01287138835f, 0.01288206317f, 0.01288848184f, + 0.01289062295f, 0.01288848184f, 0.01288206317f, 0.01287138835f, + 0.01285648718f, 0.01283740439f, 0.01281419583f, 0.0127869295f, + 0.01275568269f, 0.01272054669f, 0.0126816174f, 0.01263900381f, + 0.01259282045f, 0.0125431912f, 0.01249024551f, 0.01243411843f, + 0.01237494871f, 0.01231288072f, 0.0122480616f, 0.0121806385f, + 0.01211076323f, 0.01203858573f, 0.01196425594f, 0.01188792568f, + 0.01180974208f, 0.0117298523f, 0.01164839976f, 0.01156552508f, + 0.01148136612f, 0.0113960579f, 0.01130972803f, 0.01122250315f +}; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/math/QuantizedUnitVec.h b/openvdb/math/QuantizedUnitVec.h new file mode 100644 index 00000000..9163fab7 --- /dev/null +++ b/openvdb/math/QuantizedUnitVec.h @@ -0,0 +1,121 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_QUANTIZED_UNIT_VEC_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_QUANTIZED_UNIT_VEC_HAS_BEEN_INCLUDED + +#include +#include +#include "Vec3.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief Unit vector occupying only 16 bits +/// @details Stores two quantized components. Based on the +/// "Higher Accuracy Quantized Normals" article from GameDev.Net LLC, 2000 +class OPENVDB_API QuantizedUnitVec +{ +public: + template static uint16_t pack(const Vec3& vec); + static Vec3s unpack(const uint16_t data); + + static void flipSignBits(uint16_t&); + +private: + QuantizedUnitVec() {} + + // bit masks + static const uint16_t MASK_SLOTS = 0x1FFF; // 0001111111111111 + static const uint16_t MASK_XSLOT = 0x1F80; // 0001111110000000 + static const uint16_t MASK_YSLOT = 0x007F; // 0000000001111111 + static const uint16_t MASK_XSIGN = 0x8000; // 1000000000000000 + static const uint16_t MASK_YSIGN = 0x4000; // 0100000000000000 + static const uint16_t MASK_ZSIGN = 0x2000; // 0010000000000000 + + // normalization weights, 32 kilobytes. + static float sNormalizationWeights[MASK_SLOTS + 1]; +}; // class QuantizedUnitVec + + +//////////////////////////////////////// + + +template +inline uint16_t +QuantizedUnitVec::pack(const Vec3& vec) +{ + if (math::isZero(vec)) return 0; + + uint16_t data = 0; + T x(vec[0]), y(vec[1]), z(vec[2]); + + // The sign of the three components are first stored using + // 3-bits and can then safely be discarded. + if (x < T(0.0)) { data |= MASK_XSIGN; x = -x; } + if (y < T(0.0)) { data |= MASK_YSIGN; y = -y; } + if (z < T(0.0)) { data |= MASK_ZSIGN; z = -z; } + + // The z component is discarded and x & y are quantized in + // the 0 to 126 range. + T w = T(126.0) / (x + y + z); + uint16_t xbits = static_cast((x * w)); + uint16_t ybits = static_cast((y * w)); + + // The remaining 13 bits in our 16 bit word are dividied into a + // 6-bit x-slot and a 7-bit y-slot. Both the xbits and the ybits + // can still be represented using (2^7 - 1) quantization levels. + + // If the xbits requre more than 6-bits, store the complement. + // (xbits + ybits < 127, thus if xbits > 63 => ybits <= 63) + if (xbits > 63) { + xbits = static_cast(127 - xbits); + ybits = static_cast(127 - ybits); + } + + // Pack components into their respective slots. + data = static_cast(data | (xbits << 7)); + data = static_cast(data | ybits); + return data; +} + + +inline Vec3s +QuantizedUnitVec::unpack(const uint16_t data) +{ + const float w = sNormalizationWeights[data & MASK_SLOTS]; + + uint16_t xbits = static_cast((data & MASK_XSLOT) >> 7); + uint16_t ybits = static_cast(data & MASK_YSLOT); + + // Check if the complement components where stored and revert. + if ((xbits + ybits) > 126) { + xbits = static_cast(127 - xbits); + ybits = static_cast(127 - ybits); + } + + Vec3s vec(float(xbits) * w, float(ybits) * w, float(126 - xbits - ybits) * w); + + if (data & MASK_XSIGN) vec[0] = -vec[0]; + if (data & MASK_YSIGN) vec[1] = -vec[1]; + if (data & MASK_ZSIGN) vec[2] = -vec[2]; + return vec; +} + + +//////////////////////////////////////// + + +inline void +QuantizedUnitVec::flipSignBits(uint16_t& v) +{ + v = static_cast((v & MASK_SLOTS) | (~v & ~MASK_SLOTS)); +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_QUANTIZED_UNIT_VEC_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Quat.h b/openvdb/math/Quat.h new file mode 100644 index 00000000..4f6a3e96 --- /dev/null +++ b/openvdb/math/Quat.h @@ -0,0 +1,631 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_QUAT_H_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_QUAT_H_HAS_BEEN_INCLUDED + +#include "Mat.h" +#include "Mat3.h" +#include "Math.h" +#include "Vec3.h" +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Quat; + +/// Linear interpolation between the two quaternions +template +Quat slerp(const Quat &q1, const Quat &q2, T t, T tolerance=0.00001) +{ + T qdot, angle, sineAngle; + + qdot = q1.dot(q2); + + if (fabs(qdot) >= 1.0) { + angle = 0; // not necessary but suppresses compiler warning + sineAngle = 0; + } else { + angle = acos(qdot); + sineAngle = sin(angle); + } + + // + // Denominator close to 0 corresponds to the case where the + // two quaternions are close to the same rotation. In this + // case linear interpolation is used but we normalize to + // guarantee unit length + // + if (sineAngle <= tolerance) { + T s = 1.0 - t; + + Quat qtemp(s * q1[0] + t * q2[0], s * q1[1] + t * q2[1], + s * q1[2] + t * q2[2], s * q1[3] + t * q2[3]); + // + // Check the case where two close to antipodal quaternions were + // blended resulting in a nearly zero result which can happen, + // for example, if t is close to 0.5. In this case it is not safe + // to project back onto the sphere. + // + double lengthSquared = qtemp.dot(qtemp); + + if (lengthSquared <= tolerance * tolerance) { + qtemp = (t < 0.5) ? q1 : q2; + } else { + qtemp *= 1.0 / sqrt(lengthSquared); + } + return qtemp; + } else { + + T sine = 1.0 / sineAngle; + T a = sin((1.0 - t) * angle) * sine; + T b = sin(t * angle) * sine; + return Quat(a * q1[0] + b * q2[0], a * q1[1] + b * q2[1], + a * q1[2] + b * q2[2], a * q1[3] + b * q2[3]); + } + +} + +template +class Quat +{ +public: + using value_type = T; + using ValueType = T; + static const int size = 4; + + /// Trivial constructor, the quaternion is NOT initialized + Quat() {} + + /// Constructor with four arguments, e.g. Quatf q(1,2,3,4); + Quat(T x, T y, T z, T w) + { + mm[0] = x; + mm[1] = y; + mm[2] = z; + mm[3] = w; + + } + + /// Constructor with array argument, e.g. float a[4]; Quatf q(a); + Quat(T *a) + { + mm[0] = a[0]; + mm[1] = a[1]; + mm[2] = a[2]; + mm[3] = a[3]; + + } + + /// Constructor given rotation as axis and angle, the axis must be + /// unit vector + Quat(const Vec3 &axis, T angle) + { + // assert( REL_EQ(axis.length(), 1.) ); + + T s = T(sin(angle*T(0.5))); + + mm[0] = axis.x() * s; + mm[1] = axis.y() * s; + mm[2] = axis.z() * s; + + mm[3] = T(cos(angle*T(0.5))); + + } + + /// Constructor given rotation as axis and angle + Quat(math::Axis axis, T angle) + { + T s = T(sin(angle*T(0.5))); + + mm[0] = (axis==math::X_AXIS) * s; + mm[1] = (axis==math::Y_AXIS) * s; + mm[2] = (axis==math::Z_AXIS) * s; + + mm[3] = T(cos(angle*T(0.5))); + } + + /// Constructor given a rotation matrix + template + Quat(const Mat3 &rot) { + + // verify that the matrix is really a rotation + if(!isUnitary(rot)) { // unitary is reflection or rotation + OPENVDB_THROW(ArithmeticError, + "A non-rotation matrix can not be used to construct a quaternion"); + } + if (!isApproxEqual(rot.det(), T1(1))) { // rule out reflection + OPENVDB_THROW(ArithmeticError, + "A reflection matrix can not be used to construct a quaternion"); + } + + T trace(rot.trace()); + if (trace > 0) { + + T q_w = 0.5 * std::sqrt(trace+1); + T factor = 0.25 / q_w; + + mm[0] = factor * (rot(1,2) - rot(2,1)); + mm[1] = factor * (rot(2,0) - rot(0,2)); + mm[2] = factor * (rot(0,1) - rot(1,0)); + mm[3] = q_w; + } else if (rot(0,0) > rot(1,1) && rot(0,0) > rot(2,2)) { + + T q_x = 0.5 * sqrt(rot(0,0)- rot(1,1)-rot(2,2)+1); + T factor = 0.25 / q_x; + + mm[0] = q_x; + mm[1] = factor * (rot(0,1) + rot(1,0)); + mm[2] = factor * (rot(2,0) + rot(0,2)); + mm[3] = factor * (rot(1,2) - rot(2,1)); + } else if (rot(1,1) > rot(2,2)) { + + T q_y = 0.5 * sqrt(rot(1,1)-rot(0,0)-rot(2,2)+1); + T factor = 0.25 / q_y; + + mm[0] = factor * (rot(0,1) + rot(1,0)); + mm[1] = q_y; + mm[2] = factor * (rot(1,2) + rot(2,1)); + mm[3] = factor * (rot(2,0) - rot(0,2)); + } else { + + T q_z = 0.5 * sqrt(rot(2,2)-rot(0,0)-rot(1,1)+1); + T factor = 0.25 / q_z; + + mm[0] = factor * (rot(2,0) + rot(0,2)); + mm[1] = factor * (rot(1,2) + rot(2,1)); + mm[2] = q_z; + mm[3] = factor * (rot(0,1) - rot(1,0)); + } + } + + /// Copy constructor + Quat(const Quat &q) + { + mm[0] = q.mm[0]; + mm[1] = q.mm[1]; + mm[2] = q.mm[2]; + mm[3] = q.mm[3]; + + } + + /// Reference to the component, e.g. q.x() = 4.5f; + T& x() { return mm[0]; } + T& y() { return mm[1]; } + T& z() { return mm[2]; } + T& w() { return mm[3]; } + + /// Get the component, e.g. float f = q.w(); + T x() const { return mm[0]; } + T y() const { return mm[1]; } + T z() const { return mm[2]; } + T w() const { return mm[3]; } + + // Number of elements + static unsigned numElements() { return 4; } + + /// Array style reference to the components, e.g. q[3] = 1.34f; + T& operator[](int i) { return mm[i]; } + + /// Array style constant reference to the components, e.g. float f = q[1]; + T operator[](int i) const { return mm[i]; } + + /// Cast to T* + operator T*() { return mm; } + operator const T*() const { return mm; } + + /// Alternative indexed reference to the elements + T& operator()(int i) { return mm[i]; } + + /// Alternative indexed constant reference to the elements, + T operator()(int i) const { return mm[i]; } + + /// Return angle of rotation + T angle() const + { + T sqrLength = mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2]; + + if ( sqrLength > 1.0e-8 ) { + + return T(T(2.0) * acos(mm[3])); + + } else { + + return T(0.0); + } + } + + /// Return axis of rotation + Vec3 axis() const + { + T sqrLength = mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2]; + + if ( sqrLength > 1.0e-8 ) { + + T invLength = T(T(1)/sqrt(sqrLength)); + + return Vec3( mm[0]*invLength, mm[1]*invLength, mm[2]*invLength ); + } else { + + return Vec3(1,0,0); + } + } + + + /// "this" quaternion gets initialized to [x, y, z, w] + Quat& init(T x, T y, T z, T w) + { + mm[0] = x; mm[1] = y; mm[2] = z; mm[3] = w; + return *this; + } + + /// "this" quaternion gets initialized to identity, same as setIdentity() + Quat& init() { return setIdentity(); } + + /// Set "this" quaternion to rotation specified by axis and angle, + /// the axis must be unit vector + Quat& setAxisAngle(const Vec3& axis, T angle) + { + + T s = T(sin(angle*T(0.5))); + + mm[0] = axis.x() * s; + mm[1] = axis.y() * s; + mm[2] = axis.z() * s; + + mm[3] = T(cos(angle*T(0.5))); + + return *this; + } // axisAngleTest + + /// Set "this" vector to zero + Quat& setZero() + { + mm[0] = mm[1] = mm[2] = mm[3] = 0; + return *this; + } + + /// Set "this" vector to identity + Quat& setIdentity() + { + mm[0] = mm[1] = mm[2] = 0; + mm[3] = 1; + return *this; + } + + /// Returns vector of x,y,z rotational components + Vec3 eulerAngles(RotationOrder rotationOrder) const + { return math::eulerAngles(Mat3(*this), rotationOrder); } + + /// Assignment operator + Quat& operator=(const Quat &q) + { + mm[0] = q.mm[0]; + mm[1] = q.mm[1]; + mm[2] = q.mm[2]; + mm[3] = q.mm[3]; + + return *this; + } + + /// Equality operator, does exact floating point comparisons + bool operator==(const Quat &q) const + { + return (isExactlyEqual(mm[0],q.mm[0]) && + isExactlyEqual(mm[1],q.mm[1]) && + isExactlyEqual(mm[2],q.mm[2]) && + isExactlyEqual(mm[3],q.mm[3]) ); + } + + /// Test if "this" is equivalent to q with tolerance of eps value + bool eq(const Quat &q, T eps=1.0e-7) const + { + return isApproxEqual(mm[0],q.mm[0],eps) && isApproxEqual(mm[1],q.mm[1],eps) && + isApproxEqual(mm[2],q.mm[2],eps) && isApproxEqual(mm[3],q.mm[3],eps) ; + } // trivial + + /// Add quaternion q to "this" quaternion, e.g. q += q1; + Quat& operator+=(const Quat &q) + { + mm[0] += q.mm[0]; + mm[1] += q.mm[1]; + mm[2] += q.mm[2]; + mm[3] += q.mm[3]; + + return *this; + } + + /// Subtract quaternion q from "this" quaternion, e.g. q -= q1; + Quat& operator-=(const Quat &q) + { + mm[0] -= q.mm[0]; + mm[1] -= q.mm[1]; + mm[2] -= q.mm[2]; + mm[3] -= q.mm[3]; + + return *this; + } + + /// Scale "this" quaternion by scalar, e.g. q *= scalar; + Quat& operator*=(T scalar) + { + mm[0] *= scalar; + mm[1] *= scalar; + mm[2] *= scalar; + mm[3] *= scalar; + + return *this; + } + + /// Return (this+q), e.g. q = q1 + q2; + Quat operator+(const Quat &q) const + { + return Quat(mm[0]+q.mm[0], mm[1]+q.mm[1], mm[2]+q.mm[2], mm[3]+q.mm[3]); + } + + /// Return (this-q), e.g. q = q1 - q2; + Quat operator-(const Quat &q) const + { + return Quat(mm[0]-q.mm[0], mm[1]-q.mm[1], mm[2]-q.mm[2], mm[3]-q.mm[3]); + } + + /// Return (this*q), e.g. q = q1 * q2; + Quat operator*(const Quat &q) const + { + Quat prod; + + prod.mm[0] = mm[3]*q.mm[0] + mm[0]*q.mm[3] + mm[1]*q.mm[2] - mm[2]*q.mm[1]; + prod.mm[1] = mm[3]*q.mm[1] + mm[1]*q.mm[3] + mm[2]*q.mm[0] - mm[0]*q.mm[2]; + prod.mm[2] = mm[3]*q.mm[2] + mm[2]*q.mm[3] + mm[0]*q.mm[1] - mm[1]*q.mm[0]; + prod.mm[3] = mm[3]*q.mm[3] - mm[0]*q.mm[0] - mm[1]*q.mm[1] - mm[2]*q.mm[2]; + + return prod; + + } + + /// Assigns this to (this*q), e.g. q *= q1; + Quat operator*=(const Quat &q) + { + *this = *this * q; + return *this; + } + + /// Return (this*scalar), e.g. q = q1 * scalar; + Quat operator*(T scalar) const + { + return Quat(mm[0]*scalar, mm[1]*scalar, mm[2]*scalar, mm[3]*scalar); + } + + /// Return (this/scalar), e.g. q = q1 / scalar; + Quat operator/(T scalar) const + { + return Quat(mm[0]/scalar, mm[1]/scalar, mm[2]/scalar, mm[3]/scalar); + } + + /// Negation operator, e.g. q = -q; + Quat operator-() const + { return Quat(-mm[0], -mm[1], -mm[2], -mm[3]); } + + /// this = q1 + q2 + /// "this", q1 and q2 need not be distinct objects, e.g. q.add(q1,q); + Quat& add(const Quat &q1, const Quat &q2) + { + mm[0] = q1.mm[0] + q2.mm[0]; + mm[1] = q1.mm[1] + q2.mm[1]; + mm[2] = q1.mm[2] + q2.mm[2]; + mm[3] = q1.mm[3] + q2.mm[3]; + + return *this; + } + + /// this = q1 - q2 + /// "this", q1 and q2 need not be distinct objects, e.g. q.sub(q1,q); + Quat& sub(const Quat &q1, const Quat &q2) + { + mm[0] = q1.mm[0] - q2.mm[0]; + mm[1] = q1.mm[1] - q2.mm[1]; + mm[2] = q1.mm[2] - q2.mm[2]; + mm[3] = q1.mm[3] - q2.mm[3]; + + return *this; + } + + /// this = q1 * q2 + /// q1 and q2 must be distinct objects than "this", e.g. q.mult(q1,q2); + Quat& mult(const Quat &q1, const Quat &q2) + { + mm[0] = q1.mm[3]*q2.mm[0] + q1.mm[0]*q2.mm[3] + + q1.mm[1]*q2.mm[2] - q1.mm[2]*q2.mm[1]; + mm[1] = q1.mm[3]*q2.mm[1] + q1.mm[1]*q2.mm[3] + + q1.mm[2]*q2.mm[0] - q1.mm[0]*q2.mm[2]; + mm[2] = q1.mm[3]*q2.mm[2] + q1.mm[2]*q2.mm[3] + + q1.mm[0]*q2.mm[1] - q1.mm[1]*q2.mm[0]; + mm[3] = q1.mm[3]*q2.mm[3] - q1.mm[0]*q2.mm[0] - + q1.mm[1]*q2.mm[1] - q1.mm[2]*q2.mm[2]; + + return *this; + } + + /// this = scalar*q, q need not be distinct object than "this", + /// e.g. q.scale(1.5,q1); + Quat& scale(T scale, const Quat &q) + { + mm[0] = scale * q.mm[0]; + mm[1] = scale * q.mm[1]; + mm[2] = scale * q.mm[2]; + mm[3] = scale * q.mm[3]; + + return *this; + } + + /// Dot product + T dot(const Quat &q) const + { + return (mm[0]*q.mm[0] + mm[1]*q.mm[1] + mm[2]*q.mm[2] + mm[3]*q.mm[3]); + } + + /// Return the quaternion rate corrsponding to the angular velocity omega + /// and "this" current rotation + Quat derivative(const Vec3& omega) const + { + return Quat( +w()*omega.x() -z()*omega.y() +y()*omega.z() , + +z()*omega.x() +w()*omega.y() -x()*omega.z() , + -y()*omega.x() +x()*omega.y() +w()*omega.z() , + -x()*omega.x() -y()*omega.y() -z()*omega.z() ); + } + + /// this = normalized this + bool normalize(T eps = T(1.0e-8)) + { + T d = T(sqrt(mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2] + mm[3]*mm[3])); + if( isApproxEqual(d, T(0.0), eps) ) return false; + *this *= ( T(1)/d ); + return true; + } + + /// this = normalized this + Quat unit() const + { + T d = sqrt(mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2] + mm[3]*mm[3]); + if( isExactlyEqual(d , T(0.0) ) ) + OPENVDB_THROW(ArithmeticError, + "Normalizing degenerate quaternion"); + return *this / d; + } + + /// returns inverse of this + Quat inverse(T tolerance = T(0)) + { + T d = mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2] + mm[3]*mm[3]; + if( isApproxEqual(d, T(0.0), tolerance) ) + OPENVDB_THROW(ArithmeticError, + "Cannot invert degenerate quaternion"); + Quat result = *this/-d; + result.mm[3] = -result.mm[3]; + return result; + } + + + /// Return the conjugate of "this", same as invert without + /// unit quaternion test + Quat conjugate() const + { + return Quat(-mm[0], -mm[1], -mm[2], mm[3]); + } + + /// Return rotated vector by "this" quaternion + Vec3 rotateVector(const Vec3 &v) const + { + Mat3 m(*this); + return m.transform(v); + } + + /// Predefined constants, e.g. Quat q = Quat::identity(); + static Quat zero() { return Quat(0,0,0,0); } + static Quat identity() { return Quat(0,0,0,1); } + + /// @return string representation of Classname + std::string str() const + { + std::ostringstream buffer; + + buffer << "["; + + // For each column + for (unsigned j(0); j < 4; j++) { + if (j) buffer << ", "; + buffer << mm[j]; + } + + buffer << "]"; + + return buffer.str(); + } + + /// Output to the stream, e.g. std::cout << q << std::endl; + friend std::ostream& operator<<(std::ostream &stream, const Quat &q) + { + stream << q.str(); + return stream; + } + + friend Quat slerp<>(const Quat &q1, const Quat &q2, T t, T tolerance); + + void write(std::ostream& os) const { os.write(static_cast(&mm), sizeof(T) * 4); } + void read(std::istream& is) { is.read(static_cast(&mm), sizeof(T) * 4); } + +protected: + T mm[4]; +}; + +/// Multiply each element of the given quaternion by @a scalar and return the result. +template +Quat operator*(S scalar, const Quat &q) { return q*scalar; } + + +/// @brief Interpolate between m1 and m2. +/// Converts to quaternion form and uses slerp +/// m1 and m2 must be rotation matrices! +template +Mat3 slerp(const Mat3 &m1, const Mat3 &m2, T t) +{ + using MatType = Mat3; + + Quat q1(m1); + Quat q2(m2); + + if (q1.dot(q2) < 0) q2 *= -1; + + Quat qslerp = slerp(q1, q2, static_cast(t)); + MatType m = rotation(qslerp); + return m; +} + + + +/// Interpolate between m1 and m4 by converting m1 ... m4 into +/// quaternions and treating them as control points of a Bezier +/// curve using slerp in place of lerp in the De Castlejeau evaluation +/// algorithm. Just like a cubic Bezier curve, this will interpolate +/// m1 at t = 0 and m4 at t = 1 but in general will not pass through +/// m2 and m3. Unlike a standard Bezier curve this curve will not have +/// the convex hull property. +/// m1 ... m4 must be rotation matrices! +template +Mat3 bezLerp(const Mat3 &m1, const Mat3 &m2, + const Mat3 &m3, const Mat3 &m4, + T t) +{ + Mat3 m00, m01, m02, m10, m11; + + m00 = slerp(m1, m2, t); + m01 = slerp(m2, m3, t); + m02 = slerp(m3, m4, t); + + m10 = slerp(m00, m01, t); + m11 = slerp(m01, m02, t); + + return slerp(m10, m11, t); +} + +using Quats = Quat; +using Quatd = Quat; + +} // namespace math + + +template<> inline math::Quats zeroVal() { return math::Quats::zero(); } +template<> inline math::Quatd zeroVal() { return math::Quatd::zero(); } + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif //OPENVDB_MATH_QUAT_H_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Ray.h b/openvdb/math/Ray.h new file mode 100644 index 00000000..e0d92b01 --- /dev/null +++ b/openvdb/math/Ray.h @@ -0,0 +1,311 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Ray.h +/// +/// @author Ken Museth +/// +/// @brief A Ray class. + +#ifndef OPENVDB_MATH_RAY_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_RAY_HAS_BEEN_INCLUDED + +#include "Math.h" +#include "Vec3.h" +#include "Transform.h" +#include // for std::swap() +#include // for std::ostream +#include // for std::numeric_limits::max() + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template +class Ray +{ +public: + static_assert(std::is_floating_point::value, + "math::Ray requires a floating-point value type"); + + using RealType = RealT; + using Vec3Type = Vec3; + using Vec3T = Vec3Type; + + struct TimeSpan { + RealT t0, t1; + /// @brief Default constructor + TimeSpan() {} + /// @brief Constructor + TimeSpan(RealT _t0, RealT _t1) : t0(_t0), t1(_t1) {} + /// @brief Set both times + inline void set(RealT _t0, RealT _t1) { t0=_t0; t1=_t1; } + /// @brief Get both times + inline void get(RealT& _t0, RealT& _t1) const { _t0=t0; _t1=t1; } + /// @brief Return @c true if t1 is larger than t0 by at least eps. + inline bool valid(RealT eps=math::Delta::value()) const { return (t1-t0)>eps; } + /// @brief Return the midpoint of the ray. + inline RealT mid() const { return 0.5*(t0 + t1); } + /// @brief Multiplies both times + inline void scale(RealT s) {assert(s>0); t0*=s; t1*=s; } + /// @brief Return @c true if time is inclusive + inline bool test(RealT t) const { return (t>=t0 && t<=t1); } + }; + + Ray(const Vec3Type& eye = Vec3Type(0,0,0), + const Vec3Type& direction = Vec3Type(1,0,0), + RealT t0 = math::Delta::value(), + RealT t1 = std::numeric_limits::max()) + : mEye(eye), mDir(direction), mInvDir(1/mDir), mTimeSpan(t0, t1) + { + } + + inline void setEye(const Vec3Type& eye) { mEye = eye; } + + inline void setDir(const Vec3Type& dir) + { + mDir = dir; + mInvDir = 1/mDir; + } + + inline void setMinTime(RealT t0) { assert(t0>0); mTimeSpan.t0 = t0; } + + inline void setMaxTime(RealT t1) { assert(t1>0); mTimeSpan.t1 = t1; } + + inline void setTimes( + RealT t0 = math::Delta::value(), + RealT t1 = std::numeric_limits::max()) + { + assert(t0>0 && t1>0); + mTimeSpan.set(t0, t1); + } + + inline void scaleTimes(RealT scale) { mTimeSpan.scale(scale); } + + inline void reset( + const Vec3Type& eye, + const Vec3Type& direction, + RealT t0 = math::Delta::value(), + RealT t1 = std::numeric_limits::max()) + { + this->setEye(eye); + this->setDir(direction); + this->setTimes(t0, t1); + } + + inline const Vec3T& eye() const {return mEye;} + + inline const Vec3T& dir() const {return mDir;} + + inline const Vec3T& invDir() const {return mInvDir;} + + inline RealT t0() const {return mTimeSpan.t0;} + + inline RealT t1() const {return mTimeSpan.t1;} + + /// @brief Return the position along the ray at the specified time. + inline Vec3R operator()(RealT time) const { return mEye + mDir * time; } + + /// @brief Return the starting point of the ray. + inline Vec3R start() const { return (*this)(mTimeSpan.t0); } + + /// @brief Return the endpoint of the ray. + inline Vec3R end() const { return (*this)(mTimeSpan.t1); } + + /// @brief Return the midpoint of the ray. + inline Vec3R mid() const { return (*this)(mTimeSpan.mid()); } + + /// @brief Return @c true if t1 is larger than t0 by at least eps. + inline bool valid(RealT eps=math::Delta::value()) const { return mTimeSpan.valid(eps); } + + /// @brief Return @c true if @a time is within t0 and t1, both inclusive. + inline bool test(RealT time) const { return mTimeSpan.test(time); } + + /// @brief Return a new Ray that is transformed with the specified map. + /// @param map the map from which to construct the new Ray. + /// @warning Assumes a linear map and a normalized direction. + /// @details The requirement that the direction is normalized + /// follows from the transformation of t0 and t1 - and that fact that + /// we want applyMap and applyInverseMap to be inverse operations. + template + inline Ray applyMap(const MapType& map) const + { + assert(map.isLinear()); + assert(math::isRelOrApproxEqual(mDir.length(), RealT(1), + Tolerance::value(), Delta::value())); + const Vec3T eye = map.applyMap(mEye); + const Vec3T dir = map.applyJacobian(mDir); + const RealT length = dir.length(); + return Ray(eye, dir/length, length*mTimeSpan.t0, length*mTimeSpan.t1); + } + + /// @brief Return a new Ray that is transformed with the inverse of the specified map. + /// @param map the map from which to construct the new Ray by inverse mapping. + /// @warning Assumes a linear map and a normalized direction. + /// @details The requirement that the direction is normalized + /// follows from the transformation of t0 and t1 - and that fact that + /// we want applyMap and applyInverseMap to be inverse operations. + template + inline Ray applyInverseMap(const MapType& map) const + { + assert(map.isLinear()); + assert(math::isRelOrApproxEqual(mDir.length(), RealT(1), Tolerance::value(), Delta::value())); + const Vec3T eye = map.applyInverseMap(mEye); + const Vec3T dir = map.applyInverseJacobian(mDir); + const RealT length = dir.length(); + return Ray(eye, dir/length, length*mTimeSpan.t0, length*mTimeSpan.t1); + } + + /// @brief Return a new ray in world space, assuming the existing + /// ray is represented in the index space of the specified grid. + template + inline Ray indexToWorld(const GridType& grid) const + { + return this->applyMap(*(grid.transform().baseMap())); + } + + /// @brief Return a new ray in the index space of the specified + /// grid, assuming the existing ray is represented in world space. + template + inline Ray worldToIndex(const GridType& grid) const + { + return this->applyInverseMap(*(grid.transform().baseMap())); + } + + /// @brief Return true if this ray intersects the specified sphere. + /// @param center The center of the sphere in the same space as this ray. + /// @param radius The radius of the sphere in the same units as this ray. + /// @param t0 The first intersection point if an intersection exists. + /// @param t1 The second intersection point if an intersection exists. + /// @note If the return value is true, i.e. a hit, and t0 = + /// this->t0() or t1 == this->t1() only one true intersection exist. + inline bool intersects(const Vec3T& center, RealT radius, RealT& t0, RealT& t1) const + { + const Vec3T origin = mEye - center; + const RealT A = mDir.lengthSqr(); + const RealT B = 2 * mDir.dot(origin); + const RealT C = origin.lengthSqr() - radius * radius; + const RealT D = B * B - 4 * A * C; + + if (D < 0) return false; + + const RealT Q = RealT(-0.5)*(B<0 ? (B + Sqrt(D)) : (B - Sqrt(D))); + + t0 = Q / A; + t1 = C / Q; + + if (t0 > t1) std::swap(t0, t1); + if (t0 < mTimeSpan.t0) t0 = mTimeSpan.t0; + if (t1 > mTimeSpan.t1) t1 = mTimeSpan.t1; + return t0 <= t1; + } + + /// @brief Return true if this ray intersects the specified sphere. + /// @param center The center of the sphere in the same space as this ray. + /// @param radius The radius of the sphere in the same units as this ray. + inline bool intersects(const Vec3T& center, RealT radius) const + { + RealT t0, t1; + return this->intersects(center, radius, t0, t1)>0; + } + + /// @brief Return true if this ray intersects the specified sphere. + /// @note For intersection this ray is clipped to the two intersection points. + /// @param center The center of the sphere in the same space as this ray. + /// @param radius The radius of the sphere in the same units as this ray. + inline bool clip(const Vec3T& center, RealT radius) + { + RealT t0, t1; + const bool hit = this->intersects(center, radius, t0, t1); + if (hit) mTimeSpan.set(t0, t1); + return hit; + } + + /// @brief Return true if the Ray intersects the specified + /// axisaligned bounding box. + /// @param bbox Axis-aligned bounding box in the same space as the Ray. + /// @param t0 If an intersection is detected this is assigned + /// the time for the first intersection point. + /// @param t1 If an intersection is detected this is assigned + /// the time for the second intersection point. + template + inline bool intersects(const BBoxT& bbox, RealT& t0, RealT& t1) const + { + mTimeSpan.get(t0, t1); + for (int i = 0; i < 3; ++i) { + RealT a = (bbox.min()[i] - mEye[i]) * mInvDir[i]; + RealT b = (bbox.max()[i] - mEye[i]) * mInvDir[i]; + if (a > b) std::swap(a, b); + if (a > t0) t0 = a; + if (b < t1) t1 = b; + if (t0 > t1) return false; + } + return true; + } + + /// @brief Return true if this ray intersects the specified bounding box. + /// @param bbox Axis-aligned bounding box in the same space as this ray. + template + inline bool intersects(const BBoxT& bbox) const + { + RealT t0, t1; + return this->intersects(bbox, t0, t1); + } + + /// @brief Return true if this ray intersects the specified bounding box. + /// @note For intersection this ray is clipped to the two intersection points. + /// @param bbox Axis-aligned bounding box in the same space as this ray. + template + inline bool clip(const BBoxT& bbox) + { + RealT t0, t1; + const bool hit = this->intersects(bbox, t0, t1); + if (hit) mTimeSpan.set(t0, t1); + return hit; + } + + /// @brief Return true if the Ray intersects the plane specified + /// by a normal and distance from the origin. + /// @param normal Normal of the plane. + /// @param distance Distance of the plane to the origin. + /// @param t Time of intersection, if one exists. + inline bool intersects(const Vec3T& normal, RealT distance, RealT& t) const + { + const RealT cosAngle = mDir.dot(normal); + if (math::isApproxZero(cosAngle)) return false;//parallel + t = (distance - mEye.dot(normal))/cosAngle; + return this->test(t); + } + + /// @brief Return true if the Ray intersects the plane specified + /// by a normal and point. + /// @param normal Normal of the plane. + /// @param point Point in the plane. + /// @param t Time of intersection, if one exists. + inline bool intersects(const Vec3T& normal, const Vec3T& point, RealT& t) const + { + return this->intersects(normal, point.dot(normal), t); + } + +private: + Vec3T mEye, mDir, mInvDir; + TimeSpan mTimeSpan; +}; // end of Ray class + + +/// @brief Output streaming of the Ray class. +/// @note Primarily intended for debugging. +template +inline std::ostream& operator<<(std::ostream& os, const Ray& r) +{ + os << "eye=" << r.eye() << " dir=" << r.dir() << " 1/dir="< > +class MinMax +{ + using Limits = std::numeric_limits; +public: + + /// @brief Empty constructor + /// + /// @warning Only use this constructor with POD types + MinMax() : mMin(Limits::max()), mMax(Limits::lowest()) + { + static_assert(std::numeric_limits::is_specialized, + "openvdb::math::MinMax default constructor requires a std::numeric_limits specialization"); + } + + /// @brief Constructor + MinMax(const ValueType &min, const ValueType &max) : mMin(min), mMax(max) + { + } + + /// @brief Default copy constructor + MinMax(const MinMax &other) = default; + + /// Add a single sample. + inline void add(const ValueType &val, const Less &less = Less()) + { + if (less(val, mMin)) mMin = val; + if (less(mMax, val)) mMax = val; + } + + /// Return the minimum value. + inline const ValueType& min() const { return mMin; } + + /// Return the maximum value. + inline const ValueType& max() const { return mMax; } + + /// Add the samples from the other Stats instance. + inline void add(const MinMax& other, const Less &less = Less()) + { + if (less(other.mMin, mMin)) mMin = other.mMin; + if (less(mMax, other.mMax)) mMax = other.mMax; + } + + /// @brief Print MinMax to the specified output stream. + void print(const std::string &name= "", std::ostream &strm=std::cout, int precision=3) const + { + // Write to a temporary string stream so as not to affect the state + // (precision, field width, etc.) of the output stream. + std::ostringstream os; + os << std::setprecision(precision) << std::setiosflags(std::ios::fixed); + os << "MinMax "; + if (!name.empty()) os << "for \"" << name << "\" "; + os << " Min=" << mMin << ", Max=" << mMax << std::endl; + strm << os.str(); + } + +protected: + + ValueType mMin, mMax; +};//end MinMax + +/// @brief This class computes the minimum and maximum values of a population +/// of floating-point values. +class Extrema +{ +public: + + /// @brief Constructor + /// @warning The min/max values are initiated to extreme values + Extrema() + : mSize(0) + , mMin(std::numeric_limits::max()) + , mMax(-mMin) + { + } + + /// Add a single sample. + void add(double val) + { + ++mSize; + mMin = std::min(val, mMin); + mMax = std::max(val, mMax); + } + + /// Add @a n samples with constant value @a val. + void add(double val, uint64_t n) + { + mSize += n; + mMin = std::min(val, mMin); + mMax = std::max(val, mMax); + } + + /// Return the size of the population, i.e., the total number of samples. + inline uint64_t size() const { return mSize; } + + /// Return the minimum value. + inline double min() const { return mMin; } + + /// Return the maximum value. + inline double max() const { return mMax; } + + /// Return the range defined as the maximum value minus the minimum value. + inline double range() const { return mMax - mMin; } + + /// Add the samples from the other Stats instance. + void add(const Extrema& other) + { + if (other.mSize > 0) this->join(other); + } + + /// @brief Print extrema to the specified output stream. + void print(const std::string &name= "", std::ostream &strm=std::cout, int precision=3) const + { + // Write to a temporary string stream so as not to affect the state + // (precision, field width, etc.) of the output stream. + std::ostringstream os; + os << std::setprecision(precision) << std::setiosflags(std::ios::fixed); + os << "Extrema "; + if (!name.empty()) os << "for \"" << name << "\" "; + if (mSize>0) { + os << "with " << mSize << " samples:\n" + << " Min=" << mMin + << ", Max=" << mMax + << ", Range="<< this->range() << std::endl; + } else { + os << ": no samples were added." << std::endl; + } + strm << os.str(); + } + +protected: + + inline void join(const Extrema& other) + { + assert(other.mSize > 0); + mSize += other.mSize; + mMin = std::min(mMin, other.mMin); + mMax = std::max(mMax, other.mMax); + } + + uint64_t mSize; + double mMin, mMax; +};//end Extrema + + +/// @brief This class computes statistics (minimum value, maximum +/// value, mean, variance and standard deviation) of a population +/// of floating-point values. +/// +/// @details variance = Mean[ (X-Mean[X])^2 ] = Mean[X^2] - Mean[X]^2, +/// standard deviation = sqrt(variance) +/// +/// @note This class employs incremental computation and double precision. +class Stats : public Extrema +{ +public: + Stats() + : Extrema() + , mAvg(0.0) + , mAux(0.0) + { + } + + /// Add a single sample. + void add(double val) + { + Extrema::add(val); + const double delta = val - mAvg; + mAvg += delta/double(mSize); + mAux += delta*(val - mAvg); + } + + /// Add @a n samples with constant value @a val. + void add(double val, uint64_t n) + { + const double denom = 1.0/double(mSize + n); + const double delta = val - mAvg; + mAvg += denom * delta * double(n); + mAux += denom * delta * delta * double(mSize) * double(n); + Extrema::add(val, n); + } + + /// Add the samples from the other Stats instance. + void add(const Stats& other) + { + if (other.mSize > 0) { + const double denom = 1.0/double(mSize + other.mSize); + const double delta = other.mAvg - mAvg; + mAvg += denom * delta * double(other.mSize); + mAux += other.mAux + denom * delta * delta * double(mSize) * double(other.mSize); + Extrema::join(other); + } + } + + //@{ + /// Return the arithmetic mean, i.e. average, value. + inline double avg() const { return mAvg; } + inline double mean() const { return mAvg; } + //@} + + //@{ + /// @brief Return the population variance. + /// @note The unbiased sample variance = population variance * + //num/(num-1) + inline double var() const { return mSize<2 ? 0.0 : mAux/double(mSize); } + inline double variance() const { return this->var(); } + //@} + + //@{ + /// @brief Return the standard deviation (=Sqrt(variance)) as + /// defined from the (biased) population variance. + inline double std() const { return sqrt(this->var()); } + inline double stdDev() const { return this->std(); } + //@} + + /// @brief Print statistics to the specified output stream. + void print(const std::string &name= "", std::ostream &strm=std::cout, int precision=3) const + { + // Write to a temporary string stream so as not to affect the state + // (precision, field width, etc.) of the output stream. + std::ostringstream os; + os << std::setprecision(precision) << std::setiosflags(std::ios::fixed); + os << "Statistics "; + if (!name.empty()) os << "for \"" << name << "\" "; + if (mSize>0) { + os << "with " << mSize << " samples:\n" + << " Min=" << mMin + << ", Max=" << mMax + << ", Ave=" << mAvg + << ", Std=" << this->stdDev() + << ", Var=" << this->variance() << std::endl; + } else { + os << ": no samples were added." << std::endl; + } + strm << os.str(); + } + +protected: + using Extrema::mSize; + using Extrema::mMin; + using Extrema::mMax; + double mAvg, mAux; +}; // end Stats + + +//////////////////////////////////////// + + +/// @brief This class computes a histogram, with a fixed interval width, +/// of a population of floating-point values. +class Histogram +{ +public: + /// Construct with given minimum and maximum values and the given bin count. + Histogram(double min, double max, size_t numBins = 10) + : mSize(0), mMin(min), mMax(max + 1e-10), + mDelta(double(numBins)/(max-min)), mBins(numBins) + { + if ( mMax <= mMin ) { + OPENVDB_THROW(ValueError, "Histogram: expected min < max"); + } else if ( numBins == 0 ) { + OPENVDB_THROW(ValueError, "Histogram: expected at least one bin"); + } + for (size_t i=0; imMax) return false; + mBins[size_t(mDelta*(val-mMin))] += n; + mSize += n; + return true; + } + + /// @brief Add all the contributions from the other histogram, provided that + /// it has the same configuration as this histogram. + bool add(const Histogram& other) + { + if (!isApproxEqual(mMin, other.mMin) || !isApproxEqual(mMax, other.mMax) || + mBins.size() != other.mBins.size()) return false; + for (size_t i=0, e=mBins.size(); i!=e; ++i) mBins[i] += other.mBins[i]; + mSize += other.mSize; + return true; + } + + /// Return the number of bins in this histogram. + inline size_t numBins() const { return mBins.size(); } + /// Return the lower bound of this histogram's value range. + inline double min() const { return mMin; } + /// Return the upper bound of this histogram's value range. + inline double max() const { return mMax; } + /// Return the minimum value in the nth bin. + inline double min(int n) const { return mMin+n/mDelta; } + /// Return the maximum value in the nth bin. + inline double max(int n) const { return mMin+(n+1)/mDelta; } + /// Return the number of samples in the nth bin. + inline uint64_t count(int n) const { return mBins[n]; } + /// Return the population size, i.e., the total number of samples. + inline uint64_t size() const { return mSize; } + + /// Print the histogram to the specified output stream. + void print(const std::string& name = "", std::ostream& strm = std::cout) const + { + // Write to a temporary string stream so as not to affect the state + // (precision, field width, etc.) of the output stream. + std::ostringstream os; + os << std::setprecision(6) << std::setiosflags(std::ios::fixed) << std::endl; + os << "Histogram "; + if (!name.empty()) os << "for \"" << name << "\" "; + if (mSize > 0) { + os << "with " << mSize << " samples:\n"; + os << "==============================================================\n"; + os << "|| # | Min | Max | Frequency | % ||\n"; + os << "==============================================================\n"; + for (int i = 0, e = int(mBins.size()); i != e; ++i) { + os << "|| " << std::setw(4) << i << " | " << std::setw(14) << this->min(i) << " | " + << std::setw(14) << this->max(i) << " | " << std::setw(9) << mBins[i] << " | " + << std::setw(3) << (100*mBins[i]/mSize) << " ||\n"; + } + os << "==============================================================\n"; + } else { + os << ": no samples were added." << std::endl; + } + strm << os.str(); + } + +private: + uint64_t mSize; + double mMin, mMax, mDelta; + std::vector mBins; +};// end Histogram + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_STATS_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Stencils.h b/openvdb/math/Stencils.h new file mode 100644 index 00000000..2cb269ab --- /dev/null +++ b/openvdb/math/Stencils.h @@ -0,0 +1,1815 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @author Ken Museth +/// +/// @file Stencils.h +/// +/// @brief Defines various finite difference stencils by means of the +/// "curiously recurring template pattern" on a BaseStencil +/// that caches stencil values and stores a ValueAccessor for +/// fast lookup. + +#ifndef OPENVDB_MATH_STENCILS_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_STENCILS_HAS_BEEN_INCLUDED + +#include +#include // for std::vector +#include // for std::bitset +#include // for Pow2, needed by WENO and Godunov +#include // for Real +#include // for Coord +#include // for WENO5 and GodunovsNormSqrd +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + + +//////////////////////////////////////// + +template +class BaseStencil +{ +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridT::ValueType ValueType; + typedef tree::ValueAccessor AccessorType; + typedef std::vector BufferType; + typedef typename BufferType::iterator IterType; + + /// @brief Initialize the stencil buffer with the values of voxel (i, j, k) + /// and its neighbors. + /// @param ijk Index coordinates of stencil center + inline void moveTo(const Coord& ijk) + { + mCenter = ijk; + mValues[0] = mAcc.getValue(ijk); + static_cast(*this).init(mCenter); + } + + /// @brief Initialize the stencil buffer with the values of voxel (i, j, k) + /// and its neighbors. The method also takes a value of the center + /// element of the stencil, assuming it is already known. + /// @param ijk Index coordinates of stnecil center + /// @param centerValue Value of the center element of the stencil + inline void moveTo(const Coord& ijk, const ValueType& centerValue) + { + mCenter = ijk; + mValues[0] = centerValue; + static_cast(*this).init(mCenter); + } + + /// @brief Initialize the stencil buffer with the values of voxel + /// (x, y, z) and its neighbors. + /// + /// @note This version is slightly faster than the one above, since + /// the center voxel's value is read directly from the iterator. + template + inline void moveTo(const IterType& iter) + { + mCenter = iter.getCoord(); + mValues[0] = *iter; + static_cast(*this).init(mCenter); + } + + /// @brief Initialize the stencil buffer with the values of voxel (x, y, z) + /// and its neighbors. + /// @param xyz Floating point voxel coordinates of stencil center + /// @details This method will check to see if it is necessary to + /// update the stencil based on the cached index coordinates of + /// the center point. + template + inline void moveTo(const Vec3& xyz) + { + Coord ijk = Coord::floor(xyz); + if (ijk != mCenter) this->moveTo(ijk); + } + + /// @brief Return the value from the stencil buffer with linear + /// offset pos. + /// + /// @note The default (@a pos = 0) corresponds to the first element + /// which is typically the center point of the stencil. + inline const ValueType& getValue(unsigned int pos = 0) const + { + assert(pos < mValues.size()); + return mValues[pos]; + } + + /// @brief Return the value at the specified location relative to the center of the stencil + template + inline const ValueType& getValue() const + { + return mValues[static_cast(*this).template pos()]; + } + + /// @brief Set the value at the specified location relative to the center of the stencil + template + inline void setValue(const ValueType& value) + { + mValues[static_cast(*this).template pos()] = value; + } + + /// @brief Return the size of the stencil buffer. + inline int size() { return mValues.size(); } + + /// @brief Return the median value of the current stencil. + inline ValueType median() const + { + BufferType tmp(mValues);//local copy + assert(!tmp.empty()); + size_t midpoint = (tmp.size() - 1) >> 1; + // Partially sort the vector until the median value is at the midpoint. + std::nth_element(tmp.begin(), tmp.begin() + midpoint, tmp.end()); + return tmp[midpoint]; + } + + /// @brief Return the mean value of the current stencil. + inline ValueType mean() const + { + ValueType sum = 0.0; + for (int n = 0, s = int(mValues.size()); n < s; ++n) sum += mValues[n]; + return sum / ValueType(mValues.size()); + } + + /// @brief Return the smallest value in the stencil buffer. + inline ValueType min() const + { + IterType iter = std::min_element(mValues.begin(), mValues.end()); + return *iter; + } + + /// @brief Return the largest value in the stencil buffer. + inline ValueType max() const + { + IterType iter = std::max_element(mValues.begin(), mValues.end()); + return *iter; + } + + /// @brief Return the coordinates of the center point of the stencil. + inline const Coord& getCenterCoord() const { return mCenter; } + + /// @brief Return the value at the center of the stencil + inline const ValueType& getCenterValue() const { return mValues[0]; } + + /// @brief Return true if the center of the stencil intersects the + /// iso-contour specified by the isoValue + inline bool intersects(const ValueType &isoValue = zeroVal()) const + { + const bool less = this->getValue< 0, 0, 0>() < isoValue; + return (less ^ (this->getValue<-1, 0, 0>() < isoValue)) || + (less ^ (this->getValue< 1, 0, 0>() < isoValue)) || + (less ^ (this->getValue< 0,-1, 0>() < isoValue)) || + (less ^ (this->getValue< 0, 1, 0>() < isoValue)) || + (less ^ (this->getValue< 0, 0,-1>() < isoValue)) || + (less ^ (this->getValue< 0, 0, 1>() < isoValue)) ; + } + + /// @brief Return true a bit-mask where the 6 bits indicates if the + /// center of the stencil intersects the iso-contour specified by the isoValue. + /// + /// @note There are 2^6 = 64 different possible cases, including no intersections! + /// + /// @details The ordering of bit mask is ( -x, +x, -y, +y, -z, +z ), so to + /// check if there is an intersection in -y use mask.test(2) where mask is + /// ther return value from this function. To check if there are any + /// intersections use mask.any(), and for no intersections use mask.none(). + /// To count the number of intersections use mask.count(). + inline std::bitset<6> intersectionMask(const ValueType &isoValue = zeroVal()) const + { + std::bitset<6> mask; + const bool less = this->getValue< 0, 0, 0>() < isoValue; + mask[0] = less ^ (this->getValue<-1, 0, 0>() < isoValue); + mask[1] = less ^ (this->getValue< 1, 0, 0>() < isoValue); + mask[2] = less ^ (this->getValue< 0,-1, 0>() < isoValue); + mask[3] = less ^ (this->getValue< 0, 1, 0>() < isoValue); + mask[4] = less ^ (this->getValue< 0, 0,-1>() < isoValue); + mask[5] = less ^ (this->getValue< 0, 0, 1>() < isoValue); + return mask; + } + + /// @brief Return a const reference to the grid from which this + /// stencil was constructed. + inline const GridType& grid() const { return *mGrid; } + + /// @brief Return a const reference to the ValueAccessor + /// associated with this Stencil. + inline const AccessorType& accessor() const { return mAcc; } + +protected: + // Constructor is protected to prevent direct instantiation. + BaseStencil(const GridType& grid, int size) + : mGrid(&grid) + , mAcc(grid.tree()) + , mValues(size) + , mCenter(Coord::max()) + { + } + + const GridType* mGrid; + AccessorType mAcc; + BufferType mValues; + Coord mCenter; + +}; // BaseStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the seven point stencil + template struct SevenPt {}; + template<> struct SevenPt< 0, 0, 0> { enum { idx = 0 }; }; + template<> struct SevenPt< 1, 0, 0> { enum { idx = 1 }; }; + template<> struct SevenPt< 0, 1, 0> { enum { idx = 2 }; }; + template<> struct SevenPt< 0, 0, 1> { enum { idx = 3 }; }; + template<> struct SevenPt<-1, 0, 0> { enum { idx = 4 }; }; + template<> struct SevenPt< 0,-1, 0> { enum { idx = 5 }; }; + template<> struct SevenPt< 0, 0,-1> { enum { idx = 6 }; }; + +} + + +template +class SevenPointStencil: public BaseStencil, GridT, IsSafe> +{ + typedef SevenPointStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridT::ValueType ValueType; + + static const int SIZE = 7; + + SevenPointStencil(const GridT& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return SevenPt::idx; } + +private: + inline void init(const Coord& ijk) + { + BaseType::template setValue<-1, 0, 0>(mAcc.getValue(ijk.offsetBy(-1, 0, 0))); + BaseType::template setValue< 1, 0, 0>(mAcc.getValue(ijk.offsetBy( 1, 0, 0))); + + BaseType::template setValue< 0,-1, 0>(mAcc.getValue(ijk.offsetBy( 0,-1, 0))); + BaseType::template setValue< 0, 1, 0>(mAcc.getValue(ijk.offsetBy( 0, 1, 0))); + + BaseType::template setValue< 0, 0,-1>(mAcc.getValue(ijk.offsetBy( 0, 0,-1))); + BaseType::template setValue< 0, 0, 1>(mAcc.getValue(ijk.offsetBy( 0, 0, 1))); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; +};// SevenPointStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the eight point box stencil + template struct BoxPt {}; + template<> struct BoxPt< 0, 0, 0> { enum { idx = 0 }; }; + template<> struct BoxPt< 0, 0, 1> { enum { idx = 1 }; }; + template<> struct BoxPt< 0, 1, 1> { enum { idx = 2 }; }; + template<> struct BoxPt< 0, 1, 0> { enum { idx = 3 }; }; + template<> struct BoxPt< 1, 0, 0> { enum { idx = 4 }; }; + template<> struct BoxPt< 1, 0, 1> { enum { idx = 5 }; }; + template<> struct BoxPt< 1, 1, 1> { enum { idx = 6 }; }; + template<> struct BoxPt< 1, 1, 0> { enum { idx = 7 }; }; +} + +template +class BoxStencil: public BaseStencil, GridT, IsSafe> +{ + typedef BoxStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridT::ValueType ValueType; + + static const int SIZE = 8; + + BoxStencil(const GridType& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return BoxPt::idx; } + + /// @brief Return true if the center of the stencil intersects the + /// iso-contour specified by the isoValue + inline bool intersects(const ValueType &isoValue = zeroVal()) const + { + const bool less = mValues[0] < isoValue; + return (less ^ (mValues[1] < isoValue)) || + (less ^ (mValues[2] < isoValue)) || + (less ^ (mValues[3] < isoValue)) || + (less ^ (mValues[4] < isoValue)) || + (less ^ (mValues[5] < isoValue)) || + (less ^ (mValues[6] < isoValue)) || + (less ^ (mValues[7] < isoValue)) ; + } + + /// @brief Return the trilinear interpolation at the normalized position. + /// @param xyz Floating point coordinate position. + /// @warning It is assumed that the stencil has already been moved + /// to the relevant voxel position, e.g. using moveTo(xyz). + /// @note Trilinear interpolation kernal reads as: + /// v000 (1-u)(1-v)(1-w) + v001 (1-u)(1-v)w + v010 (1-u)v(1-w) + v011 (1-u)vw + /// + v100 u(1-v)(1-w) + v101 u(1-v)w + v110 uv(1-w) + v111 uvw + inline ValueType interpolation(const math::Vec3& xyz) const + { + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const ValueType u = xyz[0] - BaseType::mCenter[0]; + const ValueType v = xyz[1] - BaseType::mCenter[1]; + const ValueType w = xyz[2] - BaseType::mCenter[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + + assert(u>=0 && u<=1); + assert(v>=0 && v<=1); + assert(w>=0 && w<=1); + + ValueType V = BaseType::template getValue<0,0,0>(); + ValueType A = static_cast(V + (BaseType::template getValue<0,0,1>() - V) * w); + V = BaseType::template getValue< 0, 1, 0>(); + ValueType B = static_cast(V + (BaseType::template getValue<0,1,1>() - V) * w); + ValueType C = static_cast(A + (B - A) * v); + + V = BaseType::template getValue<1,0,0>(); + A = static_cast(V + (BaseType::template getValue<1,0,1>() - V) * w); + V = BaseType::template getValue<1,1,0>(); + B = static_cast(V + (BaseType::template getValue<1,1,1>() - V) * w); + ValueType D = static_cast(A + (B - A) * v); + + return static_cast(C + (D - C) * u); + } + + /// @brief Return the gradient in world space of the trilinear interpolation kernel. + /// @param xyz Floating point coordinate position. + /// @warning It is assumed that the stencil has already been moved + /// to the relevant voxel position, e.g. using moveTo(xyz). + /// @note Computed as partial derivatives of the trilinear interpolation kernel: + /// v000 (1-u)(1-v)(1-w) + v001 (1-u)(1-v)w + v010 (1-u)v(1-w) + v011 (1-u)vw + /// + v100 u(1-v)(1-w) + v101 u(1-v)w + v110 uv(1-w) + v111 uvw + inline math::Vec3 gradient(const math::Vec3& xyz) const + { + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const ValueType u = xyz[0] - BaseType::mCenter[0]; + const ValueType v = xyz[1] - BaseType::mCenter[1]; + const ValueType w = xyz[2] - BaseType::mCenter[2]; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + + assert(u>=0 && u<=1); + assert(v>=0 && v<=1); + assert(w>=0 && w<=1); + + ValueType D[4]={BaseType::template getValue<0,0,1>()-BaseType::template getValue<0,0,0>(), + BaseType::template getValue<0,1,1>()-BaseType::template getValue<0,1,0>(), + BaseType::template getValue<1,0,1>()-BaseType::template getValue<1,0,0>(), + BaseType::template getValue<1,1,1>()-BaseType::template getValue<1,1,0>()}; + + // Z component + ValueType A = static_cast(D[0] + (D[1]- D[0]) * v); + ValueType B = static_cast(D[2] + (D[3]- D[2]) * v); + math::Vec3 grad(zeroVal(), + zeroVal(), + static_cast(A + (B - A) * u)); + + D[0] = static_cast(BaseType::template getValue<0,0,0>() + D[0] * w); + D[1] = static_cast(BaseType::template getValue<0,1,0>() + D[1] * w); + D[2] = static_cast(BaseType::template getValue<1,0,0>() + D[2] * w); + D[3] = static_cast(BaseType::template getValue<1,1,0>() + D[3] * w); + + // X component + A = static_cast(D[0] + (D[1] - D[0]) * v); + B = static_cast(D[2] + (D[3] - D[2]) * v); + + grad[0] = B - A; + + // Y component + A = D[1] - D[0]; + B = D[3] - D[2]; + + grad[1] = static_cast(A + (B - A) * u); + + return BaseType::mGrid->transform().baseMap()->applyIJT(grad, xyz); + } + +private: + inline void init(const Coord& ijk) + { + BaseType::template setValue< 0, 0, 1>(mAcc.getValue(ijk.offsetBy( 0, 0, 1))); + BaseType::template setValue< 0, 1, 1>(mAcc.getValue(ijk.offsetBy( 0, 1, 1))); + BaseType::template setValue< 0, 1, 0>(mAcc.getValue(ijk.offsetBy( 0, 1, 0))); + BaseType::template setValue< 1, 0, 0>(mAcc.getValue(ijk.offsetBy( 1, 0, 0))); + BaseType::template setValue< 1, 0, 1>(mAcc.getValue(ijk.offsetBy( 1, 0, 1))); + BaseType::template setValue< 1, 1, 1>(mAcc.getValue(ijk.offsetBy( 1, 1, 1))); + BaseType::template setValue< 1, 1, 0>(mAcc.getValue(ijk.offsetBy( 1, 1, 0))); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; +};// BoxStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the dense point stencil + template struct DensePt {}; + template<> struct DensePt< 0, 0, 0> { enum { idx = 0 }; }; + + template<> struct DensePt< 1, 0, 0> { enum { idx = 1 }; }; + template<> struct DensePt< 0, 1, 0> { enum { idx = 2 }; }; + template<> struct DensePt< 0, 0, 1> { enum { idx = 3 }; }; + + template<> struct DensePt<-1, 0, 0> { enum { idx = 4 }; }; + template<> struct DensePt< 0,-1, 0> { enum { idx = 5 }; }; + template<> struct DensePt< 0, 0,-1> { enum { idx = 6 }; }; + + template<> struct DensePt<-1,-1, 0> { enum { idx = 7 }; }; + template<> struct DensePt< 0,-1,-1> { enum { idx = 8 }; }; + template<> struct DensePt<-1, 0,-1> { enum { idx = 9 }; }; + + template<> struct DensePt< 1,-1, 0> { enum { idx = 10 }; }; + template<> struct DensePt< 0, 1,-1> { enum { idx = 11 }; }; + template<> struct DensePt<-1, 0, 1> { enum { idx = 12 }; }; + + template<> struct DensePt<-1, 1, 0> { enum { idx = 13 }; }; + template<> struct DensePt< 0,-1, 1> { enum { idx = 14 }; }; + template<> struct DensePt< 1, 0,-1> { enum { idx = 15 }; }; + + template<> struct DensePt< 1, 1, 0> { enum { idx = 16 }; }; + template<> struct DensePt< 0, 1, 1> { enum { idx = 17 }; }; + template<> struct DensePt< 1, 0, 1> { enum { idx = 18 }; }; +} + + +template +class SecondOrderDenseStencil + : public BaseStencil, GridT, IsSafe > +{ + typedef SecondOrderDenseStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + static const int SIZE = 19; + + SecondOrderDenseStencil(const GridType& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return DensePt::idx; } + +private: + inline void init(const Coord& ijk) + { + mValues[DensePt< 1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 0)); + mValues[DensePt< 0, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 0)); + mValues[DensePt< 0, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 1)); + + mValues[DensePt<-1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 0)); + mValues[DensePt< 0,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, -1, 0)); + mValues[DensePt< 0, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, -1)); + + mValues[DensePt<-1,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, -1, 0)); + mValues[DensePt< 1,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, -1, 0)); + mValues[DensePt<-1, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 1, 0)); + mValues[DensePt< 1, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 1, 0)); + + mValues[DensePt<-1, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, -1)); + mValues[DensePt< 1, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, -1)); + mValues[DensePt<-1, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 1)); + mValues[DensePt< 1, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 1)); + + mValues[DensePt< 0,-1,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, -1, -1)); + mValues[DensePt< 0, 1,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, -1)); + mValues[DensePt< 0,-1, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, -1, 1)); + mValues[DensePt< 0, 1, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 1)); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; +};// SecondOrderDenseStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the dense point stencil + template struct ThirteenPt {}; + template<> struct ThirteenPt< 0, 0, 0> { enum { idx = 0 }; }; + + template<> struct ThirteenPt< 1, 0, 0> { enum { idx = 1 }; }; + template<> struct ThirteenPt< 0, 1, 0> { enum { idx = 2 }; }; + template<> struct ThirteenPt< 0, 0, 1> { enum { idx = 3 }; }; + + template<> struct ThirteenPt<-1, 0, 0> { enum { idx = 4 }; }; + template<> struct ThirteenPt< 0,-1, 0> { enum { idx = 5 }; }; + template<> struct ThirteenPt< 0, 0,-1> { enum { idx = 6 }; }; + + template<> struct ThirteenPt< 2, 0, 0> { enum { idx = 7 }; }; + template<> struct ThirteenPt< 0, 2, 0> { enum { idx = 8 }; }; + template<> struct ThirteenPt< 0, 0, 2> { enum { idx = 9 }; }; + + template<> struct ThirteenPt<-2, 0, 0> { enum { idx = 10 }; }; + template<> struct ThirteenPt< 0,-2, 0> { enum { idx = 11 }; }; + template<> struct ThirteenPt< 0, 0,-2> { enum { idx = 12 }; }; + +} + + +template +class ThirteenPointStencil + : public BaseStencil, GridT, IsSafe> +{ + typedef ThirteenPointStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + static const int SIZE = 13; + + ThirteenPointStencil(const GridType& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return ThirteenPt::idx; } + +private: + inline void init(const Coord& ijk) + { + mValues[ThirteenPt< 2, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0, 0)); + mValues[ThirteenPt< 1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 0)); + mValues[ThirteenPt<-1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 0)); + mValues[ThirteenPt<-2, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0, 0)); + + mValues[ThirteenPt< 0, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2, 0)); + mValues[ThirteenPt< 0, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 0)); + mValues[ThirteenPt< 0,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, -1, 0)); + mValues[ThirteenPt< 0,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, -2, 0)); + + mValues[ThirteenPt< 0, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 2)); + mValues[ThirteenPt< 0, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 1)); + mValues[ThirteenPt< 0, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, -1)); + mValues[ThirteenPt< 0, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, -2)); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; +};// ThirteenPointStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the 4th-order dense point stencil + template struct FourthDensePt {}; + template<> struct FourthDensePt< 0, 0, 0> { enum { idx = 0 }; }; + + template<> struct FourthDensePt<-2, 2, 0> { enum { idx = 1 }; }; + template<> struct FourthDensePt<-1, 2, 0> { enum { idx = 2 }; }; + template<> struct FourthDensePt< 0, 2, 0> { enum { idx = 3 }; }; + template<> struct FourthDensePt< 1, 2, 0> { enum { idx = 4 }; }; + template<> struct FourthDensePt< 2, 2, 0> { enum { idx = 5 }; }; + + template<> struct FourthDensePt<-2, 1, 0> { enum { idx = 6 }; }; + template<> struct FourthDensePt<-1, 1, 0> { enum { idx = 7 }; }; + template<> struct FourthDensePt< 0, 1, 0> { enum { idx = 8 }; }; + template<> struct FourthDensePt< 1, 1, 0> { enum { idx = 9 }; }; + template<> struct FourthDensePt< 2, 1, 0> { enum { idx = 10 }; }; + + template<> struct FourthDensePt<-2, 0, 0> { enum { idx = 11 }; }; + template<> struct FourthDensePt<-1, 0, 0> { enum { idx = 12 }; }; + template<> struct FourthDensePt< 1, 0, 0> { enum { idx = 13 }; }; + template<> struct FourthDensePt< 2, 0, 0> { enum { idx = 14 }; }; + + template<> struct FourthDensePt<-2,-1, 0> { enum { idx = 15 }; }; + template<> struct FourthDensePt<-1,-1, 0> { enum { idx = 16 }; }; + template<> struct FourthDensePt< 0,-1, 0> { enum { idx = 17 }; }; + template<> struct FourthDensePt< 1,-1, 0> { enum { idx = 18 }; }; + template<> struct FourthDensePt< 2,-1, 0> { enum { idx = 19 }; }; + + template<> struct FourthDensePt<-2,-2, 0> { enum { idx = 20 }; }; + template<> struct FourthDensePt<-1,-2, 0> { enum { idx = 21 }; }; + template<> struct FourthDensePt< 0,-2, 0> { enum { idx = 22 }; }; + template<> struct FourthDensePt< 1,-2, 0> { enum { idx = 23 }; }; + template<> struct FourthDensePt< 2,-2, 0> { enum { idx = 24 }; }; + + + template<> struct FourthDensePt<-2, 0, 2> { enum { idx = 25 }; }; + template<> struct FourthDensePt<-1, 0, 2> { enum { idx = 26 }; }; + template<> struct FourthDensePt< 0, 0, 2> { enum { idx = 27 }; }; + template<> struct FourthDensePt< 1, 0, 2> { enum { idx = 28 }; }; + template<> struct FourthDensePt< 2, 0, 2> { enum { idx = 29 }; }; + + template<> struct FourthDensePt<-2, 0, 1> { enum { idx = 30 }; }; + template<> struct FourthDensePt<-1, 0, 1> { enum { idx = 31 }; }; + template<> struct FourthDensePt< 0, 0, 1> { enum { idx = 32 }; }; + template<> struct FourthDensePt< 1, 0, 1> { enum { idx = 33 }; }; + template<> struct FourthDensePt< 2, 0, 1> { enum { idx = 34 }; }; + + template<> struct FourthDensePt<-2, 0,-1> { enum { idx = 35 }; }; + template<> struct FourthDensePt<-1, 0,-1> { enum { idx = 36 }; }; + template<> struct FourthDensePt< 0, 0,-1> { enum { idx = 37 }; }; + template<> struct FourthDensePt< 1, 0,-1> { enum { idx = 38 }; }; + template<> struct FourthDensePt< 2, 0,-1> { enum { idx = 39 }; }; + + template<> struct FourthDensePt<-2, 0,-2> { enum { idx = 40 }; }; + template<> struct FourthDensePt<-1, 0,-2> { enum { idx = 41 }; }; + template<> struct FourthDensePt< 0, 0,-2> { enum { idx = 42 }; }; + template<> struct FourthDensePt< 1, 0,-2> { enum { idx = 43 }; }; + template<> struct FourthDensePt< 2, 0,-2> { enum { idx = 44 }; }; + + + template<> struct FourthDensePt< 0,-2, 2> { enum { idx = 45 }; }; + template<> struct FourthDensePt< 0,-1, 2> { enum { idx = 46 }; }; + template<> struct FourthDensePt< 0, 1, 2> { enum { idx = 47 }; }; + template<> struct FourthDensePt< 0, 2, 2> { enum { idx = 48 }; }; + + template<> struct FourthDensePt< 0,-2, 1> { enum { idx = 49 }; }; + template<> struct FourthDensePt< 0,-1, 1> { enum { idx = 50 }; }; + template<> struct FourthDensePt< 0, 1, 1> { enum { idx = 51 }; }; + template<> struct FourthDensePt< 0, 2, 1> { enum { idx = 52 }; }; + + template<> struct FourthDensePt< 0,-2,-1> { enum { idx = 53 }; }; + template<> struct FourthDensePt< 0,-1,-1> { enum { idx = 54 }; }; + template<> struct FourthDensePt< 0, 1,-1> { enum { idx = 55 }; }; + template<> struct FourthDensePt< 0, 2,-1> { enum { idx = 56 }; }; + + template<> struct FourthDensePt< 0,-2,-2> { enum { idx = 57 }; }; + template<> struct FourthDensePt< 0,-1,-2> { enum { idx = 58 }; }; + template<> struct FourthDensePt< 0, 1,-2> { enum { idx = 59 }; }; + template<> struct FourthDensePt< 0, 2,-2> { enum { idx = 60 }; }; + +} + + +template +class FourthOrderDenseStencil + : public BaseStencil, GridT, IsSafe> +{ + typedef FourthOrderDenseStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + static const int SIZE = 61; + + FourthOrderDenseStencil(const GridType& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return FourthDensePt::idx; } + +private: + inline void init(const Coord& ijk) + { + mValues[FourthDensePt<-2, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2, 2, 0)); + mValues[FourthDensePt<-1, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 2, 0)); + mValues[FourthDensePt< 0, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2, 0)); + mValues[FourthDensePt< 1, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 2, 0)); + mValues[FourthDensePt< 2, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2, 2, 0)); + + mValues[FourthDensePt<-2, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2, 1, 0)); + mValues[FourthDensePt<-1, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 1, 0)); + mValues[FourthDensePt< 0, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 0)); + mValues[FourthDensePt< 1, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 1, 0)); + mValues[FourthDensePt< 2, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2, 1, 0)); + + mValues[FourthDensePt<-2, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0, 0)); + mValues[FourthDensePt<-1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 0)); + mValues[FourthDensePt< 1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 0)); + mValues[FourthDensePt< 2, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0, 0)); + + mValues[FourthDensePt<-2,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2,-1, 0)); + mValues[FourthDensePt<-1,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1,-1, 0)); + mValues[FourthDensePt< 0,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1, 0)); + mValues[FourthDensePt< 1,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1,-1, 0)); + mValues[FourthDensePt< 2,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2,-1, 0)); + + mValues[FourthDensePt<-2,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2,-2, 0)); + mValues[FourthDensePt<-1,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1,-2, 0)); + mValues[FourthDensePt< 0,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2, 0)); + mValues[FourthDensePt< 1,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1,-2, 0)); + mValues[FourthDensePt< 2,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2,-2, 0)); + + mValues[FourthDensePt<-2, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0, 2)); + mValues[FourthDensePt<-1, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 2)); + mValues[FourthDensePt< 0, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 2)); + mValues[FourthDensePt< 1, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 2)); + mValues[FourthDensePt< 2, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0, 2)); + + mValues[FourthDensePt<-2, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0, 1)); + mValues[FourthDensePt<-1, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 1)); + mValues[FourthDensePt< 0, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 1)); + mValues[FourthDensePt< 1, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 1)); + mValues[FourthDensePt< 2, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0, 1)); + + mValues[FourthDensePt<-2, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0,-1)); + mValues[FourthDensePt<-1, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0,-1)); + mValues[FourthDensePt< 0, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0,-1)); + mValues[FourthDensePt< 1, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0,-1)); + mValues[FourthDensePt< 2, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0,-1)); + + mValues[FourthDensePt<-2, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0,-2)); + mValues[FourthDensePt<-1, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0,-2)); + mValues[FourthDensePt< 0, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0,-2)); + mValues[FourthDensePt< 1, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0,-2)); + mValues[FourthDensePt< 2, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0,-2)); + + + mValues[FourthDensePt< 0,-2, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2, 2)); + mValues[FourthDensePt< 0,-1, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1, 2)); + mValues[FourthDensePt< 0, 1, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 2)); + mValues[FourthDensePt< 0, 2, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2, 2)); + + mValues[FourthDensePt< 0,-2, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2, 1)); + mValues[FourthDensePt< 0,-1, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1, 1)); + mValues[FourthDensePt< 0, 1, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 1)); + mValues[FourthDensePt< 0, 2, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2, 1)); + + mValues[FourthDensePt< 0,-2,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2,-1)); + mValues[FourthDensePt< 0,-1,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1,-1)); + mValues[FourthDensePt< 0, 1,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1,-1)); + mValues[FourthDensePt< 0, 2,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2,-1)); + + mValues[FourthDensePt< 0,-2,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2,-2)); + mValues[FourthDensePt< 0,-1,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1,-2)); + mValues[FourthDensePt< 0, 1,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1,-2)); + mValues[FourthDensePt< 0, 2,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2,-2)); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; +};// FourthOrderDenseStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the dense point stencil + template struct NineteenPt {}; + template<> struct NineteenPt< 0, 0, 0> { enum { idx = 0 }; }; + + template<> struct NineteenPt< 1, 0, 0> { enum { idx = 1 }; }; + template<> struct NineteenPt< 0, 1, 0> { enum { idx = 2 }; }; + template<> struct NineteenPt< 0, 0, 1> { enum { idx = 3 }; }; + + template<> struct NineteenPt<-1, 0, 0> { enum { idx = 4 }; }; + template<> struct NineteenPt< 0,-1, 0> { enum { idx = 5 }; }; + template<> struct NineteenPt< 0, 0,-1> { enum { idx = 6 }; }; + + template<> struct NineteenPt< 2, 0, 0> { enum { idx = 7 }; }; + template<> struct NineteenPt< 0, 2, 0> { enum { idx = 8 }; }; + template<> struct NineteenPt< 0, 0, 2> { enum { idx = 9 }; }; + + template<> struct NineteenPt<-2, 0, 0> { enum { idx = 10 }; }; + template<> struct NineteenPt< 0,-2, 0> { enum { idx = 11 }; }; + template<> struct NineteenPt< 0, 0,-2> { enum { idx = 12 }; }; + + template<> struct NineteenPt< 3, 0, 0> { enum { idx = 13 }; }; + template<> struct NineteenPt< 0, 3, 0> { enum { idx = 14 }; }; + template<> struct NineteenPt< 0, 0, 3> { enum { idx = 15 }; }; + + template<> struct NineteenPt<-3, 0, 0> { enum { idx = 16 }; }; + template<> struct NineteenPt< 0,-3, 0> { enum { idx = 17 }; }; + template<> struct NineteenPt< 0, 0,-3> { enum { idx = 18 }; }; + +} + + +template +class NineteenPointStencil + : public BaseStencil, GridT, IsSafe> +{ + typedef NineteenPointStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + static const int SIZE = 19; + + NineteenPointStencil(const GridType& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return NineteenPt::idx; } + +private: + inline void init(const Coord& ijk) + { + mValues[NineteenPt< 3, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 3, 0, 0)); + mValues[NineteenPt< 2, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0, 0)); + mValues[NineteenPt< 1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 0)); + mValues[NineteenPt<-1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 0)); + mValues[NineteenPt<-2, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0, 0)); + mValues[NineteenPt<-3, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-3, 0, 0)); + + mValues[NineteenPt< 0, 3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 3, 0)); + mValues[NineteenPt< 0, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2, 0)); + mValues[NineteenPt< 0, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 0)); + mValues[NineteenPt< 0,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, -1, 0)); + mValues[NineteenPt< 0,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, -2, 0)); + mValues[NineteenPt< 0,-3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, -3, 0)); + + mValues[NineteenPt< 0, 0, 3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 3)); + mValues[NineteenPt< 0, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 2)); + mValues[NineteenPt< 0, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 1)); + mValues[NineteenPt< 0, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, -1)); + mValues[NineteenPt< 0, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, -2)); + mValues[NineteenPt< 0, 0,-3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, -3)); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; +};// NineteenPointStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the 4th-order dense point stencil + template struct SixthDensePt { }; + template<> struct SixthDensePt< 0, 0, 0> { enum { idx = 0 }; }; + + template<> struct SixthDensePt<-3, 3, 0> { enum { idx = 1 }; }; + template<> struct SixthDensePt<-2, 3, 0> { enum { idx = 2 }; }; + template<> struct SixthDensePt<-1, 3, 0> { enum { idx = 3 }; }; + template<> struct SixthDensePt< 0, 3, 0> { enum { idx = 4 }; }; + template<> struct SixthDensePt< 1, 3, 0> { enum { idx = 5 }; }; + template<> struct SixthDensePt< 2, 3, 0> { enum { idx = 6 }; }; + template<> struct SixthDensePt< 3, 3, 0> { enum { idx = 7 }; }; + + template<> struct SixthDensePt<-3, 2, 0> { enum { idx = 8 }; }; + template<> struct SixthDensePt<-2, 2, 0> { enum { idx = 9 }; }; + template<> struct SixthDensePt<-1, 2, 0> { enum { idx = 10 }; }; + template<> struct SixthDensePt< 0, 2, 0> { enum { idx = 11 }; }; + template<> struct SixthDensePt< 1, 2, 0> { enum { idx = 12 }; }; + template<> struct SixthDensePt< 2, 2, 0> { enum { idx = 13 }; }; + template<> struct SixthDensePt< 3, 2, 0> { enum { idx = 14 }; }; + + template<> struct SixthDensePt<-3, 1, 0> { enum { idx = 15 }; }; + template<> struct SixthDensePt<-2, 1, 0> { enum { idx = 16 }; }; + template<> struct SixthDensePt<-1, 1, 0> { enum { idx = 17 }; }; + template<> struct SixthDensePt< 0, 1, 0> { enum { idx = 18 }; }; + template<> struct SixthDensePt< 1, 1, 0> { enum { idx = 19 }; }; + template<> struct SixthDensePt< 2, 1, 0> { enum { idx = 20 }; }; + template<> struct SixthDensePt< 3, 1, 0> { enum { idx = 21 }; }; + + template<> struct SixthDensePt<-3, 0, 0> { enum { idx = 22 }; }; + template<> struct SixthDensePt<-2, 0, 0> { enum { idx = 23 }; }; + template<> struct SixthDensePt<-1, 0, 0> { enum { idx = 24 }; }; + template<> struct SixthDensePt< 1, 0, 0> { enum { idx = 25 }; }; + template<> struct SixthDensePt< 2, 0, 0> { enum { idx = 26 }; }; + template<> struct SixthDensePt< 3, 0, 0> { enum { idx = 27 }; }; + + + template<> struct SixthDensePt<-3,-1, 0> { enum { idx = 28 }; }; + template<> struct SixthDensePt<-2,-1, 0> { enum { idx = 29 }; }; + template<> struct SixthDensePt<-1,-1, 0> { enum { idx = 30 }; }; + template<> struct SixthDensePt< 0,-1, 0> { enum { idx = 31 }; }; + template<> struct SixthDensePt< 1,-1, 0> { enum { idx = 32 }; }; + template<> struct SixthDensePt< 2,-1, 0> { enum { idx = 33 }; }; + template<> struct SixthDensePt< 3,-1, 0> { enum { idx = 34 }; }; + + + template<> struct SixthDensePt<-3,-2, 0> { enum { idx = 35 }; }; + template<> struct SixthDensePt<-2,-2, 0> { enum { idx = 36 }; }; + template<> struct SixthDensePt<-1,-2, 0> { enum { idx = 37 }; }; + template<> struct SixthDensePt< 0,-2, 0> { enum { idx = 38 }; }; + template<> struct SixthDensePt< 1,-2, 0> { enum { idx = 39 }; }; + template<> struct SixthDensePt< 2,-2, 0> { enum { idx = 40 }; }; + template<> struct SixthDensePt< 3,-2, 0> { enum { idx = 41 }; }; + + + template<> struct SixthDensePt<-3,-3, 0> { enum { idx = 42 }; }; + template<> struct SixthDensePt<-2,-3, 0> { enum { idx = 43 }; }; + template<> struct SixthDensePt<-1,-3, 0> { enum { idx = 44 }; }; + template<> struct SixthDensePt< 0,-3, 0> { enum { idx = 45 }; }; + template<> struct SixthDensePt< 1,-3, 0> { enum { idx = 46 }; }; + template<> struct SixthDensePt< 2,-3, 0> { enum { idx = 47 }; }; + template<> struct SixthDensePt< 3,-3, 0> { enum { idx = 48 }; }; + + + template<> struct SixthDensePt<-3, 0, 3> { enum { idx = 49 }; }; + template<> struct SixthDensePt<-2, 0, 3> { enum { idx = 50 }; }; + template<> struct SixthDensePt<-1, 0, 3> { enum { idx = 51 }; }; + template<> struct SixthDensePt< 0, 0, 3> { enum { idx = 52 }; }; + template<> struct SixthDensePt< 1, 0, 3> { enum { idx = 53 }; }; + template<> struct SixthDensePt< 2, 0, 3> { enum { idx = 54 }; }; + template<> struct SixthDensePt< 3, 0, 3> { enum { idx = 55 }; }; + + + template<> struct SixthDensePt<-3, 0, 2> { enum { idx = 56 }; }; + template<> struct SixthDensePt<-2, 0, 2> { enum { idx = 57 }; }; + template<> struct SixthDensePt<-1, 0, 2> { enum { idx = 58 }; }; + template<> struct SixthDensePt< 0, 0, 2> { enum { idx = 59 }; }; + template<> struct SixthDensePt< 1, 0, 2> { enum { idx = 60 }; }; + template<> struct SixthDensePt< 2, 0, 2> { enum { idx = 61 }; }; + template<> struct SixthDensePt< 3, 0, 2> { enum { idx = 62 }; }; + + template<> struct SixthDensePt<-3, 0, 1> { enum { idx = 63 }; }; + template<> struct SixthDensePt<-2, 0, 1> { enum { idx = 64 }; }; + template<> struct SixthDensePt<-1, 0, 1> { enum { idx = 65 }; }; + template<> struct SixthDensePt< 0, 0, 1> { enum { idx = 66 }; }; + template<> struct SixthDensePt< 1, 0, 1> { enum { idx = 67 }; }; + template<> struct SixthDensePt< 2, 0, 1> { enum { idx = 68 }; }; + template<> struct SixthDensePt< 3, 0, 1> { enum { idx = 69 }; }; + + + template<> struct SixthDensePt<-3, 0,-1> { enum { idx = 70 }; }; + template<> struct SixthDensePt<-2, 0,-1> { enum { idx = 71 }; }; + template<> struct SixthDensePt<-1, 0,-1> { enum { idx = 72 }; }; + template<> struct SixthDensePt< 0, 0,-1> { enum { idx = 73 }; }; + template<> struct SixthDensePt< 1, 0,-1> { enum { idx = 74 }; }; + template<> struct SixthDensePt< 2, 0,-1> { enum { idx = 75 }; }; + template<> struct SixthDensePt< 3, 0,-1> { enum { idx = 76 }; }; + + + template<> struct SixthDensePt<-3, 0,-2> { enum { idx = 77 }; }; + template<> struct SixthDensePt<-2, 0,-2> { enum { idx = 78 }; }; + template<> struct SixthDensePt<-1, 0,-2> { enum { idx = 79 }; }; + template<> struct SixthDensePt< 0, 0,-2> { enum { idx = 80 }; }; + template<> struct SixthDensePt< 1, 0,-2> { enum { idx = 81 }; }; + template<> struct SixthDensePt< 2, 0,-2> { enum { idx = 82 }; }; + template<> struct SixthDensePt< 3, 0,-2> { enum { idx = 83 }; }; + + + template<> struct SixthDensePt<-3, 0,-3> { enum { idx = 84 }; }; + template<> struct SixthDensePt<-2, 0,-3> { enum { idx = 85 }; }; + template<> struct SixthDensePt<-1, 0,-3> { enum { idx = 86 }; }; + template<> struct SixthDensePt< 0, 0,-3> { enum { idx = 87 }; }; + template<> struct SixthDensePt< 1, 0,-3> { enum { idx = 88 }; }; + template<> struct SixthDensePt< 2, 0,-3> { enum { idx = 89 }; }; + template<> struct SixthDensePt< 3, 0,-3> { enum { idx = 90 }; }; + + + template<> struct SixthDensePt< 0,-3, 3> { enum { idx = 91 }; }; + template<> struct SixthDensePt< 0,-2, 3> { enum { idx = 92 }; }; + template<> struct SixthDensePt< 0,-1, 3> { enum { idx = 93 }; }; + template<> struct SixthDensePt< 0, 1, 3> { enum { idx = 94 }; }; + template<> struct SixthDensePt< 0, 2, 3> { enum { idx = 95 }; }; + template<> struct SixthDensePt< 0, 3, 3> { enum { idx = 96 }; }; + + template<> struct SixthDensePt< 0,-3, 2> { enum { idx = 97 }; }; + template<> struct SixthDensePt< 0,-2, 2> { enum { idx = 98 }; }; + template<> struct SixthDensePt< 0,-1, 2> { enum { idx = 99 }; }; + template<> struct SixthDensePt< 0, 1, 2> { enum { idx = 100 }; }; + template<> struct SixthDensePt< 0, 2, 2> { enum { idx = 101 }; }; + template<> struct SixthDensePt< 0, 3, 2> { enum { idx = 102 }; }; + + template<> struct SixthDensePt< 0,-3, 1> { enum { idx = 103 }; }; + template<> struct SixthDensePt< 0,-2, 1> { enum { idx = 104 }; }; + template<> struct SixthDensePt< 0,-1, 1> { enum { idx = 105 }; }; + template<> struct SixthDensePt< 0, 1, 1> { enum { idx = 106 }; }; + template<> struct SixthDensePt< 0, 2, 1> { enum { idx = 107 }; }; + template<> struct SixthDensePt< 0, 3, 1> { enum { idx = 108 }; }; + + template<> struct SixthDensePt< 0,-3,-1> { enum { idx = 109 }; }; + template<> struct SixthDensePt< 0,-2,-1> { enum { idx = 110 }; }; + template<> struct SixthDensePt< 0,-1,-1> { enum { idx = 111 }; }; + template<> struct SixthDensePt< 0, 1,-1> { enum { idx = 112 }; }; + template<> struct SixthDensePt< 0, 2,-1> { enum { idx = 113 }; }; + template<> struct SixthDensePt< 0, 3,-1> { enum { idx = 114 }; }; + + template<> struct SixthDensePt< 0,-3,-2> { enum { idx = 115 }; }; + template<> struct SixthDensePt< 0,-2,-2> { enum { idx = 116 }; }; + template<> struct SixthDensePt< 0,-1,-2> { enum { idx = 117 }; }; + template<> struct SixthDensePt< 0, 1,-2> { enum { idx = 118 }; }; + template<> struct SixthDensePt< 0, 2,-2> { enum { idx = 119 }; }; + template<> struct SixthDensePt< 0, 3,-2> { enum { idx = 120 }; }; + + template<> struct SixthDensePt< 0,-3,-3> { enum { idx = 121 }; }; + template<> struct SixthDensePt< 0,-2,-3> { enum { idx = 122 }; }; + template<> struct SixthDensePt< 0,-1,-3> { enum { idx = 123 }; }; + template<> struct SixthDensePt< 0, 1,-3> { enum { idx = 124 }; }; + template<> struct SixthDensePt< 0, 2,-3> { enum { idx = 125 }; }; + template<> struct SixthDensePt< 0, 3,-3> { enum { idx = 126 }; }; + +} + + +template +class SixthOrderDenseStencil + : public BaseStencil, GridT, IsSafe> +{ + typedef SixthOrderDenseStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + static const int SIZE = 127; + + SixthOrderDenseStencil(const GridType& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return SixthDensePt::idx; } + +private: + inline void init(const Coord& ijk) + { + mValues[SixthDensePt<-3, 3, 0>::idx] = mAcc.getValue(ijk.offsetBy(-3, 3, 0)); + mValues[SixthDensePt<-2, 3, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2, 3, 0)); + mValues[SixthDensePt<-1, 3, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 3, 0)); + mValues[SixthDensePt< 0, 3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 3, 0)); + mValues[SixthDensePt< 1, 3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 3, 0)); + mValues[SixthDensePt< 2, 3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2, 3, 0)); + mValues[SixthDensePt< 3, 3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 3, 3, 0)); + + mValues[SixthDensePt<-3, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-3, 2, 0)); + mValues[SixthDensePt<-2, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2, 2, 0)); + mValues[SixthDensePt<-1, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 2, 0)); + mValues[SixthDensePt< 0, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2, 0)); + mValues[SixthDensePt< 1, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 2, 0)); + mValues[SixthDensePt< 2, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2, 2, 0)); + mValues[SixthDensePt< 3, 2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 3, 2, 0)); + + mValues[SixthDensePt<-3, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-3, 1, 0)); + mValues[SixthDensePt<-2, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2, 1, 0)); + mValues[SixthDensePt<-1, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 1, 0)); + mValues[SixthDensePt< 0, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 0)); + mValues[SixthDensePt< 1, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 1, 0)); + mValues[SixthDensePt< 2, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2, 1, 0)); + mValues[SixthDensePt< 3, 1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 3, 1, 0)); + + mValues[SixthDensePt<-3, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-3, 0, 0)); + mValues[SixthDensePt<-2, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0, 0)); + mValues[SixthDensePt<-1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 0)); + mValues[SixthDensePt< 1, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 0)); + mValues[SixthDensePt< 2, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0, 0)); + mValues[SixthDensePt< 3, 0, 0>::idx] = mAcc.getValue(ijk.offsetBy( 3, 0, 0)); + + mValues[SixthDensePt<-3,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-3,-1, 0)); + mValues[SixthDensePt<-2,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2,-1, 0)); + mValues[SixthDensePt<-1,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1,-1, 0)); + mValues[SixthDensePt< 0,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1, 0)); + mValues[SixthDensePt< 1,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1,-1, 0)); + mValues[SixthDensePt< 2,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2,-1, 0)); + mValues[SixthDensePt< 3,-1, 0>::idx] = mAcc.getValue(ijk.offsetBy( 3,-1, 0)); + + mValues[SixthDensePt<-3,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-3,-2, 0)); + mValues[SixthDensePt<-2,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2,-2, 0)); + mValues[SixthDensePt<-1,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1,-2, 0)); + mValues[SixthDensePt< 0,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2, 0)); + mValues[SixthDensePt< 1,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1,-2, 0)); + mValues[SixthDensePt< 2,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2,-2, 0)); + mValues[SixthDensePt< 3,-2, 0>::idx] = mAcc.getValue(ijk.offsetBy( 3,-2, 0)); + + mValues[SixthDensePt<-3,-3, 0>::idx] = mAcc.getValue(ijk.offsetBy(-3,-3, 0)); + mValues[SixthDensePt<-2,-3, 0>::idx] = mAcc.getValue(ijk.offsetBy(-2,-3, 0)); + mValues[SixthDensePt<-1,-3, 0>::idx] = mAcc.getValue(ijk.offsetBy(-1,-3, 0)); + mValues[SixthDensePt< 0,-3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 0,-3, 0)); + mValues[SixthDensePt< 1,-3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 1,-3, 0)); + mValues[SixthDensePt< 2,-3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 2,-3, 0)); + mValues[SixthDensePt< 3,-3, 0>::idx] = mAcc.getValue(ijk.offsetBy( 3,-3, 0)); + + mValues[SixthDensePt<-3, 0, 3>::idx] = mAcc.getValue(ijk.offsetBy(-3, 0, 3)); + mValues[SixthDensePt<-2, 0, 3>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0, 3)); + mValues[SixthDensePt<-1, 0, 3>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 3)); + mValues[SixthDensePt< 0, 0, 3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 3)); + mValues[SixthDensePt< 1, 0, 3>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 3)); + mValues[SixthDensePt< 2, 0, 3>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0, 3)); + mValues[SixthDensePt< 3, 0, 3>::idx] = mAcc.getValue(ijk.offsetBy( 3, 0, 3)); + + mValues[SixthDensePt<-3, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy(-3, 0, 2)); + mValues[SixthDensePt<-2, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0, 2)); + mValues[SixthDensePt<-1, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 2)); + mValues[SixthDensePt< 0, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 2)); + mValues[SixthDensePt< 1, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 2)); + mValues[SixthDensePt< 2, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0, 2)); + mValues[SixthDensePt< 3, 0, 2>::idx] = mAcc.getValue(ijk.offsetBy( 3, 0, 2)); + + mValues[SixthDensePt<-3, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy(-3, 0, 1)); + mValues[SixthDensePt<-2, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0, 1)); + mValues[SixthDensePt<-1, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0, 1)); + mValues[SixthDensePt< 0, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0, 1)); + mValues[SixthDensePt< 1, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0, 1)); + mValues[SixthDensePt< 2, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0, 1)); + mValues[SixthDensePt< 3, 0, 1>::idx] = mAcc.getValue(ijk.offsetBy( 3, 0, 1)); + + mValues[SixthDensePt<-3, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy(-3, 0,-1)); + mValues[SixthDensePt<-2, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0,-1)); + mValues[SixthDensePt<-1, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0,-1)); + mValues[SixthDensePt< 0, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0,-1)); + mValues[SixthDensePt< 1, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0,-1)); + mValues[SixthDensePt< 2, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0,-1)); + mValues[SixthDensePt< 3, 0,-1>::idx] = mAcc.getValue(ijk.offsetBy( 3, 0,-1)); + + mValues[SixthDensePt<-3, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy(-3, 0,-2)); + mValues[SixthDensePt<-2, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0,-2)); + mValues[SixthDensePt<-1, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0,-2)); + mValues[SixthDensePt< 0, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0,-2)); + mValues[SixthDensePt< 1, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0,-2)); + mValues[SixthDensePt< 2, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0,-2)); + mValues[SixthDensePt< 3, 0,-2>::idx] = mAcc.getValue(ijk.offsetBy( 3, 0,-2)); + + mValues[SixthDensePt<-3, 0,-3>::idx] = mAcc.getValue(ijk.offsetBy(-3, 0,-3)); + mValues[SixthDensePt<-2, 0,-3>::idx] = mAcc.getValue(ijk.offsetBy(-2, 0,-3)); + mValues[SixthDensePt<-1, 0,-3>::idx] = mAcc.getValue(ijk.offsetBy(-1, 0,-3)); + mValues[SixthDensePt< 0, 0,-3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 0,-3)); + mValues[SixthDensePt< 1, 0,-3>::idx] = mAcc.getValue(ijk.offsetBy( 1, 0,-3)); + mValues[SixthDensePt< 2, 0,-3>::idx] = mAcc.getValue(ijk.offsetBy( 2, 0,-3)); + mValues[SixthDensePt< 3, 0,-3>::idx] = mAcc.getValue(ijk.offsetBy( 3, 0,-3)); + + mValues[SixthDensePt< 0,-3, 3>::idx] = mAcc.getValue(ijk.offsetBy( 0,-3, 3)); + mValues[SixthDensePt< 0,-2, 3>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2, 3)); + mValues[SixthDensePt< 0,-1, 3>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1, 3)); + mValues[SixthDensePt< 0, 1, 3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 3)); + mValues[SixthDensePt< 0, 2, 3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2, 3)); + mValues[SixthDensePt< 0, 3, 3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 3, 3)); + + mValues[SixthDensePt< 0,-3, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-3, 2)); + mValues[SixthDensePt< 0,-2, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2, 2)); + mValues[SixthDensePt< 0,-1, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1, 2)); + mValues[SixthDensePt< 0, 1, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 2)); + mValues[SixthDensePt< 0, 2, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2, 2)); + mValues[SixthDensePt< 0, 3, 2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 3, 2)); + + mValues[SixthDensePt< 0,-3, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-3, 1)); + mValues[SixthDensePt< 0,-2, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2, 1)); + mValues[SixthDensePt< 0,-1, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1, 1)); + mValues[SixthDensePt< 0, 1, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1, 1)); + mValues[SixthDensePt< 0, 2, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2, 1)); + mValues[SixthDensePt< 0, 3, 1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 3, 1)); + + mValues[SixthDensePt< 0,-3,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-3,-1)); + mValues[SixthDensePt< 0,-2,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2,-1)); + mValues[SixthDensePt< 0,-1,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1,-1)); + mValues[SixthDensePt< 0, 1,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1,-1)); + mValues[SixthDensePt< 0, 2,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2,-1)); + mValues[SixthDensePt< 0, 3,-1>::idx] = mAcc.getValue(ijk.offsetBy( 0, 3,-1)); + + mValues[SixthDensePt< 0,-3,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-3,-2)); + mValues[SixthDensePt< 0,-2,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2,-2)); + mValues[SixthDensePt< 0,-1,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1,-2)); + mValues[SixthDensePt< 0, 1,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1,-2)); + mValues[SixthDensePt< 0, 2,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2,-2)); + mValues[SixthDensePt< 0, 3,-2>::idx] = mAcc.getValue(ijk.offsetBy( 0, 3,-2)); + + mValues[SixthDensePt< 0,-3,-3>::idx] = mAcc.getValue(ijk.offsetBy( 0,-3,-3)); + mValues[SixthDensePt< 0,-2,-3>::idx] = mAcc.getValue(ijk.offsetBy( 0,-2,-3)); + mValues[SixthDensePt< 0,-1,-3>::idx] = mAcc.getValue(ijk.offsetBy( 0,-1,-3)); + mValues[SixthDensePt< 0, 1,-3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 1,-3)); + mValues[SixthDensePt< 0, 2,-3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 2,-3)); + mValues[SixthDensePt< 0, 3,-3>::idx] = mAcc.getValue(ijk.offsetBy( 0, 3,-3)); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; +};// SixthOrderDenseStencil class + + +////////////////////////////////////////////////////////////////////// + +namespace { // anonymous namespace for stencil-layout map + + // the seven point stencil with a different layout from SevenPt + template struct GradPt {}; + template<> struct GradPt< 0, 0, 0> { enum { idx = 0 }; }; + template<> struct GradPt< 1, 0, 0> { enum { idx = 2 }; }; + template<> struct GradPt< 0, 1, 0> { enum { idx = 4 }; }; + template<> struct GradPt< 0, 0, 1> { enum { idx = 6 }; }; + template<> struct GradPt<-1, 0, 0> { enum { idx = 1 }; }; + template<> struct GradPt< 0,-1, 0> { enum { idx = 3 }; }; + template<> struct GradPt< 0, 0,-1> { enum { idx = 5 }; }; +} + +/// This is a simple 7-point nearest neighbor stencil that supports +/// gradient by second-order central differencing, first-order upwinding, +/// Laplacian, closest-point transform and zero-crossing test. +/// +/// @note For optimal random access performance this class +/// includes its own grid accessor. +template +class GradStencil : public BaseStencil, GridT, IsSafe> +{ + typedef GradStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + static const int SIZE = 7; + + GradStencil(const GridType& grid) + : BaseType(grid, SIZE) + , mInv2Dx(ValueType(0.5 / grid.voxelSize()[0])) + , mInvDx2(ValueType(4.0 * mInv2Dx * mInv2Dx)) + { + } + + GradStencil(const GridType& grid, Real dx) + : BaseType(grid, SIZE) + , mInv2Dx(ValueType(0.5 / dx)) + , mInvDx2(ValueType(4.0 * mInv2Dx * mInv2Dx)) + { + } + + /// @brief Return the norm square of the single-sided upwind gradient + /// (computed via Godunov's scheme) at the previously buffered location. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline ValueType normSqGrad() const + { + return mInvDx2 * math::GodunovsNormSqrd(mValues[0] > zeroVal(), + mValues[0] - mValues[1], + mValues[2] - mValues[0], + mValues[0] - mValues[3], + mValues[4] - mValues[0], + mValues[0] - mValues[5], + mValues[6] - mValues[0]); + } + + /// @brief Return the gradient computed at the previously buffered + /// location by second order central differencing. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline math::Vec3 gradient() const + { + return math::Vec3(mValues[2] - mValues[1], + mValues[4] - mValues[3], + mValues[6] - mValues[5])*mInv2Dx; + } + /// @brief Return the first-order upwind gradient corresponding to the direction V. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline math::Vec3 gradient(const math::Vec3& V) const + { + return math::Vec3( + V[0]>0 ? mValues[0] - mValues[1] : mValues[2] - mValues[0], + V[1]>0 ? mValues[0] - mValues[3] : mValues[4] - mValues[0], + V[2]>0 ? mValues[0] - mValues[5] : mValues[6] - mValues[0])*2*mInv2Dx; + } + + /// Return the Laplacian computed at the previously buffered + /// location by second-order central differencing. + inline ValueType laplacian() const + { + return mInvDx2 * (mValues[1] + mValues[2] + + mValues[3] + mValues[4] + + mValues[5] + mValues[6] - 6*mValues[0]); + } + + /// Return @c true if the sign of the value at the center point of the stencil + /// is different from the signs of any of its six nearest neighbors. + inline bool zeroCrossing() const + { + const typename BaseType::BufferType& v = mValues; + return (v[0]>0 ? (v[1]<0 || v[2]<0 || v[3]<0 || v[4]<0 || v[5]<0 || v[6]<0) + : (v[1]>0 || v[2]>0 || v[3]>0 || v[4]>0 || v[5]>0 || v[6]>0)); + } + + /// @brief Compute the closest-point transform to a level set. + /// @return the closest point in index space to the surface + /// from which the level set was derived. + /// + /// @note This method assumes that the grid represents a level set + /// with distances in world units and a simple affine transfrom + /// with uniform scaling. + inline math::Vec3 cpt() + { + const Coord& ijk = BaseType::getCenterCoord(); + const ValueType d = ValueType(mValues[0] * 0.5 * mInvDx2); // distance in voxels / (2dx^2) + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto value = math::Vec3( ijk[0] - d*(mValues[2] - mValues[1]), + ijk[1] - d*(mValues[4] - mValues[3]), + ijk[2] - d*(mValues[6] - mValues[5])); + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return value; + } + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return GradPt::idx; } + +private: + + inline void init(const Coord& ijk) + { + BaseType::template setValue<-1, 0, 0>(mAcc.getValue(ijk.offsetBy(-1, 0, 0))); + BaseType::template setValue< 1, 0, 0>(mAcc.getValue(ijk.offsetBy( 1, 0, 0))); + + BaseType::template setValue< 0,-1, 0>(mAcc.getValue(ijk.offsetBy( 0,-1, 0))); + BaseType::template setValue< 0, 1, 0>(mAcc.getValue(ijk.offsetBy( 0, 1, 0))); + + BaseType::template setValue< 0, 0,-1>(mAcc.getValue(ijk.offsetBy( 0, 0,-1))); + BaseType::template setValue< 0, 0, 1>(mAcc.getValue(ijk.offsetBy( 0, 0, 1))); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; + const ValueType mInv2Dx, mInvDx2; +}; // GradStencil class + +//////////////////////////////////////// + + +/// @brief This is a special 19-point stencil that supports optimal fifth-order WENO +/// upwinding, second-order central differencing, Laplacian, and zero-crossing test. +/// +/// @note For optimal random access performance this class +/// includes its own grid accessor. +template +class WenoStencil: public BaseStencil, GridT, IsSafe> +{ + typedef WenoStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + static const int SIZE = 19; + + WenoStencil(const GridType& grid) + : BaseType(grid, SIZE) + , mDx2(ValueType(math::Pow2(grid.voxelSize()[0]))) + , mInv2Dx(ValueType(0.5 / grid.voxelSize()[0])) + , mInvDx2(ValueType(1.0 / mDx2)) + { + } + + WenoStencil(const GridType& grid, Real dx) + : BaseType(grid, SIZE) + , mDx2(ValueType(dx * dx)) + , mInv2Dx(ValueType(0.5 / dx)) + , mInvDx2(ValueType(1.0 / mDx2)) + { + } + + /// @brief Return the norm-square of the WENO upwind gradient (computed via + /// WENO upwinding and Godunov's scheme) at the previously buffered location. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline ValueType normSqGrad(const ValueType &isoValue = zeroVal()) const + { + const typename BaseType::BufferType& v = mValues; +#ifdef DWA_OPENVDB + // SSE optimized + const simd::Float4 + v1(v[2]-v[1], v[ 8]-v[ 7], v[14]-v[13], 0), + v2(v[3]-v[2], v[ 9]-v[ 8], v[15]-v[14], 0), + v3(v[0]-v[3], v[ 0]-v[ 9], v[ 0]-v[15], 0), + v4(v[4]-v[0], v[10]-v[ 0], v[16]-v[ 0], 0), + v5(v[5]-v[4], v[11]-v[10], v[17]-v[16], 0), + v6(v[6]-v[5], v[12]-v[11], v[18]-v[17], 0), + dP_m = math::WENO5(v1, v2, v3, v4, v5, mDx2), + dP_p = math::WENO5(v6, v5, v4, v3, v2, mDx2); + + return mInvDx2 * math::GodunovsNormSqrd(mValues[0] > isoValue, dP_m, dP_p); +#else + const Real + dP_xm = math::WENO5(v[ 2]-v[ 1],v[ 3]-v[ 2],v[ 0]-v[ 3],v[ 4]-v[ 0],v[ 5]-v[ 4],mDx2), + dP_xp = math::WENO5(v[ 6]-v[ 5],v[ 5]-v[ 4],v[ 4]-v[ 0],v[ 0]-v[ 3],v[ 3]-v[ 2],mDx2), + dP_ym = math::WENO5(v[ 8]-v[ 7],v[ 9]-v[ 8],v[ 0]-v[ 9],v[10]-v[ 0],v[11]-v[10],mDx2), + dP_yp = math::WENO5(v[12]-v[11],v[11]-v[10],v[10]-v[ 0],v[ 0]-v[ 9],v[ 9]-v[ 8],mDx2), + dP_zm = math::WENO5(v[14]-v[13],v[15]-v[14],v[ 0]-v[15],v[16]-v[ 0],v[17]-v[16],mDx2), + dP_zp = math::WENO5(v[18]-v[17],v[17]-v[16],v[16]-v[ 0],v[ 0]-v[15],v[15]-v[14],mDx2); + return static_cast( + mInvDx2*math::GodunovsNormSqrd(v[0]>isoValue, dP_xm, dP_xp, dP_ym, dP_yp, dP_zm, dP_zp)); +#endif + } + + /// Return the optimal fifth-order upwind gradient corresponding to the + /// direction V. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline math::Vec3 gradient(const math::Vec3& V) const + { + const typename BaseType::BufferType& v = mValues; + return 2*mInv2Dx * math::Vec3( + V[0]>0 ? math::WENO5(v[ 2]-v[ 1],v[ 3]-v[ 2],v[ 0]-v[ 3], v[ 4]-v[ 0],v[ 5]-v[ 4],mDx2) + : math::WENO5(v[ 6]-v[ 5],v[ 5]-v[ 4],v[ 4]-v[ 0], v[ 0]-v[ 3],v[ 3]-v[ 2],mDx2), + V[1]>0 ? math::WENO5(v[ 8]-v[ 7],v[ 9]-v[ 8],v[ 0]-v[ 9], v[10]-v[ 0],v[11]-v[10],mDx2) + : math::WENO5(v[12]-v[11],v[11]-v[10],v[10]-v[ 0], v[ 0]-v[ 9],v[ 9]-v[ 8],mDx2), + V[2]>0 ? math::WENO5(v[14]-v[13],v[15]-v[14],v[ 0]-v[15], v[16]-v[ 0],v[17]-v[16],mDx2) + : math::WENO5(v[18]-v[17],v[17]-v[16],v[16]-v[ 0], v[ 0]-v[15],v[15]-v[14],mDx2)); + } + /// Return the gradient computed at the previously buffered + /// location by second-order central differencing. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline math::Vec3 gradient() const + { + return mInv2Dx * math::Vec3(mValues[ 4] - mValues[ 3], + mValues[10] - mValues[ 9], + mValues[16] - mValues[15]); + } + + /// Return the Laplacian computed at the previously buffered + /// location by second-order central differencing. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline ValueType laplacian() const + { + return mInvDx2 * ( + mValues[ 3] + mValues[ 4] + + mValues[ 9] + mValues[10] + + mValues[15] + mValues[16] - 6*mValues[0]); + } + + /// Return @c true if the sign of the value at the center point of the stencil + /// differs from the sign of any of its six nearest neighbors + inline bool zeroCrossing() const + { + const typename BaseType::BufferType& v = mValues; + return (v[ 0]>0 ? (v[ 3]<0 || v[ 4]<0 || v[ 9]<0 || v[10]<0 || v[15]<0 || v[16]<0) + : (v[ 3]>0 || v[ 4]>0 || v[ 9]>0 || v[10]>0 || v[15]>0 || v[16]>0)); + } + +private: + inline void init(const Coord& ijk) + { + mValues[ 1] = mAcc.getValue(ijk.offsetBy(-3, 0, 0)); + mValues[ 2] = mAcc.getValue(ijk.offsetBy(-2, 0, 0)); + mValues[ 3] = mAcc.getValue(ijk.offsetBy(-1, 0, 0)); + mValues[ 4] = mAcc.getValue(ijk.offsetBy( 1, 0, 0)); + mValues[ 5] = mAcc.getValue(ijk.offsetBy( 2, 0, 0)); + mValues[ 6] = mAcc.getValue(ijk.offsetBy( 3, 0, 0)); + + mValues[ 7] = mAcc.getValue(ijk.offsetBy( 0, -3, 0)); + mValues[ 8] = mAcc.getValue(ijk.offsetBy( 0, -2, 0)); + mValues[ 9] = mAcc.getValue(ijk.offsetBy( 0, -1, 0)); + mValues[10] = mAcc.getValue(ijk.offsetBy( 0, 1, 0)); + mValues[11] = mAcc.getValue(ijk.offsetBy( 0, 2, 0)); + mValues[12] = mAcc.getValue(ijk.offsetBy( 0, 3, 0)); + + mValues[13] = mAcc.getValue(ijk.offsetBy( 0, 0, -3)); + mValues[14] = mAcc.getValue(ijk.offsetBy( 0, 0, -2)); + mValues[15] = mAcc.getValue(ijk.offsetBy( 0, 0, -1)); + mValues[16] = mAcc.getValue(ijk.offsetBy( 0, 0, 1)); + mValues[17] = mAcc.getValue(ijk.offsetBy( 0, 0, 2)); + mValues[18] = mAcc.getValue(ijk.offsetBy( 0, 0, 3)); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; + const ValueType mDx2, mInv2Dx, mInvDx2; +}; // WenoStencil class + + +////////////////////////////////////////////////////////////////////// + + +template +class CurvatureStencil: public BaseStencil, GridT, IsSafe> +{ + typedef CurvatureStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridT::ValueType ValueType; + + static const int SIZE = 19; + + CurvatureStencil(const GridType& grid) + : BaseType(grid, SIZE) + , mInv2Dx(ValueType(0.5 / grid.voxelSize()[0])) + , mInvDx2(ValueType(4.0 * mInv2Dx * mInv2Dx)) + { + } + + CurvatureStencil(const GridType& grid, Real dx) + : BaseType(grid, SIZE) + , mInv2Dx(ValueType(0.5 / dx)) + , mInvDx2(ValueType(4.0 * mInv2Dx * mInv2Dx)) + { + } + + /// @brief Return the mean curvature at the previously buffered location. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline ValueType meanCurvature() const + { + Real alpha, normGrad; + return this->meanCurvature(alpha, normGrad) ? + ValueType(alpha*mInv2Dx/math::Pow3(normGrad)) : 0; + } + + /// @brief Return the Gaussian curvature at the previously buffered location. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline ValueType gaussianCurvature() const + { + Real alpha, normGrad; + return this->gaussianCurvature(alpha, normGrad) ? + ValueType(alpha*mInvDx2/math::Pow4(normGrad)) : 0; + } + + /// @brief Return both the mean and the Gaussian curvature at the + /// previously buffered location. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline void curvatures(ValueType &mean, ValueType& gauss) const + { + Real alphaM, alphaG, normGrad; + if (this->curvatures(alphaM, alphaG, normGrad)) { + mean = ValueType(alphaM*mInv2Dx/math::Pow3(normGrad)); + gauss = ValueType(alphaG*mInvDx2/math::Pow4(normGrad)); + } else { + mean = gauss = 0; + } + } + + /// Return the mean curvature multiplied by the norm of the + /// central-difference gradient. This method is very useful for + /// mean-curvature flow of level sets! + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline ValueType meanCurvatureNormGrad() const + { + Real alpha, normGrad; + return this->meanCurvature(alpha, normGrad) ? + ValueType(alpha*mInvDx2/(2*math::Pow2(normGrad))) : 0; + } + + /// Return the mean Gaussian multiplied by the norm of the + /// central-difference gradient. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline ValueType gaussianCurvatureNormGrad() const + { + Real alpha, normGrad; + return this->gaussianCurvature(alpha, normGrad) ? + ValueType(2*alpha*mInv2Dx*mInvDx2/math::Pow3(normGrad)) : 0; + } + + /// @brief Return both the mean and the Gaussian curvature at the + /// previously buffered location. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline void curvaturesNormGrad(ValueType &mean, ValueType& gauss) const + { + Real alphaM, alphaG, normGrad; + if (this->curvatures(alphaM, alphaG, normGrad)) { + mean = ValueType(alphaM*mInvDx2/(2*math::Pow2(normGrad))); + gauss = ValueType(2*alphaG*mInv2Dx*mInvDx2/math::Pow3(normGrad)); + } else { + mean = gauss = 0; + } + } + + /// @brief Return the pair (minimum, maximum) principal curvature at the + /// previously buffered location. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline std::pair principalCurvatures() const + { + std::pair pair(0, 0);// min, max + Real alphaM, alphaG, normGrad; + if (this->curvatures(alphaM, alphaG, normGrad)) { + const Real mean = alphaM*mInv2Dx/math::Pow3(normGrad); + const Real tmp = std::sqrt(mean*mean - alphaG*mInvDx2/math::Pow4(normGrad)); + pair.first = ValueType(mean - tmp); + pair.second = ValueType(mean + tmp); + } + return pair;// min, max + } + + /// Return the Laplacian computed at the previously buffered + /// location by second-order central differencing. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline ValueType laplacian() const + { + return mInvDx2 * ( + mValues[1] + mValues[2] + + mValues[3] + mValues[4] + + mValues[5] + mValues[6] - 6*mValues[0]); + } + + /// Return the gradient computed at the previously buffered + /// location by second-order central differencing. + /// + /// @note This method should not be called until the stencil + /// buffer has been populated via a call to moveTo(ijk). + inline math::Vec3 gradient() const + { + return math::Vec3( + mValues[2] - mValues[1], + mValues[4] - mValues[3], + mValues[6] - mValues[5])*mInv2Dx; + } + +private: + inline void init(const Coord &ijk) + { + mValues[ 1] = mAcc.getValue(ijk.offsetBy(-1, 0, 0)); + mValues[ 2] = mAcc.getValue(ijk.offsetBy( 1, 0, 0)); + + mValues[ 3] = mAcc.getValue(ijk.offsetBy( 0, -1, 0)); + mValues[ 4] = mAcc.getValue(ijk.offsetBy( 0, 1, 0)); + + mValues[ 5] = mAcc.getValue(ijk.offsetBy( 0, 0, -1)); + mValues[ 6] = mAcc.getValue(ijk.offsetBy( 0, 0, 1)); + + mValues[ 7] = mAcc.getValue(ijk.offsetBy(-1, -1, 0)); + mValues[ 8] = mAcc.getValue(ijk.offsetBy( 1, -1, 0)); + mValues[ 9] = mAcc.getValue(ijk.offsetBy(-1, 1, 0)); + mValues[10] = mAcc.getValue(ijk.offsetBy( 1, 1, 0)); + + mValues[11] = mAcc.getValue(ijk.offsetBy(-1, 0, -1)); + mValues[12] = mAcc.getValue(ijk.offsetBy( 1, 0, -1)); + mValues[13] = mAcc.getValue(ijk.offsetBy(-1, 0, 1)); + mValues[14] = mAcc.getValue(ijk.offsetBy( 1, 0, 1)); + + mValues[15] = mAcc.getValue(ijk.offsetBy( 0, -1, -1)); + mValues[16] = mAcc.getValue(ijk.offsetBy( 0, 1, -1)); + mValues[17] = mAcc.getValue(ijk.offsetBy( 0, -1, 1)); + mValues[18] = mAcc.getValue(ijk.offsetBy( 0, 1, 1)); + } + + inline Real Dx() const { return 0.5*(mValues[2] - mValues[1]); }// * 1/dx + inline Real Dy() const { return 0.5*(mValues[4] - mValues[3]); }// * 1/dx + inline Real Dz() const { return 0.5*(mValues[6] - mValues[5]); }// * 1/dx + inline Real Dxx() const { return mValues[2] - 2 * mValues[0] + mValues[1]; }// * 1/dx2 + inline Real Dyy() const { return mValues[4] - 2 * mValues[0] + mValues[3]; }// * 1/dx2} + inline Real Dzz() const { return mValues[6] - 2 * mValues[0] + mValues[5]; }// * 1/dx2 + inline Real Dxy() const { return 0.25 * (mValues[10] - mValues[ 8] + mValues[ 7] - mValues[ 9]); }// * 1/dx2 + inline Real Dxz() const { return 0.25 * (mValues[14] - mValues[12] + mValues[11] - mValues[13]); }// * 1/dx2 + inline Real Dyz() const { return 0.25 * (mValues[18] - mValues[16] + mValues[15] - mValues[17]); }// * 1/dx2 + + inline bool meanCurvature(Real& alpha, Real& normGrad) const + { + // For performance all finite differences are unscaled wrt dx + const Real Dx = this->Dx(), Dy = this->Dy(), Dz = this->Dz(), + Dx2 = Dx*Dx, Dy2 = Dy*Dy, Dz2 = Dz*Dz, normGrad2 = Dx2 + Dy2 + Dz2; + if (normGrad2 <= math::Tolerance::value()) { + alpha = normGrad = 0; + return false; + } + const Real Dxx = this->Dxx(), Dyy = this->Dyy(), Dzz = this->Dzz(); + alpha = Dx2*(Dyy + Dzz) + Dy2*(Dxx + Dzz) + Dz2*(Dxx + Dyy) - + 2*(Dx*(Dy*this->Dxy() + Dz*this->Dxz()) + Dy*Dz*this->Dyz());// * 1/dx^4 + normGrad = std::sqrt(normGrad2); // * 1/dx + return true; + } + + inline bool gaussianCurvature(Real& alpha, Real& normGrad) const + { + // For performance all finite differences are unscaled wrt dx + const Real Dx = this->Dx(), Dy = this->Dy(), Dz = this->Dz(), + Dx2 = Dx*Dx, Dy2 = Dy*Dy, Dz2 = Dz*Dz, normGrad2 = Dx2 + Dy2 + Dz2; + if (normGrad2 <= math::Tolerance::value()) { + alpha = normGrad = 0; + return false; + } + const Real Dxx = this->Dxx(), Dyy = this->Dyy(), Dzz = this->Dzz(), + Dxy = this->Dxy(), Dxz = this->Dxz(), Dyz = this->Dyz(); + alpha = Dx2*(Dyy*Dzz - Dyz*Dyz) + Dy2*(Dxx*Dzz - Dxz*Dxz) + Dz2*(Dxx*Dyy - Dxy*Dxy) + + 2*( Dy*Dz*(Dxy*Dxz - Dyz*Dxx) + Dx*Dz*(Dxy*Dyz - Dxz*Dyy) + Dx*Dy*(Dxz*Dyz - Dxy*Dzz) );// * 1/dx^6 + normGrad = std::sqrt(normGrad2); // * 1/dx + return true; + } + inline bool curvatures(Real& alphaM, Real& alphaG, Real& normGrad) const + { + // For performance all finite differences are unscaled wrt dx + const Real Dx = this->Dx(), Dy = this->Dy(), Dz = this->Dz(), + Dx2 = Dx*Dx, Dy2 = Dy*Dy, Dz2 = Dz*Dz, normGrad2 = Dx2 + Dy2 + Dz2; + if (normGrad2 <= math::Tolerance::value()) { + alphaM = alphaG =normGrad = 0; + return false; + } + const Real Dxx = this->Dxx(), Dyy = this->Dyy(), Dzz = this->Dzz(), + Dxy = this->Dxy(), Dxz = this->Dxz(), Dyz = this->Dyz(); + alphaM = Dx2*(Dyy + Dzz) + Dy2*(Dxx + Dzz) + Dz2*(Dxx + Dyy) - + 2*(Dx*(Dy*Dxy + Dz*Dxz) + Dy*Dz*Dyz);// *1/dx^4 + alphaG = Dx2*(Dyy*Dzz - Dyz*Dyz) + Dy2*(Dxx*Dzz - Dxz*Dxz) + Dz2*(Dxx*Dyy - Dxy*Dxy) + + 2*( Dy*Dz*(Dxy*Dxz - Dyz*Dxx) + Dx*Dz*(Dxy*Dyz - Dxz*Dyy) + Dx*Dy*(Dxz*Dyz - Dxy*Dzz) );// *1/dx^6 + normGrad = std::sqrt(normGrad2); // * 1/dx + return true; + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; + const ValueType mInv2Dx, mInvDx2; +}; // CurvatureStencil class + + +////////////////////////////////////////////////////////////////////// + + +/// @brief Dense stencil of a given width +template +class DenseStencil: public BaseStencil, GridT, IsSafe> +{ + typedef DenseStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + DenseStencil(const GridType& grid, int halfWidth) + : BaseType(grid, /*size=*/math::Pow3(2 * halfWidth + 1)) + , mHalfWidth(halfWidth) + { + assert(halfWidth>0); + } + + inline const ValueType& getCenterValue() const { return mValues[(mValues.size()-1)>>1]; } + + /// @brief Initialize the stencil buffer with the values of voxel (x, y, z) + /// and its neighbors. + inline void moveTo(const Coord& ijk) + { + BaseType::mCenter = ijk; + this->init(ijk); + } + /// @brief Initialize the stencil buffer with the values of voxel + /// (x, y, z) and its neighbors. + template + inline void moveTo(const IterType& iter) + { + BaseType::mCenter = iter.getCoord(); + this->init(BaseType::mCenter); + } + +private: + /// Initialize the stencil buffer centered at (i, j, k). + /// @warning The center point is NOT at mValues[0] for this DenseStencil! + inline void init(const Coord& ijk) + { + int n = 0; + for (Coord p=ijk.offsetBy(-mHalfWidth), q=ijk.offsetBy(mHalfWidth); p[0] <= q[0]; ++p[0]) { + for (p[1] = ijk[1]-mHalfWidth; p[1] <= q[1]; ++p[1]) { + for (p[2] = ijk[2]-mHalfWidth; p[2] <= q[2]; ++p[2]) { + mValues[n++] = mAcc.getValue(p); + } + } + } + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mAcc; + using BaseType::mValues; + const int mHalfWidth; +};// DenseStencil class + + +} // end math namespace +} // namespace OPENVDB_VERSION_NAME +} // end openvdb namespace + +#endif // OPENVDB_MATH_STENCILS_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Transform.cc b/openvdb/math/Transform.cc new file mode 100644 index 00000000..430f9129 --- /dev/null +++ b/openvdb/math/Transform.cc @@ -0,0 +1,547 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Transform.h" +#include "LegacyFrustum.h" + +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + + +//////////////////////////////////////// + + +Transform::Transform(const MapBase::Ptr& map): + mMap(ConstPtrCast(map)) +{ + // auto-convert to simplest type + if (!mMap->isType() && mMap->isLinear()) { + AffineMap::Ptr affine = mMap->getAffineMap(); + mMap = simplify(affine); + } +} + +Transform::Transform(const Transform& other): + mMap(ConstPtrCast(other.baseMap())) +{ +} + + +//////////////////////////////////////// + + +// Factory methods + +Transform::Ptr +Transform::createLinearTransform(double voxelDim) +{ + return Transform::Ptr(new Transform( + MapBase::Ptr(new UniformScaleMap(voxelDim)))); +} + +Transform::Ptr +Transform::createLinearTransform(const Mat4R& m) +{ + return Transform::Ptr(new Transform(MapBase::Ptr(new AffineMap(m)))); +} + +Transform::Ptr +Transform::createFrustumTransform(const BBoxd& bbox, double taper, + double depth, double voxelDim) +{ + return Transform::Ptr(new Transform( + NonlinearFrustumMap(bbox, taper, depth).preScale(Vec3d(voxelDim, voxelDim, voxelDim)))); +} + + +//////////////////////////////////////// + + +void +Transform::read(std::istream& is) +{ + // Read the type name. + Name type = readString(is); + + if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_NEW_TRANSFORM) { + // Handle old-style transforms. + + if (type == "LinearTransform") { + // First read in the old transform's base class. + Coord tmpMin, tmpMax; + is.read(reinterpret_cast(&tmpMin), sizeof(Coord::ValueType) * 3); + is.read(reinterpret_cast(&tmpMax), sizeof(Coord::ValueType) * 3); + + // Second read in the old linear transform + Mat4d tmpLocalToWorld, tmpWorldToLocal, tmpVoxelToLocal, tmpLocalToVoxel; + + tmpLocalToWorld.read(is); + tmpWorldToLocal.read(is); + tmpVoxelToLocal.read(is); + tmpLocalToVoxel.read(is); + + // Convert and simplify + AffineMap::Ptr affineMap(new AffineMap(tmpVoxelToLocal*tmpLocalToWorld)); + mMap = simplify(affineMap); + + } else if (type == "FrustumTransform") { + + internal::LegacyFrustum legacyFrustum(is); + + CoordBBox bb = legacyFrustum.getBBox(); + BBoxd bbox(bb.min().asVec3d(), bb.max().asVec3d() + /* -Vec3d(1,1,1) */ + ); + double taper = legacyFrustum.getTaper(); + double depth = legacyFrustum.getDepth(); + + double nearPlaneWidth = legacyFrustum.getNearPlaneWidth(); + double nearPlaneDist = legacyFrustum.getNearPlaneDist(); + const Mat4d& camxform = legacyFrustum.getCamXForm(); + + // create the new frustum with these parameters + Mat4d xform(Mat4d::identity()); + xform.setToTranslation(Vec3d(0,0, -nearPlaneDist)); + xform.preScale(Vec3d(nearPlaneWidth, nearPlaneWidth, -nearPlaneWidth)); + + // create the linear part of the frustum (the second map) + Mat4d second = xform * camxform; + + // we might have precision problems, the constructor for the + // affine map is not forgiving (so we fix here). + const Vec4d col3 = second.col(3); + const Vec4d ref(0, 0, 0, 1); + + if (ref.eq(col3) ) { + second.setCol(3, ref); + } + + MapBase::Ptr linearMap(simplify(AffineMap(second).getAffineMap())); + + // note that the depth is scaled on the nearPlaneSize. + // the linearMap will uniformly scale the frustum to the correct size + // and rotate to align with the camera + mMap = MapBase::Ptr(new NonlinearFrustumMap( + bbox, taper, depth/nearPlaneWidth, linearMap)); + + } else { + OPENVDB_THROW(IoError, "Transforms of type " + type + " are no longer supported"); + } + } else { + // Check if the map has been registered. + if (!MapRegistry::isRegistered(type)) { + OPENVDB_THROW(KeyError, "Map " << type << " is not registered"); + } + + // Create the map of the type and then read it in. + mMap = math::MapRegistry::createMap(type); + mMap->read(is); + } +} + + +void +Transform::write(std::ostream& os) const +{ + if (!mMap) OPENVDB_THROW(IoError, "Transform does not have a map"); + + // Write the type-name of the map. + writeString(os, mMap->type()); + + mMap->write(os); +} + + +//////////////////////////////////////// + + +bool +Transform::isIdentity() const +{ + if (mMap->isLinear()) { + return mMap->getAffineMap()->isIdentity(); + } else if ( mMap->isType() ) { + NonlinearFrustumMap::Ptr frustum = + StaticPtrCast(mMap); + return frustum->isIdentity(); + } + // unknown nonlinear map type + return false; +} + + +//////////////////////////////////////// + + +void +Transform::preRotate(double radians, const Axis axis) +{ + mMap = mMap->preRotate(radians, axis); +} + +void +Transform::preTranslate(const Vec3d& t) +{ + mMap = mMap->preTranslate(t); +} + +void +Transform::preScale(const Vec3d& s) +{ + mMap = mMap->preScale(s); +} + +void +Transform::preScale(double s) +{ + const Vec3d vec(s,s,s); + mMap = mMap->preScale(vec); +} + +void +Transform::preShear(double shear, Axis axis0, Axis axis1) +{ + mMap = mMap->preShear(shear, axis0, axis1); +} + +void +Transform::preMult(const Mat4d& m) +{ + if (mMap->isLinear()) { + + const Mat4d currentMat4 = mMap->getAffineMap()->getMat4(); + const Mat4d newMat4 = m * currentMat4; + + AffineMap::Ptr affineMap( new AffineMap( newMat4) ); + mMap = simplify(affineMap); + + } else if (mMap->isType() ) { + + NonlinearFrustumMap::Ptr currentFrustum = + StaticPtrCast(mMap); + + const Mat4d currentMat4 = currentFrustum->secondMap().getMat4(); + const Mat4d newMat4 = m * currentMat4; + + AffineMap affine{newMat4}; + + NonlinearFrustumMap::Ptr frustum{new NonlinearFrustumMap{ + currentFrustum->getBBox(), + currentFrustum->getTaper(), + currentFrustum->getDepth(), + affine.copy() + }}; + mMap = StaticPtrCast(frustum); + } + +} + +void +Transform::preMult(const Mat3d& m) +{ + Mat4d mat4 = Mat4d::identity(); + mat4.setMat3(m); + preMult(mat4); +} + +void +Transform::postRotate(double radians, const Axis axis) +{ + mMap = mMap->postRotate(radians, axis); +} + +void +Transform::postTranslate(const Vec3d& t) +{ + mMap = mMap->postTranslate(t); +} + +void +Transform::postScale(const Vec3d& s) +{ + mMap = mMap->postScale(s); +} + +void +Transform::postScale(double s) +{ + const Vec3d vec(s,s,s); + mMap = mMap->postScale(vec); +} + +void +Transform::postShear(double shear, Axis axis0, Axis axis1) +{ + mMap = mMap->postShear(shear, axis0, axis1); +} + + +void +Transform::postMult(const Mat4d& m) +{ + if (mMap->isLinear()) { + + const Mat4d currentMat4 = mMap->getAffineMap()->getMat4(); + const Mat4d newMat4 = currentMat4 * m; + + AffineMap::Ptr affineMap{new AffineMap{newMat4}}; + mMap = simplify(affineMap); + + } else if (mMap->isType()) { + + NonlinearFrustumMap::Ptr currentFrustum = + StaticPtrCast(mMap); + + const Mat4d currentMat4 = currentFrustum->secondMap().getMat4(); + const Mat4d newMat4 = currentMat4 * m; + + AffineMap affine{newMat4}; + + NonlinearFrustumMap::Ptr frustum{new NonlinearFrustumMap{ + currentFrustum->getBBox(), + currentFrustum->getTaper(), + currentFrustum->getDepth(), + affine.copy() + }}; + mMap = StaticPtrCast(frustum); + } + +} + +void +Transform::postMult(const Mat3d& m) +{ + Mat4d mat4 = Mat4d::identity(); + mat4.setMat3(m); + postMult(mat4); +} + + +//////////////////////////////////////// + + +BBoxd +Transform::indexToWorld(const CoordBBox& indexBBox) const +{ + return this->indexToWorld(BBoxd(indexBBox.min().asVec3d(), indexBBox.max().asVec3d())); +} + + +BBoxd +Transform::indexToWorld(const BBoxd& indexBBox) const +{ + const Vec3d &imin = indexBBox.min(), &imax = indexBBox.max(); + + Vec3d corners[8]; + corners[0] = imin; + corners[1] = Vec3d(imax(0), imin(1), imin(2)); + corners[2] = Vec3d(imax(0), imax(1), imin(2)); + corners[3] = Vec3d(imin(0), imax(1), imin(2)); + corners[4] = Vec3d(imin(0), imin(1), imax(2)); + corners[5] = Vec3d(imax(0), imin(1), imax(2)); + corners[6] = imax; + corners[7] = Vec3d(imin(0), imax(1), imax(2)); + + BBoxd worldBBox; + Vec3d &wmin = worldBBox.min(), &wmax = worldBBox.max(); + + wmin = wmax = this->indexToWorld(corners[0]); + for (int i = 1; i < 8; ++i) { + Vec3d image = this->indexToWorld(corners[i]); + wmin = minComponent(wmin, image); + wmax = maxComponent(wmax, image); + } + return worldBBox; +} + + +BBoxd +Transform::worldToIndex(const BBoxd& worldBBox) const +{ + Vec3d indexMin, indexMax; + calculateBounds(*this, worldBBox.min(), worldBBox.max(), indexMin, indexMax); + return BBoxd(indexMin, indexMax); +} + + +CoordBBox +Transform::worldToIndexCellCentered(const BBoxd& worldBBox) const +{ + Vec3d indexMin, indexMax; + calculateBounds(*this, worldBBox.min(), worldBBox.max(), indexMin, indexMax); + return CoordBBox(Coord::round(indexMin), Coord::round(indexMax)); +} + + +CoordBBox +Transform::worldToIndexNodeCentered(const BBoxd& worldBBox) const +{ + Vec3d indexMin, indexMax; + calculateBounds(*this, worldBBox.min(), worldBBox.max(), indexMin, indexMax); + return CoordBBox(Coord::floor(indexMin), Coord::floor(indexMax)); +} + + +//////////////////////////////////////// + +// Utility methods + +void +calculateBounds(const Transform& t, + const Vec3d& minWS, + const Vec3d& maxWS, + Vec3d& minIS, + Vec3d& maxIS) +{ + /// the pre-image of the 8 corners of the box + Vec3d corners[8]; + corners[0] = minWS; + corners[1] = Vec3d(maxWS(0), minWS(1), minWS(2)); + corners[2] = Vec3d(maxWS(0), maxWS(1), minWS(2)); + corners[3] = Vec3d(minWS(0), maxWS(1), minWS(2)); + corners[4] = Vec3d(minWS(0), minWS(1), maxWS(2)); + corners[5] = Vec3d(maxWS(0), minWS(1), maxWS(2)); + corners[6] = maxWS; + corners[7] = Vec3d(minWS(0), maxWS(1), maxWS(2)); + + Vec3d pre_image; + minIS = t.worldToIndex(corners[0]); + maxIS = minIS; + for (int i = 1; i < 8; ++i) { + pre_image = t.worldToIndex(corners[i]); + for (int j = 0; j < 3; ++j) { + minIS(j) = std::min(minIS(j), pre_image(j)); + maxIS(j) = std::max(maxIS(j), pre_image(j)); + } + } +} + + +//////////////////////////////////////// + + +bool +Transform::operator==(const Transform& other) const +{ + if (!this->voxelSize().eq(other.voxelSize())) return false; + + if (this->mapType() == other.mapType()) { + return this->baseMap()->isEqual(*other.baseMap()); + } + + if (this->isLinear() && other.isLinear()) { + // promote both maps to mat4 form and compare + return ( *(this->baseMap()->getAffineMap()) == + *(other.baseMap()->getAffineMap()) ); + } + + return this->baseMap()->isEqual(*other.baseMap()); +} + + +//////////////////////////////////////// + + +void +Transform::print(std::ostream& os, const std::string& indent) const +{ + struct Local { + // Print a Vec4d more compactly than Vec4d::str() does. + static std::string rowAsString(const Vec4d& row) + { + std::ostringstream ostr; + ostr << "[" << std::setprecision(3) << row[0] << ", " + << row[1] << ", " << row[2] << ", " << row[3] << "] "; + return ostr.str(); + } + }; + + // Write to a string stream so that I/O manipulators don't affect the output stream. + std::ostringstream ostr; + + { + Vec3d dim = this->voxelSize(); + if (dim.eq(Vec3d(dim[0]))) { + ostr << indent << std::left << "voxel size: " << std::setprecision(3) << dim[0]; + } else { + ostr << indent << std::left << "voxel dimensions: [" << std::setprecision(3) + << dim[0] << ", " << dim[1] << ", " << dim[2] << "]"; + } + ostr << "\n"; + } + + if (this->isLinear()) { + openvdb::Mat4R v2w = this->baseMap()->getAffineMap()->getMat4(); + + ostr << indent << std::left << "index to world:\n"; + for (int row = 0; row < 4; ++row) { + ostr << indent << " " << std::left << Local::rowAsString(v2w[row]) << "\n"; + } + + } else if (this->mapType() == NonlinearFrustumMap::mapType()) { + const NonlinearFrustumMap& frustum = + static_cast(*this->baseMap()); + const openvdb::Mat4R linear = this->baseMap()->getAffineMap()->getMat4(); + + std::vector linearRow; + size_t w = 0; + for (int row = 0; row < 4; ++row) { + std::string str = Local::rowAsString(linear[row]); + w = std::max(w, str.size()); + linearRow.push_back(str); + } + w = std::max(w, 30); + const int iw = int(w); + + // Print rows of the linear component matrix side-by-side with frustum parameters. + ostr << indent << std::left << std::setw(iw) << "linear:" + << " frustum:\n"; + ostr << indent << " " << std::left << std::setw(iw) << linearRow[0] + << " taper: " << frustum.getTaper() << "\n"; + ostr << indent << " " << std::left << std::setw(iw) << linearRow[1] + << " depth: " << frustum.getDepth() << "\n"; + + std::ostringstream ostmp; + ostmp << indent << " " << std::left << std::setw(iw) << linearRow[2] + << " bounds: " << frustum.getBBox(); + if (ostmp.str().size() < 79) { + ostr << ostmp.str() << "\n"; + ostr << indent << " " << std::left << std::setw(iw) << linearRow[3] << "\n"; + } else { + // If the frustum bounding box doesn't fit on one line, split it into two lines. + ostr << indent << " " << std::left << std::setw(iw) << linearRow[2] + << " bounds: " << frustum.getBBox().min() << " ->\n"; + ostr << indent << " " << std::left << std::setw(iw) << linearRow[3] + << " " << frustum.getBBox().max() << "\n"; + } + + } else { + /// @todo Handle other map types. + } + + os << ostr.str(); +} + + +//////////////////////////////////////// + + +std::ostream& +operator<<(std::ostream& os, const Transform& t) +{ + os << "Transform type: " << t.baseMap()->type() << std::endl; + os << t.baseMap()->str() << std::endl; + return os; +} + + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/math/Transform.h b/openvdb/math/Transform.h new file mode 100644 index 00000000..b6029768 --- /dev/null +++ b/openvdb/math/Transform.h @@ -0,0 +1,279 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_TRANSFORM_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_TRANSFORM_HAS_BEEN_INCLUDED + +#include "Maps.h" +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +// Forward declaration +class Transform; + + +// Utility methods + +/// @brief Calculate an axis-aligned bounding box in index space from an +/// axis-aligned bounding box in world space. +/// @see Transform::worldToIndex(const BBoxd&) const +OPENVDB_API void +calculateBounds(const Transform& t, const Vec3d& minWS, const Vec3d& maxWS, + Vec3d& minIS, Vec3d& maxIS); + +/// @todo Calculate an axis-aligned bounding box in index space from a +/// bounding sphere in world space. +//void calculateBounds(const Transform& t, const Vec3d& center, const Real radius, +// Vec3d& minIS, Vec3d& maxIS); + + +//////////////////////////////////////// + + +/// @class Transform +class OPENVDB_API Transform +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + Transform(): mMap(MapBase::Ptr(new ScaleMap())) {} + Transform(const MapBase::Ptr&); + Transform(const Transform&); + ~Transform() {} + + Ptr copy() const { return Ptr(new Transform(mMap->copy())); } + + //@{ + /// @brief Create and return a shared pointer to a new transform. + static Transform::Ptr createLinearTransform(double voxelSize = 1.0); + static Transform::Ptr createLinearTransform(const Mat4R&); + static Transform::Ptr createFrustumTransform(const BBoxd&, double taper, + double depth, double voxelSize = 1.0); + //@} + + /// Return @c true if the transformation map is exclusively linear/affine. + bool isLinear() const { return mMap->isLinear(); } + + /// Return @c true if the transform is equivalent to an idenity. + bool isIdentity() const ; + /// Return the transformation map's type-name + Name mapType() const { return mMap->type(); } + + + //@{ + /// @brief Update the linear (affine) map by prepending or + /// postfixing the appropriate operation. In the case of + /// a frustum, the pre-operations apply to the linear part + /// of the transform and not the entire transform, while the + /// post-operations are allways applied last. + void preRotate(double radians, const Axis axis = X_AXIS); + void preTranslate(const Vec3d&); + void preScale(const Vec3d&); + void preScale(double); + void preShear(double shear, Axis axis0, Axis axis1); + void preMult(const Mat4d&); + void preMult(const Mat3d&); + + void postRotate(double radians, const Axis axis = X_AXIS); + void postTranslate(const Vec3d&); + void postScale(const Vec3d&); + void postScale(double); + void postShear(double shear, Axis axis0, Axis axis1); + void postMult(const Mat4d&); + void postMult(const Mat3d&); + //@} + + /// Return the size of a voxel using the linear component of the map. + Vec3d voxelSize() const { return mMap->voxelSize(); } + /// @brief Return the size of a voxel at position (x, y, z). + /// @note Maps that have a nonlinear component (e.g., perspective and frustum maps) + /// have position-dependent voxel sizes. + Vec3d voxelSize(const Vec3d& xyz) const { return mMap->voxelSize(xyz); } + + /// Return the voxel volume of the linear component of the map. + double voxelVolume() const { return mMap->determinant(); } + /// Return the voxel volume at position (x, y, z). + double voxelVolume(const Vec3d& xyz) const { return mMap->determinant(xyz); } + /// Return true if the voxels in world space are uniformly sized cubes + bool hasUniformScale() const { return mMap->hasUniformScale(); } + + //@{ + /// @brief Apply this transformation to the given coordinates. + Vec3d indexToWorld(const Vec3d& xyz) const { return mMap->applyMap(xyz); } + Vec3d indexToWorld(const Coord& ijk) const { return mMap->applyMap(ijk.asVec3d()); } + Vec3d worldToIndex(const Vec3d& xyz) const { return mMap->applyInverseMap(xyz); } + Coord worldToIndexCellCentered(const Vec3d& xyz) const {return Coord::round(worldToIndex(xyz));} + Coord worldToIndexNodeCentered(const Vec3d& xyz) const {return Coord::floor(worldToIndex(xyz));} + //@} + + //@{ + /// @brief Apply this transformation to the given index-space bounding box. + /// @return an axis-aligned world-space bounding box + BBoxd indexToWorld(const CoordBBox&) const; + BBoxd indexToWorld(const BBoxd&) const; + //@} + //@{ + /// @brief Apply the inverse of this transformation to the given world-space bounding box. + /// @return an axis-aligned index-space bounding box + BBoxd worldToIndex(const BBoxd&) const; + CoordBBox worldToIndexCellCentered(const BBoxd&) const; + CoordBBox worldToIndexNodeCentered(const BBoxd&) const; + //@} + + //@{ + /// Return a base pointer to the transformation map. + MapBase::ConstPtr baseMap() const { return mMap; } + MapBase::Ptr baseMap() { return mMap; } + //@} + + //@{ + /// @brief Return the result of downcasting the base map pointer to a + /// @c MapType pointer, or return a null pointer if the types are incompatible. + template typename MapType::Ptr map(); + template typename MapType::ConstPtr map() const; + template typename MapType::ConstPtr constMap() const; + //@} + + /// Unserialize this transform from the given stream. + void read(std::istream&); + /// Serialize this transform to the given stream. + void write(std::ostream&) const; + + /// @brief Print a description of this transform. + /// @param os a stream to which to write textual information + /// @param indent a string with which to prefix each line of text + void print(std::ostream& os = std::cout, const std::string& indent = "") const; + + bool operator==(const Transform& other) const; + inline bool operator!=(const Transform& other) const { return !(*this == other); } + +private: + MapBase::Ptr mMap; +}; // class Transform + + +OPENVDB_API std::ostream& operator<<(std::ostream&, const Transform&); + + +//////////////////////////////////////// + + +template +inline typename MapType::Ptr +Transform::map() +{ + if (mMap->type() == MapType::mapType()) { + return StaticPtrCast(mMap); + } + return typename MapType::Ptr(); +} + + +template +inline typename MapType::ConstPtr +Transform::map() const +{ + return ConstPtrCast( + const_cast(this)->map()); +} + + +template +inline typename MapType::ConstPtr +Transform::constMap() const +{ + return map(); +} + + +//////////////////////////////////////// + + +/// Helper function used internally by processTypedMap() +template +inline void +doProcessTypedMap(Transform& transform, OpType& op) +{ + ResolvedMapType& resolvedMap = *transform.map(); +#ifdef _MSC_VER + op.operator()(resolvedMap); +#else + op.template operator()(resolvedMap); +#endif +} + +/// Helper function used internally by processTypedMap() +template +inline void +doProcessTypedMap(const Transform& transform, OpType& op) +{ + const ResolvedMapType& resolvedMap = *transform.map(); +#ifdef _MSC_VER + op.operator()(resolvedMap); +#else + op.template operator()(resolvedMap); +#endif +} + + +/// @brief Utility function that, given a generic map pointer, +/// calls a functor on the fully-resoved map +/// +/// Usage: +/// @code +/// struct Foo { +/// template +/// void operator()(const MapT& map) const { blah } +/// }; +/// +/// processTypedMap(myMap, Foo()); +/// @endcode +/// +/// @return @c false if the grid type is unknown or unhandled. +template +bool +processTypedMap(TransformType& transform, OpType& op) +{ + using namespace openvdb; + + const Name mapType = transform.mapType(); + if (mapType == UniformScaleMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == UniformScaleTranslateMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == ScaleMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == ScaleTranslateMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == UnitaryMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == AffineMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == TranslationMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == NonlinearFrustumMap::mapType()) { + doProcessTypedMap(transform, op); + } else { + return false; + } + return true; +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_TRANSFORM_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Tuple.h b/openvdb/math/Tuple.h new file mode 100644 index 00000000..c8bcde49 --- /dev/null +++ b/openvdb/math/Tuple.h @@ -0,0 +1,245 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Tuple.h +/// @author Ben Kwa + +#ifndef OPENVDB_MATH_TUPLE_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_TUPLE_HAS_BEEN_INCLUDED + +#include "Math.h" +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief Dummy class for tag dispatch of conversion constructors +struct Conversion {}; + + +/// @class Tuple "Tuple.h" +/// A base class for homogenous tuple types +template +class Tuple { +public: + using value_type = T; + using ValueType = T; + + static const int size = SIZE; + + /// @brief Default ctor. Does nothing. + /// @details This is required because declaring a copy (or other) constructor + /// prevents the compiler from synthesizing a default constructor. + Tuple() {} + + /// Copy constructor. Used when the class signature matches exactly. + Tuple(Tuple const& src) { + for (int i = 0; i < SIZE; ++i) { + mm[i] = src.mm[i]; + } + } + + /// @brief Assignment operator + /// @details This is required because declaring a copy (or other) constructor + /// prevents the compiler from synthesizing a default assignment operator. + Tuple& operator=(Tuple const& src) { + if (&src != this) { + for (int i = 0; i < SIZE; ++i) { + mm[i] = src.mm[i]; + } + } + return *this; + } + + /// @brief Conversion constructor. + /// @details Tuples with different value types and different sizes can be + /// interconverted using this member. Converting from a larger tuple + /// results in truncation; converting from a smaller tuple results in + /// the extra data members being zeroed out. This function assumes that + /// the integer 0 is convertible to the tuple's value type. + template + explicit Tuple(Tuple const &src) { + enum { COPY_END = (SIZE < src_size ? SIZE : src_size) }; + + for (int i = 0; i < COPY_END; ++i) { + mm[i] = src[i]; + } + for (int i = COPY_END; i < SIZE; ++i) { + mm[i] = 0; + } + } + + T operator[](int i) const { + // we'd prefer to use size_t, but can't because gcc3.2 doesn't like + // it - it conflicts with child class conversion operators to + // pointer types. +// assert(i >= 0 && i < SIZE); + return mm[i]; + } + + T& operator[](int i) { + // see above for size_t vs int +// assert(i >= 0 && i < SIZE); + return mm[i]; + } + + /// @name Compatibility + /// These are mostly for backwards compability with functions that take + /// old-style Vs (which are just arrays). + //@{ + /// Copies this tuple into an array of a compatible type + template + void toV(S *v) const { + for (int i = 0; i < SIZE; ++i) { + v[i] = mm[i]; + } + } + + /// Exposes the internal array. Be careful when using this function. + value_type *asV() { + return mm; + } + /// Exposes the internal array. Be careful when using this function. + value_type const *asV() const { + return mm; + } + //@} Compatibility + + /// @return string representation of Classname + std::string str() const { + std::ostringstream buffer; + + buffer << "["; + + // For each column + for (unsigned j(0); j < SIZE; j++) { + if (j) buffer << ", "; + buffer << PrintCast(mm[j]); + } + + buffer << "]"; + + return buffer.str(); + } + + void write(std::ostream& os) const { + os.write(reinterpret_cast(&mm), sizeof(T)*SIZE); + } + void read(std::istream& is) { + is.read(reinterpret_cast(&mm), sizeof(T)*SIZE); + } + + /// True if a Nan is present in this tuple + bool isNan() const { + for (int i = 0; i < SIZE; ++i) { + if (math::isNan(mm[i])) return true; + } + return false; + } + + /// True if an Inf is present in this tuple + bool isInfinite() const { + for (int i = 0; i < SIZE; ++i) { + if (math::isInfinite(mm[i])) return true; + } + return false; + } + + /// True if no Nan or Inf values are present + bool isFinite() const { + for (int i = 0; i < SIZE; ++i) { + if (!math::isFinite(mm[i])) return false; + } + return true; + } + + /// True if all elements are exactly zero + bool isZero() const { + for (int i = 0; i < SIZE; ++i) { + if (!math::isZero(mm[i])) return false; + } + return true; + } + +protected: + T mm[SIZE]; +}; + + +//////////////////////////////////////// + + +/// @return true if t0 < t1, comparing components in order of significance. +template +bool +operator<(const Tuple& t0, const Tuple& t1) +{ + for (int i = 0; i < SIZE-1; ++i) { + if (!isExactlyEqual(t0[i], t1[i])) return t0[i] < t1[i]; + } + return t0[SIZE-1] < t1[SIZE-1]; +} + + +/// @return true if t0 > t1, comparing components in order of significance. +template +bool +operator>(const Tuple& t0, const Tuple& t1) +{ + for (int i = 0; i < SIZE-1; ++i) { + if (!isExactlyEqual(t0[i], t1[i])) return t0[i] > t1[i]; + } + return t0[SIZE-1] > t1[SIZE-1]; +} + + +//////////////////////////////////////// + + +/// @return the absolute value of the given Tuple. +template +Tuple +Abs(const Tuple& t) +{ + Tuple result; + for (int i = 0; i < SIZE; ++i) result[i] = math::Abs(t[i]); + return result; +} + +/// Return @c true if a Nan is present in the tuple. +template +inline bool isNan(const Tuple& t) { return t.isNan(); } + +/// Return @c true if an Inf is present in the tuple. +template +inline bool isInfinite(const Tuple& t) { return t.isInfinite(); } + +/// Return @c true if no Nan or Inf values are present. +template +inline bool isFinite(const Tuple& t) { return t.isFinite(); } + +/// Return @c true if all elements are exactly equal to zero. +template +inline bool isZero(const Tuple& t) { return t.isZero(); } + +//////////////////////////////////////// + + +/// Write a Tuple to an output stream +template +std::ostream& operator<<(std::ostream& ostr, const Tuple& classname) +{ + ostr << classname.str(); + return ostr; +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_TUPLE_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Vec2.h b/openvdb/math/Vec2.h new file mode 100644 index 00000000..124c7660 --- /dev/null +++ b/openvdb/math/Vec2.h @@ -0,0 +1,538 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_VEC2_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_VEC2_HAS_BEEN_INCLUDED + +#include +#include "Math.h" +#include "Tuple.h" +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Mat2; + +template +class Vec2: public Tuple<2, T> +{ +public: + using value_type = T; + using ValueType = T; + + /// Trivial constructor, the vector is NOT initialized + Vec2() {} + + /// @brief Construct a vector all of whose components have the given value. + explicit Vec2(T val) { this->mm[0] = this->mm[1] = val; } + + /// Constructor with two arguments, e.g. Vec2f v(1,2,3); + Vec2(T x, T y) + { + this->mm[0] = x; + this->mm[1] = y; + } + + /// Constructor with array argument, e.g. float a[2]; Vec2f v(a); + template + Vec2(Source *a) + { + this->mm[0] = static_cast(a[0]); + this->mm[1] = static_cast(a[1]); + } // trivial + + /// Conversion constructor + template + explicit Vec2(const Tuple<2, Source> &t) + { + this->mm[0] = static_cast(t[0]); + this->mm[1] = static_cast(t[1]); + } + + /// @brief Construct a vector all of whose components have the given value, + /// which may be of an arithmetic type different from this vector's value type. + /// @details Type conversion warnings are suppressed. + template + explicit Vec2(Other val, + typename std::enable_if::value, Conversion>::type = Conversion{}) + { + this->mm[0] = this->mm[1] = static_cast(val); + } + + /// Reference to the component, e.g. v.x() = 4.5f; + T& x() {return this->mm[0];} + T& y() {return this->mm[1];} + + /// Get the component, e.g. float f = v.y(); + T x() const {return this->mm[0];} + T y() const {return this->mm[1];} + + /// Alternative indexed reference to the elements + T& operator()(int i) {return this->mm[i];} + + /// Alternative indexed constant reference to the elements, + T operator()(int i) const {return this->mm[i];} + + T* asPointer() {return this->mm;} + const T* asPointer() const {return this->mm;} + + /// "this" vector gets initialized to [x, y, z], + /// calling v.init(); has same effect as calling v = Vec2::zero(); + const Vec2& init(T x=0, T y=0) + { + this->mm[0] = x; this->mm[1] = y; + return *this; + } + + /// Set "this" vector to zero + const Vec2& setZero() + { + this->mm[0] = 0; this->mm[1] = 0; + return *this; + } + + /// Assignment operator + template + const Vec2& operator=(const Vec2 &v) + { + // note: don't static_cast because that suppresses warnings + this->mm[0] = v[0]; + this->mm[1] = v[1]; + + return *this; + } + + /// Equality operator, does exact floating point comparisons + bool operator==(const Vec2 &v) const + { + return (isExactlyEqual(this->mm[0], v.mm[0]) && isExactlyEqual(this->mm[1], v.mm[1])); + } + + /// Inequality operator, does exact floating point comparisons + bool operator!=(const Vec2 &v) const { return !(*this==v); } + + /// Test if "this" vector is equivalent to vector v with tolerance of eps + bool eq(const Vec2 &v, T eps = static_cast(1.0e-7)) const + { + return isApproxEqual(this->mm[0], v.mm[0], eps) && + isApproxEqual(this->mm[1], v.mm[1], eps); + } // trivial + + /// Negation operator, for e.g. v1 = -v2; + Vec2 operator-() const {return Vec2(-this->mm[0], -this->mm[1]);} + + /// this = v1 + v2 + /// "this", v1 and v2 need not be distinct objects, e.g. v.add(v1,v); + template + const Vec2& add(const Vec2 &v1, const Vec2 &v2) + { + this->mm[0] = v1[0] + v2[0]; + this->mm[1] = v1[1] + v2[1]; + + return *this; + } + + /// this = v1 - v2 + /// "this", v1 and v2 need not be distinct objects, e.g. v.sub(v1,v); + template + const Vec2& sub(const Vec2 &v1, const Vec2 &v2) + { + this->mm[0] = v1[0] - v2[0]; + this->mm[1] = v1[1] - v2[1]; + + return *this; + } + + /// this = scalar*v, v need not be a distinct object from "this", + /// e.g. v.scale(1.5,v1); + template + const Vec2& scale(T0 scalar, const Vec2 &v) + { + this->mm[0] = scalar * v[0]; + this->mm[1] = scalar * v[1]; + + return *this; + } + + template + const Vec2 &div(T0 scalar, const Vec2 &v) + { + this->mm[0] = v[0] / scalar; + this->mm[1] = v[1] / scalar; + + return *this; + } + + /// Dot product + T dot(const Vec2 &v) const { return this->mm[0]*v[0] + this->mm[1]*v[1]; } // trivial + + /// Length of the vector + T length() const + { + return static_cast(sqrt(double(this->mm[0]*this->mm[0] + this->mm[1]*this->mm[1]))); + } + + /// Squared length of the vector, much faster than length() as it + /// does not involve square root + T lengthSqr() const { return (this->mm[0]*this->mm[0] + this->mm[1]*this->mm[1]); } + + /// Return a reference to itsef after the exponent has been + /// applied to all the vector components. + inline const Vec2& exp() + { + this->mm[0] = std::exp(this->mm[0]); + this->mm[1] = std::exp(this->mm[1]); + return *this; + } + + /// Return a reference to itself after log has been + /// applied to all the vector components. + inline const Vec2& log() + { + this->mm[0] = std::log(this->mm[0]); + this->mm[1] = std::log(this->mm[1]); + return *this; + } + + /// Return the sum of all the vector components. + inline T sum() const + { + return this->mm[0] + this->mm[1]; + } + + /// Return the product of all the vector components. + inline T product() const + { + return this->mm[0] * this->mm[1]; + } + + /// this = normalized this + bool normalize(T eps = static_cast(1.0e-8)) + { + T d = length(); + if (isApproxEqual(d, T(0), eps)) { + return false; + } + *this *= (T(1) / d); + return true; + } + + /// return normalized this, throws if null vector + Vec2 unit(T eps=0) const + { + T d; + return unit(eps, d); + } + + /// return normalized this and length, throws if null vector + Vec2 unit(T eps, T& len) const + { + len = length(); + if (isApproxEqual(len, T(0), eps)) { + OPENVDB_THROW(ArithmeticError, "Normalizing null 2-vector"); + } + return *this / len; + } + + /// return normalized this, or (1, 0) if this is null vector + Vec2 unitSafe() const + { + T l2 = lengthSqr(); + return l2 ? *this/static_cast(sqrt(l2)) : Vec2(1,0); + } + + /// Multiply each element of this vector by @a scalar. + template + const Vec2 &operator*=(S scalar) + { + this->mm[0] *= scalar; + this->mm[1] *= scalar; + return *this; + } + + /// Multiply each element of this vector by the corresponding element of the given vector. + template + const Vec2 &operator*=(const Vec2 &v1) + { + this->mm[0] *= v1[0]; + this->mm[1] *= v1[1]; + return *this; + } + + /// Divide each element of this vector by @a scalar. + template + const Vec2 &operator/=(S scalar) + { + this->mm[0] /= scalar; + this->mm[1] /= scalar; + return *this; + } + + /// Divide each element of this vector by the corresponding element of the given vector. + template + const Vec2 &operator/=(const Vec2 &v1) + { + this->mm[0] /= v1[0]; + this->mm[1] /= v1[1]; + return *this; + } + + /// Add @a scalar to each element of this vector. + template + const Vec2 &operator+=(S scalar) + { + this->mm[0] += scalar; + this->mm[1] += scalar; + return *this; + } + + /// Add each element of the given vector to the corresponding element of this vector. + template + const Vec2 &operator+=(const Vec2 &v1) + { + this->mm[0] += v1[0]; + this->mm[1] += v1[1]; + return *this; + } + + /// Subtract @a scalar from each element of this vector. + template + const Vec2 &operator-=(S scalar) + { + this->mm[0] -= scalar; + this->mm[1] -= scalar; + return *this; + } + + /// Subtract each element of the given vector from the corresponding element of this vector. + template + const Vec2 &operator-=(const Vec2 &v1) + { + this->mm[0] -= v1[0]; + this->mm[1] -= v1[1]; + return *this; + } + + // Number of cols, rows, elements + static unsigned numRows() { return 1; } + static unsigned numColumns() { return 2; } + static unsigned numElements() { return 2; } + + /// Returns the scalar component of v in the direction of onto, onto need + /// not be unit. e.g float c = Vec2f::component(v1,v2); + T component(const Vec2 &onto, T eps = static_cast(1.0e-8)) const + { + T l = onto.length(); + if (isApproxEqual(l, T(0), eps)) return 0; + + return dot(onto)*(T(1)/l); + } + + /// Return the projection of v onto the vector, onto need not be unit + /// e.g. Vec2f v = Vec2f::projection(v,n); + Vec2 projection(const Vec2 &onto, T eps = static_cast(1.0e-8)) const + { + T l = onto.lengthSqr(); + if (isApproxEqual(l, T(0), eps)) return Vec2::zero(); + + return onto*(dot(onto)*(T(1)/l)); + } + + /// Return an arbitrary unit vector perpendicular to v + /// Vector v must be a unit vector + /// e.g. v.normalize(); Vec2f n = Vec2f::getArbPerpendicular(v); + Vec2 getArbPerpendicular() const { return Vec2(-this->mm[1], this->mm[0]); } + + /// Predefined constants, e.g. Vec2f v = Vec2f::xNegAxis(); + static Vec2 zero() { return Vec2(0, 0); } + static Vec2 ones() { return Vec2(1, 1); } +}; + + +/// Multiply each element of the given vector by @a scalar and return the result. +template +inline Vec2::type> operator*(S scalar, const Vec2 &v) +{ + return v * scalar; +} + +/// Multiply each element of the given vector by @a scalar and return the result. +template +inline Vec2::type> operator*(const Vec2 &v, S scalar) +{ + Vec2::type> result(v); + result *= scalar; + return result; +} + +/// Multiply corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec2::type> operator*(const Vec2 &v0, const Vec2 &v1) +{ + Vec2::type> result(v0[0] * v1[0], v0[1] * v1[1]); + return result; +} + +/// Divide @a scalar by each element of the given vector and return the result. +template +inline Vec2::type> operator/(S scalar, const Vec2 &v) +{ + return Vec2::type>(scalar/v[0], scalar/v[1]); +} + +/// Divide each element of the given vector by @a scalar and return the result. +template +inline Vec2::type> operator/(const Vec2 &v, S scalar) +{ + Vec2::type> result(v); + result /= scalar; + return result; +} + +/// Divide corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec2::type> operator/(const Vec2 &v0, const Vec2 &v1) +{ + Vec2::type> result(v0[0] / v1[0], v0[1] / v1[1]); + return result; +} + +/// Add corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec2::type> operator+(const Vec2 &v0, const Vec2 &v1) +{ + Vec2::type> result(v0); + result += v1; + return result; +} + +/// Add @a scalar to each element of the given vector and return the result. +template +inline Vec2::type> operator+(const Vec2 &v, S scalar) +{ + Vec2::type> result(v); + result += scalar; + return result; +} + +/// Subtract corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec2::type> operator-(const Vec2 &v0, const Vec2 &v1) +{ + Vec2::type> result(v0); + result -= v1; + return result; +} + +/// Subtract @a scalar from each element of the given vector and return the result. +template +inline Vec2::type> operator-(const Vec2 &v, S scalar) +{ + Vec2::type> result(v); + result -= scalar; + return result; +} + +/// Angle between two vectors, the result is between [0, pi], +/// e.g. float a = Vec2f::angle(v1,v2); +template +inline T angle(const Vec2 &v1, const Vec2 &v2) +{ + T c = v1.dot(v2); + return acos(c); +} + +template +inline bool +isApproxEqual(const Vec2& a, const Vec2& b) +{ + return a.eq(b); +} +template +inline bool +isApproxEqual(const Vec2& a, const Vec2& b, const Vec2& eps) +{ + return isApproxEqual(a.x(), b.x(), eps.x()) && + isApproxEqual(a.y(), b.y(), eps.y()); +} + +template +inline Vec2 +Abs(const Vec2& v) +{ + return Vec2(Abs(v[0]), Abs(v[1])); +} + +/// Orthonormalize vectors v1 and v2 and store back the resulting basis +/// e.g. Vec2f::orthonormalize(v1,v2); +template +inline void orthonormalize(Vec2 &v1, Vec2 &v2) +{ + // If the input vectors are v0, v1, and v2, then the Gram-Schmidt + // orthonormalization produces vectors u0, u1, and u2 as follows, + // + // u0 = v0/|v0| + // u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0| + // + // where |A| indicates length of vector A and A*B indicates dot + // product of vectors A and B. + + // compute u0 + v1.normalize(); + + // compute u1 + T d0 = v1.dot(v2); + v2 -= v1*d0; + v2.normalize(); +} + + +/// \remark We are switching to a more explicit name because the semantics +/// are different from std::min/max. In that case, the function returns a +/// reference to one of the objects based on a comparator. Here, we must +/// fabricate a new object which might not match either of the inputs. + +/// Return component-wise minimum of the two vectors. +template +inline Vec2 minComponent(const Vec2 &v1, const Vec2 &v2) +{ + return Vec2( + std::min(v1.x(), v2.x()), + std::min(v1.y(), v2.y())); +} + +/// Return component-wise maximum of the two vectors. +template +inline Vec2 maxComponent(const Vec2 &v1, const Vec2 &v2) +{ + return Vec2( + std::max(v1.x(), v2.x()), + std::max(v1.y(), v2.y())); +} + +/// @brief Return a vector with the exponent applied to each of +/// the components of the input vector. +template +inline Vec2 Exp(Vec2 v) { return v.exp(); } + +/// @brief Return a vector with log applied to each of +/// the components of the input vector. +template +inline Vec2 Log(Vec2 v) { return v.log(); } + +using Vec2i = Vec2; +using Vec2ui = Vec2; +using Vec2s = Vec2; +using Vec2d = Vec2; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_VEC2_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Vec3.h b/openvdb/math/Vec3.h new file mode 100644 index 00000000..97aceaac --- /dev/null +++ b/openvdb/math/Vec3.h @@ -0,0 +1,668 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED + +#include +#include "Math.h" +#include "Tuple.h" +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Mat3; + +template +class Vec3: public Tuple<3, T> +{ +public: + using value_type = T; + using ValueType = T; + + /// Trivial constructor, the vector is NOT initialized + Vec3() {} + + /// @brief Construct a vector all of whose components have the given value. + explicit Vec3(T val) { this->mm[0] = this->mm[1] = this->mm[2] = val; } + + /// Constructor with three arguments, e.g. Vec3d v(1,2,3); + Vec3(T x, T y, T z) + { + this->mm[0] = x; + this->mm[1] = y; + this->mm[2] = z; + } + + /// Constructor with array argument, e.g. double a[3]; Vec3d v(a); + template + Vec3(Source *a) + { + this->mm[0] = static_cast(a[0]); + this->mm[1] = static_cast(a[1]); + this->mm[2] = static_cast(a[2]); + } + + /// @brief Construct a Vec3 from a 3-Tuple with a possibly different value type. + /// @details Type conversion warnings are suppressed. + template + explicit Vec3(const Tuple<3, Source> &v) + { + this->mm[0] = static_cast(v[0]); + this->mm[1] = static_cast(v[1]); + this->mm[2] = static_cast(v[2]); + } + + /// @brief Construct a vector all of whose components have the given value, + /// which may be of an arithmetic type different from this vector's value type. + /// @details Type conversion warnings are suppressed. + template + explicit Vec3(Other val, + typename std::enable_if::value, Conversion>::type = Conversion{}) + { + this->mm[0] = this->mm[1] = this->mm[2] = static_cast(val); + } + + /// @brief Construct a Vec3 from another Vec3 with a possibly different value type. + /// @details Type conversion warnings are suppressed. + template + Vec3(const Vec3& v) + { + this->mm[0] = static_cast(v[0]); + this->mm[1] = static_cast(v[1]); + this->mm[2] = static_cast(v[2]); + } + + /// Reference to the component, e.g. v.x() = 4.5f; + T& x() { return this->mm[0]; } + T& y() { return this->mm[1]; } + T& z() { return this->mm[2]; } + + /// Get the component, e.g. float f = v.y(); + T x() const { return this->mm[0]; } + T y() const { return this->mm[1]; } + T z() const { return this->mm[2]; } + + T* asPointer() { return this->mm; } + const T* asPointer() const { return this->mm; } + + /// Alternative indexed reference to the elements + T& operator()(int i) { return this->mm[i]; } + + /// Alternative indexed constant reference to the elements, + T operator()(int i) const { return this->mm[i]; } + + /// "this" vector gets initialized to [x, y, z], + /// calling v.init(); has same effect as calling v = Vec3::zero(); + const Vec3& init(T x=0, T y=0, T z=0) + { + this->mm[0] = x; this->mm[1] = y; this->mm[2] = z; + return *this; + } + + + /// Set "this" vector to zero + const Vec3& setZero() + { + this->mm[0] = 0; this->mm[1] = 0; this->mm[2] = 0; + return *this; + } + + /// @brief Assignment operator + /// @details Type conversion warnings are not suppressed. + template + const Vec3& operator=(const Vec3 &v) + { + // note: don't static_cast because that suppresses warnings + this->mm[0] = v[0]; + this->mm[1] = v[1]; + this->mm[2] = v[2]; + + return *this; + } + + /// Test if "this" vector is equivalent to vector v with tolerance of eps + bool eq(const Vec3 &v, T eps = static_cast(1.0e-7)) const + { + return isRelOrApproxEqual(this->mm[0], v.mm[0], eps, eps) && + isRelOrApproxEqual(this->mm[1], v.mm[1], eps, eps) && + isRelOrApproxEqual(this->mm[2], v.mm[2], eps, eps); + } + + + /// Negation operator, for e.g. v1 = -v2; + Vec3 operator-() const { return Vec3(-this->mm[0], -this->mm[1], -this->mm[2]); } + + /// this = v1 + v2 + /// "this", v1 and v2 need not be distinct objects, e.g. v.add(v1,v); + template + const Vec3& add(const Vec3 &v1, const Vec3 &v2) + { + this->mm[0] = v1[0] + v2[0]; + this->mm[1] = v1[1] + v2[1]; + this->mm[2] = v1[2] + v2[2]; + + return *this; + } + + /// this = v1 - v2 + /// "this", v1 and v2 need not be distinct objects, e.g. v.sub(v1,v); + template + const Vec3& sub(const Vec3 &v1, const Vec3 &v2) + { + this->mm[0] = v1[0] - v2[0]; + this->mm[1] = v1[1] - v2[1]; + this->mm[2] = v1[2] - v2[2]; + + return *this; + } + + /// this = scalar*v, v need not be a distinct object from "this", + /// e.g. v.scale(1.5,v1); + template + const Vec3& scale(T0 scale, const Vec3 &v) + { + this->mm[0] = scale * v[0]; + this->mm[1] = scale * v[1]; + this->mm[2] = scale * v[2]; + + return *this; + } + + template + const Vec3 &div(T0 scale, const Vec3 &v) + { + this->mm[0] = v[0] / scale; + this->mm[1] = v[1] / scale; + this->mm[2] = v[2] / scale; + + return *this; + } + + /// Dot product + T dot(const Vec3 &v) const + { + return + this->mm[0]*v.mm[0] + + this->mm[1]*v.mm[1] + + this->mm[2]*v.mm[2]; + } + + /// Length of the vector + T length() const + { + return static_cast(sqrt(double( + this->mm[0]*this->mm[0] + + this->mm[1]*this->mm[1] + + this->mm[2]*this->mm[2]))); + } + + + /// Squared length of the vector, much faster than length() as it + /// does not involve square root + T lengthSqr() const + { + return + this->mm[0]*this->mm[0] + + this->mm[1]*this->mm[1] + + this->mm[2]*this->mm[2]; + } + + /// Return the cross product of "this" vector and v; + Vec3 cross(const Vec3 &v) const + { + return Vec3(this->mm[1]*v.mm[2] - this->mm[2]*v.mm[1], + this->mm[2]*v.mm[0] - this->mm[0]*v.mm[2], + this->mm[0]*v.mm[1] - this->mm[1]*v.mm[0]); + } + + + /// this = v1 cross v2, v1 and v2 must be distinct objects than "this" + const Vec3& cross(const Vec3 &v1, const Vec3 &v2) + { + // assert(this!=&v1); + // assert(this!=&v2); + this->mm[0] = v1.mm[1]*v2.mm[2] - v1.mm[2]*v2.mm[1]; + this->mm[1] = v1.mm[2]*v2.mm[0] - v1.mm[0]*v2.mm[2]; + this->mm[2] = v1.mm[0]*v2.mm[1] - v1.mm[1]*v2.mm[0]; + return *this; + } + + /// Multiply each element of this vector by @a scalar. + template + const Vec3 &operator*=(S scalar) + { + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto value0 = this->mm[0] * scalar; + const auto value1 = this->mm[1] * scalar; + const auto value2 = this->mm[2] * scalar; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + this->mm[0] = static_cast(value0); + this->mm[1] = static_cast(value1); + this->mm[2] = static_cast(value2); + return *this; + } + + /// Multiply each element of this vector by the corresponding element of the given vector. + template + const Vec3 &operator*=(const Vec3 &v1) + { + this->mm[0] *= v1[0]; + this->mm[1] *= v1[1]; + this->mm[2] *= v1[2]; + return *this; + } + + /// Divide each element of this vector by @a scalar. + template + const Vec3 &operator/=(S scalar) + { + this->mm[0] /= scalar; + this->mm[1] /= scalar; + this->mm[2] /= scalar; + return *this; + } + + /// Divide each element of this vector by the corresponding element of the given vector. + template + const Vec3 &operator/=(const Vec3 &v1) + { + this->mm[0] /= v1[0]; + this->mm[1] /= v1[1]; + this->mm[2] /= v1[2]; + return *this; + } + + /// Add @a scalar to each element of this vector. + template + const Vec3 &operator+=(S scalar) + { + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto value0 = this->mm[0] + scalar; + const auto value1 = this->mm[1] + scalar; + const auto value2 = this->mm[2] + scalar; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + this->mm[0] = static_cast(value0); + this->mm[1] = static_cast(value1); + this->mm[2] = static_cast(value2); + return *this; + } + + /// Add each element of the given vector to the corresponding element of this vector. + template + const Vec3 &operator+=(const Vec3 &v1) + { + this->mm[0] += v1[0]; + this->mm[1] += v1[1]; + this->mm[2] += v1[2]; + return *this; + } + + /// Subtract @a scalar from each element of this vector. + template + const Vec3 &operator-=(S scalar) + { + this->mm[0] -= scalar; + this->mm[1] -= scalar; + this->mm[2] -= scalar; + return *this; + } + + /// Subtract each element of the given vector from the corresponding element of this vector. + template + const Vec3 &operator-=(const Vec3 &v1) + { + this->mm[0] -= v1[0]; + this->mm[1] -= v1[1]; + this->mm[2] -= v1[2]; + return *this; + } + + /// Return a reference to itself after the exponent has been + /// applied to all the vector components. + inline const Vec3& exp() + { + this->mm[0] = std::exp(this->mm[0]); + this->mm[1] = std::exp(this->mm[1]); + this->mm[2] = std::exp(this->mm[2]); + return *this; + } + + /// Return a reference to itself after log has been + /// applied to all the vector components. + inline const Vec3& log() + { + this->mm[0] = std::log(this->mm[0]); + this->mm[1] = std::log(this->mm[1]); + this->mm[2] = std::log(this->mm[2]); + return *this; + } + + /// Return the sum of all the vector components. + inline T sum() const + { + return this->mm[0] + this->mm[1] + this->mm[2]; + } + + /// Return the product of all the vector components. + inline T product() const + { + return this->mm[0] * this->mm[1] * this->mm[2]; + } + + /// this = normalized this + bool normalize(T eps = T(1.0e-7)) + { + T d = length(); + if (isApproxEqual(d, T(0), eps)) { + return false; + } + *this *= (T(1) / d); + return true; + } + + + /// return normalized this, throws if null vector + Vec3 unit(T eps=0) const + { + T d; + return unit(eps, d); + } + + /// return normalized this and length, throws if null vector + Vec3 unit(T eps, T& len) const + { + len = length(); + if (isApproxEqual(len, T(0), eps)) { + OPENVDB_THROW(ArithmeticError, "Normalizing null 3-vector"); + } + return *this / len; + } + + /// return normalized this, or (1, 0, 0) if this is null vector + Vec3 unitSafe() const + { + T l2 = lengthSqr(); + return l2 ? *this / static_cast(sqrt(l2)) : Vec3(1, 0 ,0); + } + + // Number of cols, rows, elements + static unsigned numRows() { return 1; } + static unsigned numColumns() { return 3; } + static unsigned numElements() { return 3; } + + /// Returns the scalar component of v in the direction of onto, onto need + /// not be unit. e.g double c = Vec3d::component(v1,v2); + T component(const Vec3 &onto, T eps = static_cast(1.0e-7)) const + { + T l = onto.length(); + if (isApproxEqual(l, T(0), eps)) return 0; + + return dot(onto)*(T(1)/l); + } + + /// Return the projection of v onto the vector, onto need not be unit + /// e.g. Vec3d a = vprojection(n); + Vec3 projection(const Vec3 &onto, T eps = static_cast(1.0e-7)) const + { + T l = onto.lengthSqr(); + if (isApproxEqual(l, T(0), eps)) return Vec3::zero(); + + return onto*(dot(onto)*(T(1)/l)); + } + + /// Return an arbitrary unit vector perpendicular to v + /// Vector this must be a unit vector + /// e.g. v = v.normalize(); Vec3d n = v.getArbPerpendicular(); + Vec3 getArbPerpendicular() const + { + Vec3 u; + T l; + + if ( fabs(this->mm[0]) >= fabs(this->mm[1]) ) { + // v.x or v.z is the largest magnitude component, swap them + l = this->mm[0]*this->mm[0] + this->mm[2]*this->mm[2]; + l = static_cast(T(1)/sqrt(double(l))); + u.mm[0] = -this->mm[2]*l; + u.mm[1] = T(0); + u.mm[2] = +this->mm[0]*l; + } else { + // W.y or W.z is the largest magnitude component, swap them + l = this->mm[1]*this->mm[1] + this->mm[2]*this->mm[2]; + l = static_cast(T(1)/sqrt(double(l))); + u.mm[0] = T(0); + u.mm[1] = +this->mm[2]*l; + u.mm[2] = -this->mm[1]*l; + } + + return u; + } + + /// Return a vector with the components of this in ascending order + Vec3 sorted() const + { + Vec3 r(*this); + if( r.mm[0] > r.mm[1] ) std::swap(r.mm[0], r.mm[1]); + if( r.mm[1] > r.mm[2] ) std::swap(r.mm[1], r.mm[2]); + if( r.mm[0] > r.mm[1] ) std::swap(r.mm[0], r.mm[1]); + return r; + } + + /// Return the vector (z, y, x) + Vec3 reversed() const + { + return Vec3(this->mm[2], this->mm[1], this->mm[0]); + } + + /// Predefined constants, e.g. Vec3d v = Vec3d::xNegAxis(); + static Vec3 zero() { return Vec3(0, 0, 0); } + static Vec3 ones() { return Vec3(1, 1, 1); } +}; + + +/// Equality operator, does exact floating point comparisons +template +inline bool operator==(const Vec3 &v0, const Vec3 &v1) +{ + return isExactlyEqual(v0[0], v1[0]) && isExactlyEqual(v0[1], v1[1]) + && isExactlyEqual(v0[2], v1[2]); +} + +/// Inequality operator, does exact floating point comparisons +template +inline bool operator!=(const Vec3 &v0, const Vec3 &v1) { return !(v0==v1); } + +/// Multiply each element of the given vector by @a scalar and return the result. +template +inline Vec3::type> operator*(S scalar, const Vec3 &v) { return v*scalar; } + +/// Multiply each element of the given vector by @a scalar and return the result. +template +inline Vec3::type> operator*(const Vec3 &v, S scalar) +{ + Vec3::type> result(v); + result *= scalar; + return result; +} + +/// Multiply corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec3::type> operator*(const Vec3 &v0, const Vec3 &v1) +{ + Vec3::type> result(v0[0] * v1[0], v0[1] * v1[1], v0[2] * v1[2]); + return result; +} + + +/// Divide @a scalar by each element of the given vector and return the result. +template +inline Vec3::type> operator/(S scalar, const Vec3 &v) +{ + return Vec3::type>(scalar/v[0], scalar/v[1], scalar/v[2]); +} + +/// Divide each element of the given vector by @a scalar and return the result. +template +inline Vec3::type> operator/(const Vec3 &v, S scalar) +{ + Vec3::type> result(v); + result /= scalar; + return result; +} + +/// Divide corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec3::type> operator/(const Vec3 &v0, const Vec3 &v1) +{ + Vec3::type> result(v0[0] / v1[0], v0[1] / v1[1], v0[2] / v1[2]); + return result; +} + +/// Add corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec3::type> operator+(const Vec3 &v0, const Vec3 &v1) +{ + Vec3::type> result(v0); + result += v1; + return result; +} + +/// Add @a scalar to each element of the given vector and return the result. +template +inline Vec3::type> operator+(const Vec3 &v, S scalar) +{ + Vec3::type> result(v); + result += scalar; + return result; +} + +/// Subtract corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec3::type> operator-(const Vec3 &v0, const Vec3 &v1) +{ + Vec3::type> result(v0); + result -= v1; + return result; +} + +/// Subtract @a scalar from each element of the given vector and return the result. +template +inline Vec3::type> operator-(const Vec3 &v, S scalar) +{ + Vec3::type> result(v); + result -= scalar; + return result; +} + +/// Angle between two vectors, the result is between [0, pi], +/// e.g. double a = Vec3d::angle(v1,v2); +template +inline T angle(const Vec3 &v1, const Vec3 &v2) +{ + Vec3 c = v1.cross(v2); + return static_cast(atan2(c.length(), v1.dot(v2))); +} + +template +inline bool +isApproxEqual(const Vec3& a, const Vec3& b) +{ + return a.eq(b); +} +template +inline bool +isApproxEqual(const Vec3& a, const Vec3& b, const Vec3& eps) +{ + return isApproxEqual(a.x(), b.x(), eps.x()) && + isApproxEqual(a.y(), b.y(), eps.y()) && + isApproxEqual(a.z(), b.z(), eps.z()); +} + +template +inline Vec3 +Abs(const Vec3& v) +{ + return Vec3(Abs(v[0]), Abs(v[1]), Abs(v[2])); +} + +/// Orthonormalize vectors v1, v2 and v3 and store back the resulting +/// basis e.g. Vec3d::orthonormalize(v1,v2,v3); +template +inline void orthonormalize(Vec3 &v1, Vec3 &v2, Vec3 &v3) +{ + // If the input vectors are v0, v1, and v2, then the Gram-Schmidt + // orthonormalization produces vectors u0, u1, and u2 as follows, + // + // u0 = v0/|v0| + // u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0| + // u2 = (v2-(u0*v2)u0-(u1*v2)u1)/|v2-(u0*v2)u0-(u1*v2)u1| + // + // where |A| indicates length of vector A and A*B indicates dot + // product of vectors A and B. + + // compute u0 + v1.normalize(); + + // compute u1 + T d0 = v1.dot(v2); + v2 -= v1*d0; + v2.normalize(); + + // compute u2 + T d1 = v2.dot(v3); + d0 = v1.dot(v3); + v3 -= v1*d0 + v2*d1; + v3.normalize(); +} + +/// @remark We are switching to a more explicit name because the semantics +/// are different from std::min/max. In that case, the function returns a +/// reference to one of the objects based on a comparator. Here, we must +/// fabricate a new object which might not match either of the inputs. + +/// Return component-wise minimum of the two vectors. +template +inline Vec3 minComponent(const Vec3 &v1, const Vec3 &v2) +{ + return Vec3( + std::min(v1.x(), v2.x()), + std::min(v1.y(), v2.y()), + std::min(v1.z(), v2.z())); +} + +/// Return component-wise maximum of the two vectors. +template +inline Vec3 maxComponent(const Vec3 &v1, const Vec3 &v2) +{ + return Vec3( + std::max(v1.x(), v2.x()), + std::max(v1.y(), v2.y()), + std::max(v1.z(), v2.z())); +} + +/// @brief Return a vector with the exponent applied to each of +/// the components of the input vector. +template +inline Vec3 Exp(Vec3 v) { return v.exp(); } + +/// @brief Return a vector with log applied to each of +/// the components of the input vector. +template +inline Vec3 Log(Vec3 v) { return v.log(); } + +using Vec3i = Vec3; +using Vec3ui = Vec3; +using Vec3s = Vec3; +using Vec3d = Vec3; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED diff --git a/openvdb/math/Vec4.h b/openvdb/math/Vec4.h new file mode 100644 index 00000000..25ea0eb8 --- /dev/null +++ b/openvdb/math/Vec4.h @@ -0,0 +1,566 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_MATH_VEC4_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_VEC4_HAS_BEEN_INCLUDED + +#include +#include "Math.h" +#include "Tuple.h" +#include "Vec3.h" +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Mat3; + +template +class Vec4: public Tuple<4, T> +{ +public: + using value_type = T; + using ValueType = T; + + /// Trivial constructor, the vector is NOT initialized + Vec4() {} + + /// @brief Construct a vector all of whose components have the given value. + explicit Vec4(T val) { this->mm[0] = this->mm[1] = this->mm[2] = this->mm[3] = val; } + + /// Constructor with four arguments, e.g. Vec4f v(1,2,3,4); + Vec4(T x, T y, T z, T w) + { + this->mm[0] = x; + this->mm[1] = y; + this->mm[2] = z; + this->mm[3] = w; + } + + /// Constructor with array argument, e.g. float a[4]; Vec4f v(a); + template + Vec4(Source *a) + { + this->mm[0] = static_cast(a[0]); + this->mm[1] = static_cast(a[1]); + this->mm[2] = static_cast(a[2]); + this->mm[3] = static_cast(a[3]); + } + + /// Conversion constructor + template + explicit Vec4(const Tuple<4, Source> &v) + { + this->mm[0] = static_cast(v[0]); + this->mm[1] = static_cast(v[1]); + this->mm[2] = static_cast(v[2]); + this->mm[3] = static_cast(v[3]); + } + + /// @brief Construct a vector all of whose components have the given value, + /// which may be of an arithmetic type different from this vector's value type. + /// @details Type conversion warnings are suppressed. + template + explicit Vec4(Other val, + typename std::enable_if::value, Conversion>::type = Conversion{}) + { + this->mm[0] = this->mm[1] = this->mm[2] = this->mm[3] = static_cast(val); + } + + /// Reference to the component, e.g. v.x() = 4.5f; + T& x() { return this->mm[0]; } + T& y() { return this->mm[1]; } + T& z() { return this->mm[2]; } + T& w() { return this->mm[3]; } + + /// Get the component, e.g. float f = v.y(); + T x() const { return this->mm[0]; } + T y() const { return this->mm[1]; } + T z() const { return this->mm[2]; } + T w() const { return this->mm[3]; } + + T* asPointer() { return this->mm; } + const T* asPointer() const { return this->mm; } + + /// Alternative indexed reference to the elements + T& operator()(int i) { return this->mm[i]; } + + /// Alternative indexed constant reference to the elements, + T operator()(int i) const { return this->mm[i]; } + + /// Returns a Vec3 with the first three elements of the Vec4. + Vec3 getVec3() const { return Vec3(this->mm[0], this->mm[1], this->mm[2]); } + + /// "this" vector gets initialized to [x, y, z, w], + /// calling v.init(); has same effect as calling v = Vec4::zero(); + const Vec4& init(T x=0, T y=0, T z=0, T w=0) + { + this->mm[0] = x; this->mm[1] = y; this->mm[2] = z; this->mm[3] = w; + return *this; + } + + /// Set "this" vector to zero + const Vec4& setZero() + { + this->mm[0] = 0; this->mm[1] = 0; this->mm[2] = 0; this->mm[3] = 0; + return *this; + } + + /// Assignment operator + template + const Vec4& operator=(const Vec4 &v) + { + // note: don't static_cast because that suppresses warnings + this->mm[0] = v[0]; + this->mm[1] = v[1]; + this->mm[2] = v[2]; + this->mm[3] = v[3]; + + return *this; + } + + /// Test if "this" vector is equivalent to vector v with tolerance + /// of eps + bool eq(const Vec4 &v, T eps = static_cast(1.0e-8)) const + { + return isApproxEqual(this->mm[0], v.mm[0], eps) && + isApproxEqual(this->mm[1], v.mm[1], eps) && + isApproxEqual(this->mm[2], v.mm[2], eps) && + isApproxEqual(this->mm[3], v.mm[3], eps); + } + + /// Negation operator, for e.g. v1 = -v2; + Vec4 operator-() const + { + return Vec4( + -this->mm[0], + -this->mm[1], + -this->mm[2], + -this->mm[3]); + } + + /// this = v1 + v2 + /// "this", v1 and v2 need not be distinct objects, e.g. v.add(v1,v); + template + const Vec4& add(const Vec4 &v1, const Vec4 &v2) + { + this->mm[0] = v1[0] + v2[0]; + this->mm[1] = v1[1] + v2[1]; + this->mm[2] = v1[2] + v2[2]; + this->mm[3] = v1[3] + v2[3]; + + return *this; + } + + + /// this = v1 - v2 + /// "this", v1 and v2 need not be distinct objects, e.g. v.sub(v1,v); + template + const Vec4& sub(const Vec4 &v1, const Vec4 &v2) + { + this->mm[0] = v1[0] - v2[0]; + this->mm[1] = v1[1] - v2[1]; + this->mm[2] = v1[2] - v2[2]; + this->mm[3] = v1[3] - v2[3]; + + return *this; + } + + /// this = scalar*v, v need not be a distinct object from "this", + /// e.g. v.scale(1.5,v1); + template + const Vec4& scale(T0 scale, const Vec4 &v) + { + this->mm[0] = scale * v[0]; + this->mm[1] = scale * v[1]; + this->mm[2] = scale * v[2]; + this->mm[3] = scale * v[3]; + + return *this; + } + + template + const Vec4 &div(T0 scalar, const Vec4 &v) + { + this->mm[0] = v[0] / scalar; + this->mm[1] = v[1] / scalar; + this->mm[2] = v[2] / scalar; + this->mm[3] = v[3] / scalar; + + return *this; + } + + /// Dot product + T dot(const Vec4 &v) const + { + return (this->mm[0]*v.mm[0] + this->mm[1]*v.mm[1] + + this->mm[2]*v.mm[2] + this->mm[3]*v.mm[3]); + } + + /// Length of the vector + T length() const + { + return sqrt( + this->mm[0]*this->mm[0] + + this->mm[1]*this->mm[1] + + this->mm[2]*this->mm[2] + + this->mm[3]*this->mm[3]); + } + + + /// Squared length of the vector, much faster than length() as it + /// does not involve square root + T lengthSqr() const + { + return (this->mm[0]*this->mm[0] + this->mm[1]*this->mm[1] + + this->mm[2]*this->mm[2] + this->mm[3]*this->mm[3]); + } + + /// Return a reference to itself after the exponent has been + /// applied to all the vector components. + inline const Vec4& exp() + { + this->mm[0] = std::exp(this->mm[0]); + this->mm[1] = std::exp(this->mm[1]); + this->mm[2] = std::exp(this->mm[2]); + this->mm[3] = std::exp(this->mm[3]); + return *this; + } + + /// Return a reference to itself after log has been + /// applied to all the vector components. + inline const Vec4& log() + { + this->mm[0] = std::log(this->mm[0]); + this->mm[1] = std::log(this->mm[1]); + this->mm[2] = std::log(this->mm[2]); + this->mm[3] = std::log(this->mm[3]); + return *this; + } + + /// Return the sum of all the vector components. + inline T sum() const + { + return this->mm[0] + this->mm[1] + this->mm[2] + this->mm[3]; + } + + /// Return the product of all the vector components. + inline T product() const + { + return this->mm[0] * this->mm[1] * this->mm[2] * this->mm[3]; + } + + /// this = normalized this + bool normalize(T eps = static_cast(1.0e-8)) + { + T d = length(); + if (isApproxEqual(d, T(0), eps)) { + return false; + } + *this *= (T(1) / d); + return true; + } + + /// return normalized this, throws if null vector + Vec4 unit(T eps=0) const + { + T d; + return unit(eps, d); + } + + /// return normalized this and length, throws if null vector + Vec4 unit(T eps, T& len) const + { + len = length(); + if (isApproxEqual(len, T(0), eps)) { + throw ArithmeticError("Normalizing null 4-vector"); + } + return *this / len; + } + + /// return normalized this, or (1, 0, 0, 0) if this is null vector + Vec4 unitSafe() const + { + T l2 = lengthSqr(); + return l2 ? *this / static_cast(sqrt(l2)) : Vec4(1, 0, 0, 0); + } + + /// Multiply each element of this vector by @a scalar. + template + const Vec4 &operator*=(S scalar) + { + this->mm[0] *= scalar; + this->mm[1] *= scalar; + this->mm[2] *= scalar; + this->mm[3] *= scalar; + return *this; + } + + /// Multiply each element of this vector by the corresponding element of the given vector. + template + const Vec4 &operator*=(const Vec4 &v1) + { + this->mm[0] *= v1[0]; + this->mm[1] *= v1[1]; + this->mm[2] *= v1[2]; + this->mm[3] *= v1[3]; + + return *this; + } + + /// Divide each element of this vector by @a scalar. + template + const Vec4 &operator/=(S scalar) + { + this->mm[0] /= scalar; + this->mm[1] /= scalar; + this->mm[2] /= scalar; + this->mm[3] /= scalar; + return *this; + } + + /// Divide each element of this vector by the corresponding element of the given vector. + template + const Vec4 &operator/=(const Vec4 &v1) + { + this->mm[0] /= v1[0]; + this->mm[1] /= v1[1]; + this->mm[2] /= v1[2]; + this->mm[3] /= v1[3]; + return *this; + } + + /// Add @a scalar to each element of this vector. + template + const Vec4 &operator+=(S scalar) + { + this->mm[0] += scalar; + this->mm[1] += scalar; + this->mm[2] += scalar; + this->mm[3] += scalar; + return *this; + } + + /// Add each element of the given vector to the corresponding element of this vector. + template + const Vec4 &operator+=(const Vec4 &v1) + { + this->mm[0] += v1[0]; + this->mm[1] += v1[1]; + this->mm[2] += v1[2]; + this->mm[3] += v1[3]; + return *this; + } + + /// Subtract @a scalar from each element of this vector. + template + const Vec4 &operator-=(S scalar) + { + this->mm[0] -= scalar; + this->mm[1] -= scalar; + this->mm[2] -= scalar; + this->mm[3] -= scalar; + return *this; + } + + /// Subtract each element of the given vector from the corresponding element of this vector. + template + const Vec4 &operator-=(const Vec4 &v1) + { + this->mm[0] -= v1[0]; + this->mm[1] -= v1[1]; + this->mm[2] -= v1[2]; + this->mm[3] -= v1[3]; + return *this; + } + + // Number of cols, rows, elements + static unsigned numRows() { return 1; } + static unsigned numColumns() { return 4; } + static unsigned numElements() { return 4; } + + /// Predefined constants, e.g. Vec4f v = Vec4f::xNegAxis(); + static Vec4 zero() { return Vec4(0, 0, 0, 0); } + static Vec4 origin() { return Vec4(0, 0, 0, 1); } + static Vec4 ones() { return Vec4(1, 1, 1, 1); } +}; + +/// Equality operator, does exact floating point comparisons +template +inline bool operator==(const Vec4 &v0, const Vec4 &v1) +{ + return + isExactlyEqual(v0[0], v1[0]) && + isExactlyEqual(v0[1], v1[1]) && + isExactlyEqual(v0[2], v1[2]) && + isExactlyEqual(v0[3], v1[3]); +} + +/// Inequality operator, does exact floating point comparisons +template +inline bool operator!=(const Vec4 &v0, const Vec4 &v1) { return !(v0==v1); } + +/// Multiply each element of the given vector by @a scalar and return the result. +template +inline Vec4::type> operator*(S scalar, const Vec4 &v) +{ return v*scalar; } + +/// Multiply each element of the given vector by @a scalar and return the result. +template +inline Vec4::type> operator*(const Vec4 &v, S scalar) +{ + Vec4::type> result(v); + result *= scalar; + return result; +} + +/// Multiply corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec4::type> operator*(const Vec4 &v0, const Vec4 &v1) +{ + Vec4::type> result(v0[0]*v1[0], + v0[1]*v1[1], + v0[2]*v1[2], + v0[3]*v1[3]); + return result; +} + +/// Divide @a scalar by each element of the given vector and return the result. +template +inline Vec4::type> operator/(S scalar, const Vec4 &v) +{ + return Vec4::type>(scalar/v[0], + scalar/v[1], + scalar/v[2], + scalar/v[3]); +} + +/// Divide each element of the given vector by @a scalar and return the result. +template +inline Vec4::type> operator/(const Vec4 &v, S scalar) +{ + Vec4::type> result(v); + result /= scalar; + return result; +} + +/// Divide corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec4::type> operator/(const Vec4 &v0, const Vec4 &v1) +{ + Vec4::type> + result(v0[0]/v1[0], v0[1]/v1[1], v0[2]/v1[2], v0[3]/v1[3]); + return result; +} + +/// Add corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec4::type> operator+(const Vec4 &v0, const Vec4 &v1) +{ + Vec4::type> result(v0); + result += v1; + return result; +} + +/// Add @a scalar to each element of the given vector and return the result. +template +inline Vec4::type> operator+(const Vec4 &v, S scalar) +{ + Vec4::type> result(v); + result += scalar; + return result; +} + +/// Subtract corresponding elements of @a v0 and @a v1 and return the result. +template +inline Vec4::type> operator-(const Vec4 &v0, const Vec4 &v1) +{ + Vec4::type> result(v0); + result -= v1; + return result; +} + +/// Subtract @a scalar from each element of the given vector and return the result. +template +inline Vec4::type> operator-(const Vec4 &v, S scalar) +{ + Vec4::type> result(v); + result -= scalar; + return result; +} + +template +inline bool +isApproxEqual(const Vec4& a, const Vec4& b) +{ + return a.eq(b); +} +template +inline bool +isApproxEqual(const Vec4& a, const Vec4& b, const Vec4& eps) +{ + return isApproxEqual(a[0], b[0], eps[0]) && + isApproxEqual(a[1], b[1], eps[1]) && + isApproxEqual(a[2], b[2], eps[2]) && + isApproxEqual(a[3], b[3], eps[3]); +} + +template +inline Vec4 +Abs(const Vec4& v) +{ + return Vec4(Abs(v[0]), Abs(v[1]), Abs(v[2]), Abs(v[3])); +} + +/// @remark We are switching to a more explicit name because the semantics +/// are different from std::min/max. In that case, the function returns a +/// reference to one of the objects based on a comparator. Here, we must +/// fabricate a new object which might not match either of the inputs. + +/// Return component-wise minimum of the two vectors. +template +inline Vec4 minComponent(const Vec4 &v1, const Vec4 &v2) +{ + return Vec4( + std::min(v1.x(), v2.x()), + std::min(v1.y(), v2.y()), + std::min(v1.z(), v2.z()), + std::min(v1.w(), v2.w())); +} + +/// Return component-wise maximum of the two vectors. +template +inline Vec4 maxComponent(const Vec4 &v1, const Vec4 &v2) +{ + return Vec4( + std::max(v1.x(), v2.x()), + std::max(v1.y(), v2.y()), + std::max(v1.z(), v2.z()), + std::max(v1.w(), v2.w())); +} + +/// @brief Return a vector with the exponent applied to each of +/// the components of the input vector. +template +inline Vec4 Exp(Vec4 v) { return v.exp(); } + +/// @brief Return a vector with log applied to each of +/// the components of the input vector. +template +inline Vec4 Log(Vec4 v) { return v.log(); } + +using Vec4i = Vec4; +using Vec4ui = Vec4; +using Vec4s = Vec4; +using Vec4d = Vec4; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_VEC4_HAS_BEEN_INCLUDED diff --git a/openvdb/openvdb.cc b/openvdb/openvdb.cc new file mode 100644 index 00000000..79bcdb51 --- /dev/null +++ b/openvdb/openvdb.cc @@ -0,0 +1,161 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "openvdb.h" +#include "io/DelayedLoadMetadata.h" +//#ifdef OPENVDB_ENABLE_POINTS +#include "points/PointDataGrid.h" +//#endif +#include "tools/PointIndexGrid.h" +#include "util/logging.h" +#include +#ifdef OPENVDB_USE_BLOSC +#include +#endif + +// If using an OPENVDB_ABI_VERSION_NUMBER that has been deprecated, issue an error +// directive. This can be optionally suppressed by defining OPENVDB_USE_DEPRECATED_ABI. +// Do this in the .cc of openvdb.cc to ensure this decision is made at the time of +// building the core library +#ifndef OPENVDB_USE_DEPRECATED_ABI + #if OPENVDB_ABI_VERSION_NUMBER == 4 + #error ABI = 4 is deprecated, define OPENVDB_USE_DEPRECATED_ABI to suppress this error + #elif OPENVDB_ABI_VERSION_NUMBER < 4 + #error ABI <= 3 is no longer supported + #endif +#endif + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +typedef tbb::mutex Mutex; +typedef Mutex::scoped_lock Lock; + +namespace { +// Declare this at file scope to ensure thread-safe initialization. +Mutex sInitMutex; +bool sIsInitialized = false; +} + +void +initialize() +{ + Lock lock(sInitMutex); + if (sIsInitialized) return; + + logging::initialize(); + + // Register metadata. + Metadata::clearRegistry(); + BoolMetadata::registerType(); + DoubleMetadata::registerType(); + FloatMetadata::registerType(); + Int32Metadata::registerType(); + Int64Metadata::registerType(); + StringMetadata::registerType(); + Vec2IMetadata::registerType(); + Vec2SMetadata::registerType(); + Vec2DMetadata::registerType(); + Vec3IMetadata::registerType(); + Vec3SMetadata::registerType(); + Vec3DMetadata::registerType(); + Vec4IMetadata::registerType(); + Vec4SMetadata::registerType(); + Vec4DMetadata::registerType(); + Mat4SMetadata::registerType(); + Mat4DMetadata::registerType(); + + // Register maps + math::MapRegistry::clear(); + math::AffineMap::registerMap(); + math::UnitaryMap::registerMap(); + math::ScaleMap::registerMap(); + math::UniformScaleMap::registerMap(); + math::TranslationMap::registerMap(); + math::ScaleTranslateMap::registerMap(); + math::UniformScaleTranslateMap::registerMap(); + math::NonlinearFrustumMap::registerMap(); + + // Register common grid types. + GridBase::clearRegistry(); + BoolGrid::registerGrid(); + MaskGrid::registerGrid(); + FloatGrid::registerGrid(); + DoubleGrid::registerGrid(); + Int32Grid::registerGrid(); + Int64Grid::registerGrid(); + StringGrid::registerGrid(); + Vec3IGrid::registerGrid(); + Vec3SGrid::registerGrid(); + Vec3DGrid::registerGrid(); + + // Register types associated with point index grids. + Metadata::registerType(typeNameAsString(), Int32Metadata::createMetadata); + Metadata::registerType(typeNameAsString(), Int64Metadata::createMetadata); + tools::PointIndexGrid::registerGrid(); + + // Register types associated with point data grids. +//#ifdef OPENVDB_ENABLE_POINTS + points::internal::initialize(); +//#endif + + // Register delay load metadata + io::DelayedLoadMetadata::registerType(); + +#ifdef OPENVDB_USE_BLOSC + blosc_init(); + if (blosc_set_compressor("lz4") < 0) { + OPENVDB_LOG_WARN("Blosc LZ4 compressor is unavailable"); + } + /// @todo blosc_set_nthreads(int nthreads); +#endif + +#ifdef __ICC +// Disable ICC "assignment to statically allocated variable" warning. +// This assignment is mutex-protected and therefore thread-safe. +__pragma(warning(disable:1711)) +#endif + + sIsInitialized = true; + +#ifdef __ICC +__pragma(warning(default:1711)) +#endif +} + + +void +uninitialize() +{ + Lock lock(sInitMutex); + +#ifdef __ICC +// Disable ICC "assignment to statically allocated variable" warning. +// This assignment is mutex-protected and therefore thread-safe. +__pragma(warning(disable:1711)) +#endif + + sIsInitialized = false; + +#ifdef __ICC +__pragma(warning(default:1711)) +#endif + + Metadata::clearRegistry(); + GridBase::clearRegistry(); + math::MapRegistry::clear(); + +//#ifdef OPENVDB_ENABLE_POINTS + points::internal::uninitialize(); +//#endif + +#ifdef OPENVDB_USE_BLOSC + // We don't want to destroy Blosc, because it might have been + // initialized by some other library. + //blosc_destroy(); +#endif +} + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/openvdb.h b/openvdb/openvdb.h new file mode 100644 index 00000000..dd543180 --- /dev/null +++ b/openvdb/openvdb.h @@ -0,0 +1,68 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_OPENVDB_HAS_BEEN_INCLUDED +#define OPENVDB_OPENVDB_HAS_BEEN_INCLUDED + +#include "Platform.h" +#include "Types.h" +#include "Metadata.h" +#include "math/Maps.h" +#include "math/Transform.h" +#include "Grid.h" +#include "tree/Tree.h" +#include "io/File.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +/// Common tree types +using BoolTree = tree::Tree4::Type; +using DoubleTree = tree::Tree4::Type; +using FloatTree = tree::Tree4::Type; +using Int32Tree = tree::Tree4::Type; +using Int64Tree = tree::Tree4::Type; +using MaskTree = tree::Tree4::Type; +using StringTree = tree::Tree4::Type; +using UInt32Tree = tree::Tree4::Type; +using Vec2DTree = tree::Tree4::Type; +using Vec2ITree = tree::Tree4::Type; +using Vec2STree = tree::Tree4::Type; +using Vec3DTree = tree::Tree4::Type; +using Vec3ITree = tree::Tree4::Type; +using Vec3STree = tree::Tree4::Type; +using ScalarTree = FloatTree; +using TopologyTree = MaskTree; +using Vec3dTree = Vec3DTree; +using Vec3fTree = Vec3STree; +using VectorTree = Vec3fTree; + +/// Common grid types +using BoolGrid = Grid; +using DoubleGrid = Grid; +using FloatGrid = Grid; +using Int32Grid = Grid; +using Int64Grid = Grid; +using MaskGrid = Grid; +using StringGrid = Grid; +using Vec3DGrid = Grid; +using Vec3IGrid = Grid; +using Vec3SGrid = Grid; +using ScalarGrid = FloatGrid; +using TopologyGrid = MaskGrid; +using Vec3dGrid = Vec3DGrid; +using Vec3fGrid = Vec3SGrid; +using VectorGrid = Vec3fGrid; + +/// Global registration of basic types +OPENVDB_API void initialize(); + +/// Global deregistration of basic types +OPENVDB_API void uninitialize(); + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_OPENVDB_HAS_BEEN_INCLUDED diff --git a/openvdb/points/AttributeArray.cc b/openvdb/points/AttributeArray.cc new file mode 100644 index 00000000..972be014 --- /dev/null +++ b/openvdb/points/AttributeArray.cc @@ -0,0 +1,210 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/AttributeArray.cc + +#include "AttributeArray.h" +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +//////////////////////////////////////// + + +namespace { + +using AttributeFactoryMap = std::map; + +struct LockedAttributeRegistry +{ + tbb::spin_mutex mMutex; + AttributeFactoryMap mMap; +}; + +// Global function for accessing the registry +LockedAttributeRegistry* +getAttributeRegistry() +{ + static LockedAttributeRegistry registry; + return ®istry; +} + +} // unnamed namespace + + +//////////////////////////////////////// + +// AttributeArray::ScopedRegistryLock implementation + +AttributeArray::ScopedRegistryLock::ScopedRegistryLock() + : lock(getAttributeRegistry()->mMutex) +{ +} + + +//////////////////////////////////////// + +// AttributeArray implementation + + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 +AttributeArray::AttributeArray(const AttributeArray& rhs) + : AttributeArray(rhs, tbb::spin_mutex::scoped_lock(rhs.mMutex)) +{ +} + + +AttributeArray::AttributeArray(const AttributeArray& rhs, const tbb::spin_mutex::scoped_lock&) +#else +AttributeArray::AttributeArray(const AttributeArray& rhs) +#endif + : mIsUniform(rhs.mIsUniform) + , mFlags(rhs.mFlags) + , mUsePagedRead(rhs.mUsePagedRead) + , mOutOfCore(rhs.mOutOfCore) + , mPageHandle() +{ + if (mFlags & PARTIALREAD) mCompressedBytes = rhs.mCompressedBytes; + else if (rhs.mPageHandle) mPageHandle = rhs.mPageHandle->copy(); +} + + +AttributeArray& +AttributeArray::operator=(const AttributeArray& rhs) +{ + // if this AttributeArray has been partially read, zero the compressed bytes, + // so the page handle won't attempt to clean up invalid memory + if (mFlags & PARTIALREAD) mCompressedBytes = 0; + mIsUniform = rhs.mIsUniform; + mFlags = rhs.mFlags; + mUsePagedRead = rhs.mUsePagedRead; + mOutOfCore = rhs.mOutOfCore; + if (mFlags & PARTIALREAD) mCompressedBytes = rhs.mCompressedBytes; + else if (rhs.mPageHandle) mPageHandle = rhs.mPageHandle->copy(); + else mPageHandle.reset(); + return *this; +} +#endif + + +AttributeArray::Ptr +AttributeArray::create(const NamePair& type, Index length, Index stride, + bool constantStride, const ScopedRegistryLock* lock) +{ + auto* registry = getAttributeRegistry(); + tbb::spin_mutex::scoped_lock _lock; + if (!lock) _lock.acquire(registry->mMutex); + + auto iter = registry->mMap.find(type); + if (iter == registry->mMap.end()) { + OPENVDB_THROW(LookupError, + "Cannot create attribute of unregistered type " << type.first << "_" << type.second); + } + return (iter->second)(length, stride, constantStride); +} + + +bool +AttributeArray::isRegistered(const NamePair& type, const ScopedRegistryLock* lock) +{ + LockedAttributeRegistry* registry = getAttributeRegistry(); + tbb::spin_mutex::scoped_lock _lock; + if (!lock) _lock.acquire(registry->mMutex); + return (registry->mMap.find(type) != registry->mMap.end()); +} + + +void +AttributeArray::clearRegistry(const ScopedRegistryLock* lock) +{ + LockedAttributeRegistry* registry = getAttributeRegistry(); + tbb::spin_mutex::scoped_lock _lock; + if (!lock) _lock.acquire(registry->mMutex); + registry->mMap.clear(); +} + + +void +AttributeArray::registerType(const NamePair& type, FactoryMethod factory, const ScopedRegistryLock* lock) +{ + { // check the type of the AttributeArray generated by the factory method + auto array = (*factory)(/*length=*/0, /*stride=*/0, /*constantStride=*/false); + const NamePair& factoryType = array->type(); + if (factoryType != type) { + OPENVDB_THROW(KeyError, "Attribute type " << type.first << "_" << type.second + << " does not match the type created by the factory method " + << factoryType.first << "_" << factoryType.second << "."); + } + } + + LockedAttributeRegistry* registry = getAttributeRegistry(); + tbb::spin_mutex::scoped_lock _lock; + if (!lock) _lock.acquire(registry->mMutex); + + registry->mMap[type] = factory; +} + + +void +AttributeArray::unregisterType(const NamePair& type, const ScopedRegistryLock* lock) +{ + LockedAttributeRegistry* registry = getAttributeRegistry(); + tbb::spin_mutex::scoped_lock _lock; + if (!lock) _lock.acquire(registry->mMutex); + + registry->mMap.erase(type); +} + + +void +AttributeArray::setTransient(bool state) +{ + if (state) mFlags = static_cast(mFlags | Int16(TRANSIENT)); + else mFlags = static_cast(mFlags & ~Int16(TRANSIENT)); +} + + +void +AttributeArray::setHidden(bool state) +{ + if (state) mFlags = static_cast(mFlags | Int16(HIDDEN)); + else mFlags = static_cast(mFlags & ~Int16(HIDDEN)); +} + + +void +AttributeArray::setStreaming(bool state) +{ + if (state) mFlags = static_cast(mFlags | Int16(STREAMING)); + else mFlags = static_cast(mFlags & ~Int16(STREAMING)); +} + + +void +AttributeArray::setConstantStride(bool state) +{ + if (state) mFlags = static_cast(mFlags | Int16(CONSTANTSTRIDE)); + else mFlags = static_cast(mFlags & ~Int16(CONSTANTSTRIDE)); +} + + +bool +AttributeArray::operator==(const AttributeArray& other) const +{ + this->loadData(); + other.loadData(); + + if (this->mUsePagedRead != other.mUsePagedRead || + this->mFlags != other.mFlags) return false; + return this->isEqual(other); +} + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/points/AttributeArray.h b/openvdb/points/AttributeArray.h new file mode 100644 index 00000000..b1ac971a --- /dev/null +++ b/openvdb/points/AttributeArray.h @@ -0,0 +1,2339 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/AttributeArray.h +/// +/// @authors Dan Bailey, Mihai Alden, Nick Avramoussis, James Bird, Khang Ngo +/// +/// @brief Attribute Array storage templated on type and compression codec. + +#ifndef OPENVDB_POINTS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include // MappedFile +#include // COMPRESS_BLOSC + +#include "IndexIterator.h" +#include "StreamCompression.h" + +#include +#include + +#include +#include +#include +#include + + +class TestAttributeArray; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + + +using NamePair = std::pair; + +namespace points { + + +//////////////////////////////////////// + +// Utility methods + +template +inline IntegerT +floatingPointToFixedPoint(const FloatT s) +{ + static_assert(std::is_unsigned::value, "IntegerT must be unsigned"); + if (FloatT(0.0) > s) return std::numeric_limits::min(); + else if (FloatT(1.0) <= s) return std::numeric_limits::max(); + return IntegerT(std::floor(s * FloatT(std::numeric_limits::max()))); +} + + +template +inline FloatT +fixedPointToFloatingPoint(const IntegerT s) +{ + static_assert(std::is_unsigned::value, "IntegerT must be unsigned"); + return FloatT(s) / FloatT((std::numeric_limits::max())); +} + +template +inline IntegerVectorT +floatingPointToFixedPoint(const math::Vec3& v) +{ + return IntegerVectorT( + floatingPointToFixedPoint(v.x()), + floatingPointToFixedPoint(v.y()), + floatingPointToFixedPoint(v.z())); +} + +template +inline FloatVectorT +fixedPointToFloatingPoint(const math::Vec3& v) +{ + return FloatVectorT( + fixedPointToFloatingPoint(v.x()), + fixedPointToFloatingPoint(v.y()), + fixedPointToFloatingPoint(v.z())); +} + + +//////////////////////////////////////// + + +/// Base class for storing attribute data +class OPENVDB_API AttributeArray +{ +protected: + struct AccessorBase; + template struct Accessor; + + using AccessorBasePtr = std::shared_ptr; + +public: + enum Flag { + TRANSIENT = 0x1, /// by default not written to disk + HIDDEN = 0x2, /// hidden from UIs or iterators + OUTOFCORE = 0x4, /// data not yet loaded from disk (deprecated flag as of ABI=5) + CONSTANTSTRIDE = 0x8, /// stride size does not vary in the array + STREAMING = 0x10, /// streaming mode collapses attributes when first accessed + PARTIALREAD = 0x20 /// data has been partially read (compressed bytes is used) + }; + + enum SerializationFlag { + WRITESTRIDED = 0x1, /// data is marked as strided when written + WRITEUNIFORM = 0x2, /// data is marked as uniform when written + WRITEMEMCOMPRESS = 0x4, /// data is marked as compressed in-memory when written + /// (deprecated flag as of ABI=6) + WRITEPAGED = 0x8 /// data is written out in pages + }; + + // Scoped Lock wrapper class that locks the AttributeArray registry mutex + class OPENVDB_API ScopedRegistryLock + { + tbb::spin_mutex::scoped_lock lock; + public: + ScopedRegistryLock(); + }; // class ScopedRegistryLock + + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + using FactoryMethod = Ptr (*)(Index, Index, bool); + + template friend class AttributeHandle; + +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + AttributeArray(): mPageHandle() { mOutOfCore = 0; } +#else + AttributeArray(): mPageHandle() {} +#endif + virtual ~AttributeArray() + { + // if this AttributeArray has been partially read, zero the compressed bytes, + // so the page handle won't attempt to clean up invalid memory + if (mFlags & PARTIALREAD) mCompressedBytes = 0; + } +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + AttributeArray(const AttributeArray& rhs); + AttributeArray& operator=(const AttributeArray& rhs); +#else + AttributeArray(const AttributeArray&) = default; + AttributeArray& operator=(const AttributeArray&) = default; +#endif + AttributeArray(AttributeArray&&) = default; + AttributeArray& operator=(AttributeArray&&) = default; + + /// Return a copy of this attribute. + virtual AttributeArray::Ptr copy() const = 0; + + /// Return a copy of this attribute. + /// @deprecated In-memory compression no longer supported, use AttributeArray::copy() instead. +#ifndef _MSC_VER + OPENVDB_DEPRECATED +#endif + virtual AttributeArray::Ptr copyUncompressed() const = 0; + + /// Return the number of elements in this array. + /// @note This does not count each data element in a strided array + virtual Index size() const = 0; + + /// Return the stride of this array. + /// @note a return value of zero means a non-constant stride + virtual Index stride() const = 0; + + /// Return the total number of data elements in this array. + /// @note This counts each data element in a strided array + virtual Index dataSize() const = 0; + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + /// Return the name of the value type of a single element in this array (e.g., "float" or "vec3d"). + virtual Name valueType() const = 0; + + /// Return the name of the codec used by this array (e.g., "trnc" or "fxpt"). + virtual Name codecType() const = 0; + + /// Return the size in bytes of the value type of a single element in this array. + /// (e.g. "float" -> 4 bytes, "vec3d" -> 24 bytes"). + virtual Index valueTypeSize() const = 0; + + /// Return the size in bytes of the storage type of a single element of this array. + /// @note If the Codec is a NullCodec, valueSize() == storageSize() + virtual Index storageTypeSize() const = 0; + + /// Return @c true if the value type is floating point + virtual bool valueTypeIsFloatingPoint() const = 0; + + /// Return @c true if the value type is a class (ie vector, matrix or quaternion return true) + virtual bool valueTypeIsClass() const = 0; + + /// Return @c true if the value type is a vector + virtual bool valueTypeIsVector() const = 0; + + /// Return @c true if the value type is a quaternion + virtual bool valueTypeIsQuaternion() const = 0; + + /// Return @c true if the value type is a matrix + virtual bool valueTypeIsMatrix() const = 0; +#endif + + /// Return the number of bytes of memory used by this attribute. + virtual size_t memUsage() const = 0; + + /// Create a new attribute array of the given (registered) type, length and stride. + /// @details If @a lock is non-null, the AttributeArray registry mutex + /// has already been locked + static Ptr create(const NamePair& type, Index length, Index stride = 1, + bool constantStride = true, const ScopedRegistryLock* lock = nullptr); + /// Return @c true if the given attribute type name is registered. + static bool isRegistered(const NamePair& type, const ScopedRegistryLock* lock = nullptr); + /// Clear the attribute type registry. + static void clearRegistry(const ScopedRegistryLock* lock = nullptr); + + /// Return the name of this attribute's type. + virtual const NamePair& type() const = 0; + /// Return @c true if this attribute is of the same type as the template parameter. + template + bool isType() const { return this->type() == AttributeArrayType::attributeType(); } + + /// Return @c true if this attribute has a value type the same as the template parameter + template + bool hasValueType() const { return this->type().first == typeNameAsString(); } + + /// @brief Set value at given index @a n from @a sourceIndex of another @a sourceArray. + /// @deprecated From ABI 6 on, use copyValues() with source-target index pairs. +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + // Windows does not allow base classes to be easily deprecated. +#ifndef _MSC_VER + OPENVDB_DEPRECATED +#endif +#endif + virtual void set(const Index n, const AttributeArray& sourceArray, const Index sourceIndex) = 0; + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + /// @brief Copy values into this array from a source array to a target array + /// as referenced by an iterator. + /// @details Iterators must adhere to the ForwardIterator interface described + /// in the example below: + /// @code + /// struct MyIterator + /// { + /// // returns true if the iterator is referencing valid copying indices + /// operator bool() const; + /// // increments the iterator + /// MyIterator& operator++(); + /// // returns the source index that the iterator is referencing for copying + /// Index sourceIndex() const; + /// // returns the target index that the iterator is referencing for copying + /// Index targetIndex() const; + /// }; + /// @endcode + /// @note It is assumed that the strided storage sizes match, the arrays are both in-core, + /// and both value types are floating-point or both integer. + /// @note It is possible to use this method to write to a uniform target array + /// if the iterator does not have non-zero target indices. + /// @note This method is not thread-safe, it must be guaranteed that this array is not + /// concurrently modified by another thread and that the source array is also not modified. + template + void copyValuesUnsafe(const AttributeArray& sourceArray, const IterT& iter); + /// @brief Like copyValuesUnsafe(), but if @a compact is true, attempt to collapse this array. + /// @note This method is not thread-safe, it must be guaranteed that this array is not + /// concurrently modified by another thread and that the source array is also not modified. + template + void copyValues(const AttributeArray& sourceArray, const IterT& iter, bool compact = true); +#endif + + /// Return @c true if this array is stored as a single uniform value. + virtual bool isUniform() const = 0; + /// @brief If this array is uniform, replace it with an array of length size(). + /// @param fill if true, assign the uniform value to each element of the array. + virtual void expand(bool fill = true) = 0; + /// Replace the existing array with a uniform zero value. + virtual void collapse() = 0; + /// Compact the existing array to become uniform if all values are identical + virtual bool compact() = 0; + + /// @deprecated Previously this returned @c true if the array was compressed, + /// now it always returns @c false. + OPENVDB_DEPRECATED bool isCompressed() const { return false; } + /// @deprecated Previously this compressed the attribute array, now it does nothing. + // Windows does not allow base classes to be deprecated +#ifndef _MSC_VER + OPENVDB_DEPRECATED +#endif + virtual bool compress() = 0; + /// @deprecated Previously this uncompressed the attribute array, now it does nothing. + // Windows does not allow base classes to be deprecated +#ifndef _MSC_VER + OPENVDB_DEPRECATED +#endif + virtual bool decompress() = 0; + + /// @brief Specify whether this attribute should be hidden (e.g., from UI or iterators). + /// @details This is useful if the attribute is used for blind data or as scratch space + /// for a calculation. + /// @note Attributes are not hidden by default. + void setHidden(bool state); + /// Return @c true if this attribute is hidden (e.g., from UI or iterators). + bool isHidden() const { return bool(mFlags & HIDDEN); } + + /// @brief Specify whether this attribute should only exist in memory + /// and not be serialized during stream output. + /// @note Attributes are not transient by default. + void setTransient(bool state); + /// Return @c true if this attribute is not serialized during stream output. + bool isTransient() const { return bool(mFlags & TRANSIENT); } + + /// @brief Specify whether this attribute is to be streamed off disk, in which + /// case, the attributes are collapsed after being first loaded leaving them + /// in a destroyed state. + /// @note This operation is not thread-safe. + void setStreaming(bool state); + /// Return @c true if this attribute is in streaming mode. + bool isStreaming() const { return bool(mFlags & STREAMING); } + + /// Return @c true if this attribute has a constant stride + bool hasConstantStride() const { return bool(mFlags & CONSTANTSTRIDE); } + + /// @brief Retrieve the attribute array flags + uint8_t flags() const { return mFlags; } + + /// Read attribute metadata and buffers from a stream. + virtual void read(std::istream&) = 0; + /// Write attribute metadata and buffers to a stream. + /// @param outputTransient if true, write out transient attributes + virtual void write(std::ostream&, bool outputTransient) const = 0; + /// Write attribute metadata and buffers to a stream, don't write transient attributes. + virtual void write(std::ostream&) const = 0; + + /// Read attribute metadata from a stream. + virtual void readMetadata(std::istream&) = 0; + /// Write attribute metadata to a stream. + /// @param outputTransient if true, write out transient attributes + /// @param paged if true, data is written out in pages + virtual void writeMetadata(std::ostream&, bool outputTransient, bool paged) const = 0; + + /// Read attribute buffers from a stream. + virtual void readBuffers(std::istream&) = 0; + /// Write attribute buffers to a stream. + /// @param outputTransient if true, write out transient attributes + virtual void writeBuffers(std::ostream&, bool outputTransient) const = 0; + + /// Read attribute buffers from a paged stream. + virtual void readPagedBuffers(compression::PagedInputStream&) = 0; + /// Write attribute buffers to a paged stream. + /// @param outputTransient if true, write out transient attributes + virtual void writePagedBuffers(compression::PagedOutputStream&, bool outputTransient) const = 0; + + /// Ensures all data is in-core + virtual void loadData() const = 0; + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + /// Return @c true if all data has been loaded + virtual bool isDataLoaded() const = 0; +#endif + + /// Check the compressed bytes and flags. If they are equal, perform a deeper + /// comparison check necessary on the inherited types (TypedAttributeArray) + /// Requires non operator implementation due to inheritance + bool operator==(const AttributeArray& other) const; + bool operator!=(const AttributeArray& other) const { return !this->operator==(other); } + +private: + friend class ::TestAttributeArray; + + /// Virtual function used by the comparison operator to perform + /// comparisons on inherited types + virtual bool isEqual(const AttributeArray& other) const = 0; + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + /// Virtual function to retrieve the data buffer cast to a char byte array + virtual char* dataAsByteArray() = 0; + virtual const char* dataAsByteArray() const = 0; + + /// Private implementation for copyValues/copyValuesUnsafe + template + void doCopyValues(const AttributeArray& sourceArray, const IterT& iter, + bool rangeChecking = true); +#endif + +protected: +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + AttributeArray(const AttributeArray& rhs, const tbb::spin_mutex::scoped_lock&); +#endif + + /// @brief Specify whether this attribute has a constant stride or not. + void setConstantStride(bool state); + + /// Obtain an Accessor that stores getter and setter functors. + virtual AccessorBasePtr getAccessor() const = 0; + + /// Register a attribute type along with a factory function. + static void registerType(const NamePair& type, FactoryMethod, + const ScopedRegistryLock* lock = nullptr); + /// Remove a attribute type from the registry. + static void unregisterType(const NamePair& type, + const ScopedRegistryLock* lock = nullptr); + +#if OPENVDB_ABI_VERSION_NUMBER < 6 + + size_t mCompressedBytes = 0; + uint8_t mFlags = 0; + uint8_t mUsePagedRead = 0; +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + tbb::atomic mOutOfCore; // interpreted as bool +#endif + compression::PageHandle::Ptr mPageHandle; + +#else // #if OPENVDB_ABI_VERSION_NUMBER < 6 + + bool mIsUniform = true; + mutable tbb::spin_mutex mMutex; + uint8_t mFlags = 0; + uint8_t mUsePagedRead = 0; + tbb::atomic mOutOfCore; // interpreted as bool + /// used for out-of-core, paged reading + union { + compression::PageHandle::Ptr mPageHandle; + size_t mCompressedBytes; // as of ABI=6, this data is packed together to save memory + }; + +#endif +}; // class AttributeArray + + +//////////////////////////////////////// + + +/// Accessor base class for AttributeArray storage where type is not available +struct AttributeArray::AccessorBase { virtual ~AccessorBase() = default; }; + +/// Templated Accessor stores typed function pointers used in binding +/// AttributeHandles +template +struct AttributeArray::Accessor : public AttributeArray::AccessorBase +{ + using GetterPtr = T (*)(const AttributeArray* array, const Index n); + using SetterPtr = void (*)(AttributeArray* array, const Index n, const T& value); + using ValuePtr = void (*)(AttributeArray* array, const T& value); + + Accessor(GetterPtr getter, SetterPtr setter, ValuePtr collapser, ValuePtr filler) : + mGetter(getter), mSetter(setter), mCollapser(collapser), mFiller(filler) { } + + GetterPtr mGetter; + SetterPtr mSetter; + ValuePtr mCollapser; + ValuePtr mFiller; +}; // struct AttributeArray::Accessor + + +//////////////////////////////////////// + + +namespace attribute_traits +{ + template struct TruncateTrait { }; + template <> struct TruncateTrait { using Type = half; }; + template <> struct TruncateTrait { using Type = short; }; + + template struct TruncateTrait> { + using Type = math::Vec3::Type>; + }; + + template struct UIntTypeTrait { }; + template struct UIntTypeTrait { using Type = uint8_t; }; + template struct UIntTypeTrait { using Type = uint16_t; }; + template struct UIntTypeTrait> { + using Type = math::Vec3; + }; + template struct UIntTypeTrait> { + using Type = math::Vec3; + }; +} + + +//////////////////////////////////////// + + +// Attribute codec schemes + +struct UnknownCodec { }; + + +struct NullCodec +{ + template + struct Storage { using Type = T; }; + + template static void decode(const ValueType&, ValueType&); + template static void encode(const ValueType&, ValueType&); + static const char* name() { return "null"; } +}; + + +struct TruncateCodec +{ + template + struct Storage { using Type = typename attribute_traits::TruncateTrait::Type; }; + + template static void decode(const StorageType&, ValueType&); + template static void encode(const ValueType&, StorageType&); + static const char* name() { return "trnc"; } +}; + + +// Fixed-point codec range for voxel-space positions [-0.5,0.5] +struct PositionRange +{ + static const char* name() { return "fxpt"; } + template static ValueType encode(const ValueType& value) { return value + ValueType(0.5); } + template static ValueType decode(const ValueType& value) { return value - ValueType(0.5); } +}; + + +// Fixed-point codec range for unsigned values in the unit range [0.0,1.0] +struct UnitRange +{ + static const char* name() { return "ufxpt"; } + template static ValueType encode(const ValueType& value) { return value; } + template static ValueType decode(const ValueType& value) { return value; } +}; + + +template +struct FixedPointCodec +{ + template + struct Storage { using Type = typename attribute_traits::UIntTypeTrait::Type; }; + + template static void decode(const StorageType&, ValueType&); + template static void encode(const ValueType&, StorageType&); + + static const char* name() { + static const std::string Name = std::string(Range::name()) + (OneByte ? "8" : "16"); + return Name.c_str(); + } +}; + + +struct UnitVecCodec +{ + using StorageType = uint16_t; + + template + struct Storage { using Type = StorageType; }; + + template static void decode(const StorageType&, math::Vec3&); + template static void encode(const math::Vec3&, StorageType&); + static const char* name() { return "uvec"; } +}; + + +//////////////////////////////////////// + + +/// Typed class for storing attribute data + +template +#if OPENVDB_ABI_VERSION_NUMBER >= 6 // for ABI=6, class is final to allow for de-virtualization +class TypedAttributeArray final: public AttributeArray +#else +class TypedAttributeArray: public AttributeArray +#endif +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + using ValueType = ValueType_; + using Codec = Codec_; + using StorageType = typename Codec::template Storage::Type; + + ////////// + + /// Default constructor, always constructs a uniform attribute. + explicit TypedAttributeArray(Index n = 1, Index strideOrTotalSize = 1, bool constantStride = true, + const ValueType& uniformValue = zeroVal()); +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + /// Deep copy constructor. + /// @note This method is thread-safe (as of ABI=7) for concurrently reading from the + /// source attribute array while being deep-copied. Specifically, this means that the + /// attribute array being deep-copied can be out-of-core and safely loaded in one thread + /// while being copied using this copy-constructor in another thread. + /// It is not thread-safe for write. + TypedAttributeArray(const TypedAttributeArray&); + /// Deep copy constructor. + /// @deprecated Use copy-constructor without unused bool parameter + OPENVDB_DEPRECATED TypedAttributeArray(const TypedAttributeArray&, bool /*unused*/); +#else + /// Deep copy constructor. + /// @note This method is not thread-safe for reading or writing, use + /// TypedAttributeArray::copy() to ensure thread-safety when reading concurrently. + TypedAttributeArray(const TypedAttributeArray&, bool uncompress = false); +#endif + /// Deep copy assignment operator. + /// @note this operator is thread-safe. + TypedAttributeArray& operator=(const TypedAttributeArray&); + /// Move constructor disabled. + TypedAttributeArray(TypedAttributeArray&&) = delete; + /// Move assignment operator disabled. + TypedAttributeArray& operator=(TypedAttributeArray&&) = delete; + + ~TypedAttributeArray() override { this->deallocate(); } + + /// Return a copy of this attribute. + /// @note This method is thread-safe. + AttributeArray::Ptr copy() const override; + + /// Return an uncompressed copy of this attribute (will just return a copy if not compressed). + /// @note This method is thread-safe. + OPENVDB_DEPRECATED AttributeArray::Ptr copyUncompressed() const override; + + /// Return a new attribute array of the given length @a n and @a stride with uniform value zero. + static Ptr create(Index n, Index strideOrTotalSize = 1, bool constantStride = true); + + /// Cast an AttributeArray to TypedAttributeArray + static TypedAttributeArray& cast(AttributeArray& attributeArray); + + /// Cast an AttributeArray to TypedAttributeArray + static const TypedAttributeArray& cast(const AttributeArray& attributeArray); + + /// Return the name of this attribute's type (includes codec) + static const NamePair& attributeType(); + /// Return the name of this attribute's type. + const NamePair& type() const override { return attributeType(); } + + /// Return @c true if this attribute type is registered. + static bool isRegistered(); + /// Register this attribute type along with a factory function. + static void registerType(); + /// Remove this attribute type from the registry. + static void unregisterType(); + + /// Return the number of elements in this array. + Index size() const override { return mSize; } + + /// Return the stride of this array. + /// @note A return value of zero means a variable stride + Index stride() const override { return hasConstantStride() ? mStrideOrTotalSize : 0; } + + /// Return the size of the data in this array. + Index dataSize() const override { + return hasConstantStride() ? mSize * mStrideOrTotalSize : mStrideOrTotalSize; + } + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + /// Return the name of the value type of a single element in this array (e.g., "float" or "vec3d"). + Name valueType() const override { return typeNameAsString(); } + + /// Return the name of the codec used by this array (e.g., "trnc" or "fxpt"). + Name codecType() const override { return Codec::name(); } + + /// Return the size in bytes of the value type of a single element in this array. + Index valueTypeSize() const override { return sizeof(ValueType); } + + /// Return the size in bytes of the storage type of a single element of this array. + /// @note If the Codec is a NullCodec, valueSize() == storageSize() + Index storageTypeSize() const override { return sizeof(StorageType); } + + /// Return @c true if the value type is floating point + bool valueTypeIsFloatingPoint() const override; + + /// Return @c true if the value type is a class (ie vector, matrix or quaternion return true) + bool valueTypeIsClass() const override; + + /// Return @c true if the value type is a vector + bool valueTypeIsVector() const override; + + /// Return @c true if the value type is a quaternion + bool valueTypeIsQuaternion() const override; + + /// Return @c true if the value type is a matrix + bool valueTypeIsMatrix() const override; +#endif + + /// Return the number of bytes of memory used by this attribute. + size_t memUsage() const override; + + /// Return the value at index @a n (assumes uncompressed and in-core) + ValueType getUnsafe(Index n) const; + /// Return the value at index @a n + ValueType get(Index n) const; + /// Return the @a value at index @a n (assumes uncompressed and in-core) + template void getUnsafe(Index n, T& value) const; + /// Return the @a value at index @a n + template void get(Index n, T& value) const; + + /// Non-member equivalent to getUnsafe() that static_casts array to this TypedAttributeArray + /// (assumes uncompressed and in-core) + static ValueType getUnsafe(const AttributeArray* array, const Index n); + + /// Set @a value at the given index @a n (assumes uncompressed and in-core) + void setUnsafe(Index n, const ValueType& value); + /// Set @a value at the given index @a n + void set(Index n, const ValueType& value); + /// Set @a value at the given index @a n (assumes uncompressed and in-core) + template void setUnsafe(Index n, const T& value); + /// Set @a value at the given index @a n + template void set(Index n, const T& value); + + /// Non-member equivalent to setUnsafe() that static_casts array to this TypedAttributeArray + /// (assumes uncompressed and in-core) + static void setUnsafe(AttributeArray* array, const Index n, const ValueType& value); + + /// Set value at given index @a n from @a sourceIndex of another @a sourceArray +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + OPENVDB_DEPRECATED +#endif + void set(const Index n, const AttributeArray& sourceArray, const Index sourceIndex) override; + + /// Return @c true if this array is stored as a single uniform value. + bool isUniform() const override { return mIsUniform; } + /// @brief Replace the single value storage with an array of length size(). + /// @note Non-uniform attributes are unchanged. + /// @param fill toggle to initialize the array elements with the pre-expanded value. + void expand(bool fill = true) override; + /// Replace the existing array with a uniform zero value. + void collapse() override; + /// Compact the existing array to become uniform if all values are identical + bool compact() override; + + /// Replace the existing array with the given uniform value. + void collapse(const ValueType& uniformValue); + /// @brief Fill the existing array with the given value. + /// @note Identical to collapse() except a non-uniform array will not become uniform. + void fill(const ValueType& value); + + /// Non-member equivalent to collapse() that static_casts array to this TypedAttributeArray + static void collapse(AttributeArray* array, const ValueType& value); + /// Non-member equivalent to fill() that static_casts array to this TypedAttributeArray + static void fill(AttributeArray* array, const ValueType& value); + + /// Compress the attribute array. + OPENVDB_DEPRECATED bool compress() override; + /// Uncompress the attribute array. + OPENVDB_DEPRECATED bool decompress() override; + + /// Read attribute data from a stream. + void read(std::istream&) override; + /// Write attribute data to a stream. + /// @param os the output stream + /// @param outputTransient if true, write out transient attributes + void write(std::ostream& os, bool outputTransient) const override; + /// Write attribute data to a stream, don't write transient attributes. + void write(std::ostream&) const override; + + /// Read attribute metadata from a stream. + void readMetadata(std::istream&) override; + /// Write attribute metadata to a stream. + /// @param os the output stream + /// @param outputTransient if true, write out transient attributes + /// @param paged if true, data is written out in pages + void writeMetadata(std::ostream& os, bool outputTransient, bool paged) const override; + + /// Read attribute buffers from a stream. + void readBuffers(std::istream&) override; + /// Write attribute buffers to a stream. + /// @param os the output stream + /// @param outputTransient if true, write out transient attributes + void writeBuffers(std::ostream& os, bool outputTransient) const override; + + /// Read attribute buffers from a paged stream. + void readPagedBuffers(compression::PagedInputStream&) override; + /// Write attribute buffers to a paged stream. + /// @param os the output stream + /// @param outputTransient if true, write out transient attributes + void writePagedBuffers(compression::PagedOutputStream& os, bool outputTransient) const override; + + /// Return @c true if this buffer's values have not yet been read from disk. + inline bool isOutOfCore() const; + + /// Ensures all data is in-core + void loadData() const override; + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + /// Return @c true if all data has been loaded + bool isDataLoaded() const override; +#endif + +protected: + AccessorBasePtr getAccessor() const override; + + /// Return the raw data buffer + inline StorageType* data() { assert(validData()); return mData.get(); } + inline const StorageType* data() const { assert(validData()); return mData.get(); } + + /// Verify that data is not out-of-core or in a partially-read state + inline bool validData() const { return !(isOutOfCore() || (flags() & PARTIALREAD)); } + +private: + friend class ::TestAttributeArray; + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + TypedAttributeArray(const TypedAttributeArray&, const tbb::spin_mutex::scoped_lock&); +#endif + + /// Load data from memory-mapped file. + inline void doLoad() const; + /// Load data from memory-mapped file (unsafe as this function is not protected by a mutex). + /// @param compression if true, loading previously compressed data will re-compressed it + inline void doLoadUnsafe(const bool compression = true) const; + /// Compress in-core data assuming mutex is locked + inline bool compressUnsafe(); + + /// Toggle out-of-core state + inline void setOutOfCore(const bool); + + /// Compare the this data to another attribute array. Used by the base class comparison operator + bool isEqual(const AttributeArray& other) const override; + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + /// Virtual function to retrieve the data buffer from the derived class cast to a char byte array + char* dataAsByteArray() override; + const char* dataAsByteArray() const override; +#endif + + size_t arrayMemUsage() const; + void allocate(); + void deallocate(); + + /// Helper function for use with registerType() + static AttributeArray::Ptr factory(Index n, Index strideOrTotalSize, bool constantStride) { + return TypedAttributeArray::create(n, strideOrTotalSize, constantStride); + } + + static std::unique_ptr sTypeName; + std::unique_ptr mData; + Index mSize; + Index mStrideOrTotalSize; +#if OPENVDB_ABI_VERSION_NUMBER < 6 // as of ABI=6, this data lives in the base class to reduce memory + bool mIsUniform = true; + mutable tbb::spin_mutex mMutex; +#endif +}; // class TypedAttributeArray + + +//////////////////////////////////////// + + +/// AttributeHandles provide access to specific TypedAttributeArray methods without needing +/// to know the compression codec, however these methods also incur the cost of a function pointer +template +class AttributeHandle +{ +public: + using Handle = AttributeHandle; + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +protected: + using GetterPtr = ValueType (*)(const AttributeArray* array, const Index n); + using SetterPtr = void (*)(AttributeArray* array, const Index n, const ValueType& value); + using ValuePtr = void (*)(AttributeArray* array, const ValueType& value); + +public: + static Ptr create(const AttributeArray& array, const bool collapseOnDestruction = true); + + AttributeHandle(const AttributeArray& array, const bool collapseOnDestruction = true); + + AttributeHandle(const AttributeHandle&) = default; + AttributeHandle& operator=(const AttributeHandle&) = default; + + virtual ~AttributeHandle(); + + Index stride() const { return mStrideOrTotalSize; } + Index size() const { return mSize; } + + bool isUniform() const; + bool hasConstantStride() const; + + ValueType get(Index n, Index m = 0) const; + + const AttributeArray& array() const; + +protected: + Index index(Index n, Index m) const; + + const AttributeArray* mArray; + + GetterPtr mGetter; + SetterPtr mSetter; + ValuePtr mCollapser; + ValuePtr mFiller; + +private: + friend class ::TestAttributeArray; + + template + typename std::enable_if::type compatibleType() const; + + template + typename std::enable_if::type compatibleType() const; + + template + typename std::enable_if::type get(Index index) const; + + template + typename std::enable_if::type get(Index index) const; + + // local copy of AttributeArray (to preserve compression) + AttributeArray::Ptr mLocalArray; + + Index mStrideOrTotalSize; + Index mSize; + bool mCollapseOnDestruction; +}; // class AttributeHandle + + +//////////////////////////////////////// + + +/// Write-able version of AttributeHandle +template +class AttributeWriteHandle : public AttributeHandle +{ +public: + using Handle = AttributeWriteHandle; + using Ptr = std::shared_ptr; + using ScopedPtr = std::unique_ptr; + + static Ptr create(AttributeArray& array, const bool expand = true); + + AttributeWriteHandle(AttributeArray& array, const bool expand = true); + + virtual ~AttributeWriteHandle() = default; + + /// @brief If this array is uniform, replace it with an array of length size(). + /// @param fill if true, assign the uniform value to each element of the array. + void expand(bool fill = true); + + /// Replace the existing array with a uniform value (zero if none provided). + void collapse(); + void collapse(const ValueType& uniformValue); + + /// Compact the existing array to become uniform if all values are identical + bool compact(); + + /// @brief Fill the existing array with the given value. + /// @note Identical to collapse() except a non-uniform array will not become uniform. + void fill(const ValueType& value); + + void set(Index n, const ValueType& value); + void set(Index n, Index m, const ValueType& value); + + AttributeArray& array(); + +private: + friend class ::TestAttributeArray; + + template + typename std::enable_if::type set(Index index, const ValueType& value) const; + + template + typename std::enable_if::type set(Index index, const ValueType& value) const; +}; // class AttributeWriteHandle + + +//////////////////////////////////////// + + +// Attribute codec implementation + + +template +inline void +NullCodec::decode(const ValueType& data, ValueType& val) +{ + val = data; +} + + +template +inline void +NullCodec::encode(const ValueType& val, ValueType& data) +{ + data = val; +} + + +template +inline void +TruncateCodec::decode(const StorageType& data, ValueType& val) +{ + val = static_cast(data); +} + + +template +inline void +TruncateCodec::encode(const ValueType& val, StorageType& data) +{ + data = static_cast(val); +} + + +template +template +inline void +FixedPointCodec::decode(const StorageType& data, ValueType& val) +{ + val = fixedPointToFloatingPoint(data); + + // shift value range to be -0.5 => 0.5 (as this is most commonly used for position) + + val = Range::template decode(val); +} + + +template +template +inline void +FixedPointCodec::encode(const ValueType& val, StorageType& data) +{ + // shift value range to be -0.5 => 0.5 (as this is most commonly used for position) + + const ValueType newVal = Range::template encode(val); + + data = floatingPointToFixedPoint(newVal); +} + + +template +inline void +UnitVecCodec::decode(const StorageType& data, math::Vec3& val) +{ + val = math::QuantizedUnitVec::unpack(data); +} + + +template +inline void +UnitVecCodec::encode(const math::Vec3& val, StorageType& data) +{ + data = math::QuantizedUnitVec::pack(val); +} + + +//////////////////////////////////////// + +// AttributeArray implementation + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + +template +void AttributeArray::doCopyValues(const AttributeArray& sourceArray, const IterT& iter, + bool rangeChecking/*=true*/) +{ + // ensure both arrays have float-float or integer-integer value types + assert(sourceArray.valueTypeIsFloatingPoint() == this->valueTypeIsFloatingPoint()); + // ensure both arrays have been loaded from disk (if delay-loaded) + assert(sourceArray.isDataLoaded() && this->isDataLoaded()); + // ensure storage size * stride matches on both arrays + assert(this->storageTypeSize()*this->stride() == + sourceArray.storageTypeSize()*sourceArray.stride()); + + const size_t bytes(sourceArray.storageTypeSize()*sourceArray.stride()); + const char* const sourceBuffer = sourceArray.dataAsByteArray(); + char* const targetBuffer = this->dataAsByteArray(); + assert(sourceBuffer && targetBuffer); + + if (rangeChecking && this->isUniform()) { + OPENVDB_THROW(IndexError, "Cannot copy array data as target array is uniform."); + } + + const bool sourceIsUniform = sourceArray.isUniform(); + + const Index sourceDataSize = rangeChecking ? sourceArray.dataSize() : 0; + const Index targetDataSize = rangeChecking ? this->dataSize() : 0; + + for (IterT it(iter); it; ++it) { + const Index sourceIndex = sourceIsUniform ? 0 : it.sourceIndex(); + const Index targetIndex = it.targetIndex(); + + if (rangeChecking) { + if (sourceIndex >= sourceDataSize) { + OPENVDB_THROW(IndexError, + "Cannot copy array data as source index exceeds size of source array."); + } + if (targetIndex >= targetDataSize) { + OPENVDB_THROW(IndexError, + "Cannot copy array data as target index exceeds size of target array."); + } + } else { + // range-checking asserts + assert(sourceIndex < sourceArray.dataSize()); + assert(targetIndex < this->dataSize()); + if (this->isUniform()) assert(targetIndex == Index(0)); + } + + const size_t targetOffset(targetIndex * bytes); + const size_t sourceOffset(sourceIndex * bytes); + + std::memcpy(targetBuffer + targetOffset, sourceBuffer + sourceOffset, bytes); + } +} + +template +void AttributeArray::copyValuesUnsafe(const AttributeArray& sourceArray, const IterT& iter) +{ + this->doCopyValues(sourceArray, iter, /*range-checking=*/false); +} + +template +void AttributeArray::copyValues(const AttributeArray& sourceArray, const IterT& iter, + bool compact/* = true*/) +{ + const Index bytes = sourceArray.storageTypeSize(); + if (bytes != this->storageTypeSize()) { + OPENVDB_THROW(TypeError, "Cannot copy array data due to mis-match in storage type sizes."); + } + + // ensure both arrays have been loaded from disk + sourceArray.loadData(); + this->loadData(); + + // if the target array is uniform, expand it first + this->expand(); + + // TODO: Acquire mutex locks for source and target arrays to ensure that + // value copying is always thread-safe. Note that the unsafe method will be + // faster, but can only be used if neither the source or target arrays are + // modified during copying. Note that this will require a new private + // virtual method with ABI=7 to access the mutex from the derived class. + + this->doCopyValues(sourceArray, iter, true); + + // attempt to compact target array + if (compact) { + this->compact(); + } +} +#endif + + +//////////////////////////////////////// + +// TypedAttributeArray implementation + +template +std::unique_ptr TypedAttributeArray::sTypeName; + + +template +TypedAttributeArray::TypedAttributeArray( + Index n, Index strideOrTotalSize, bool constantStride, const ValueType& uniformValue) + : AttributeArray() + , mData(new StorageType[1]) + , mSize(n) + , mStrideOrTotalSize(strideOrTotalSize) +{ + if (constantStride) { + this->setConstantStride(true); + if (strideOrTotalSize == 0) { + OPENVDB_THROW(ValueError, "Creating a TypedAttributeArray with a constant stride requires that " \ + "stride to be at least one.") + } + } + else { + this->setConstantStride(false); + if (mStrideOrTotalSize < n) { + OPENVDB_THROW(ValueError, "Creating a TypedAttributeArray with a non-constant stride must have " \ + "a total size of at least the number of elements in the array.") + } + } + mSize = std::max(Index(1), mSize); + mStrideOrTotalSize = std::max(Index(1), mStrideOrTotalSize); + Codec::encode(uniformValue, this->data()[0]); +} + + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 +template +TypedAttributeArray::TypedAttributeArray(const TypedAttributeArray& rhs) + : TypedAttributeArray(rhs, tbb::spin_mutex::scoped_lock(rhs.mMutex)) +{ +} + + +template +TypedAttributeArray::TypedAttributeArray(const TypedAttributeArray& rhs, + const tbb::spin_mutex::scoped_lock& lock) + : AttributeArray(rhs, lock) +#else +template +TypedAttributeArray::TypedAttributeArray(const TypedAttributeArray& rhs, bool) + : AttributeArray(rhs) +#endif + , mSize(rhs.mSize) + , mStrideOrTotalSize(rhs.mStrideOrTotalSize) +#if OPENVDB_ABI_VERSION_NUMBER < 6 + , mIsUniform(rhs.mIsUniform) +#endif +{ + if (this->validData()) { + this->allocate(); + std::memcpy(this->data(), rhs.data(), this->arrayMemUsage()); + } +} + + +template +TypedAttributeArray& +TypedAttributeArray::operator=(const TypedAttributeArray& rhs) +{ + if (&rhs != this) { + // lock both the source and target arrays to ensure thread-safety + tbb::spin_mutex::scoped_lock lock(mMutex); + tbb::spin_mutex::scoped_lock rhsLock(rhs.mMutex); + + this->deallocate(); + + mFlags = rhs.mFlags; + mUsePagedRead = rhs.mUsePagedRead; + mSize = rhs.mSize; + mStrideOrTotalSize = rhs.mStrideOrTotalSize; + mIsUniform = rhs.mIsUniform; + + if (this->validData()) { + this->allocate(); + std::memcpy(this->newDataAsByteArray(), rhs.newDataAsByteArray(), this->arrayMemUsage()); + } + } +} + + +template +inline const NamePair& +TypedAttributeArray::attributeType() +{ + static std::once_flag once; + std::call_once(once, []() + { + sTypeName.reset(new NamePair(typeNameAsString(), Codec::name())); + }); + return *sTypeName; +} + + +template +inline bool +TypedAttributeArray::isRegistered() +{ + return AttributeArray::isRegistered(TypedAttributeArray::attributeType()); +} + + +template +inline void +TypedAttributeArray::registerType() +{ + AttributeArray::registerType(TypedAttributeArray::attributeType(), TypedAttributeArray::factory); +} + + +template +inline void +TypedAttributeArray::unregisterType() +{ + AttributeArray::unregisterType(TypedAttributeArray::attributeType()); +} + + +template +inline typename TypedAttributeArray::Ptr +TypedAttributeArray::create(Index n, Index stride, bool constantStride) +{ + return Ptr(new TypedAttributeArray(n, stride, constantStride)); +} + +template +inline TypedAttributeArray& +TypedAttributeArray::cast(AttributeArray& attributeArray) +{ + if (!attributeArray.isType()) { + OPENVDB_THROW(TypeError, "Invalid Attribute Type"); + } + return static_cast(attributeArray); +} + +template +inline const TypedAttributeArray& +TypedAttributeArray::cast(const AttributeArray& attributeArray) +{ + if (!attributeArray.isType()) { + OPENVDB_THROW(TypeError, "Invalid Attribute Type"); + } + return static_cast(attributeArray); +} + +template +AttributeArray::Ptr +TypedAttributeArray::copy() const +{ +#if OPENVDB_ABI_VERSION_NUMBER < 7 + tbb::spin_mutex::scoped_lock lock(mMutex); +#endif + return AttributeArray::Ptr(new TypedAttributeArray(*this)); +} + + +template +AttributeArray::Ptr +TypedAttributeArray::copyUncompressed() const +{ + return this->copy(); +} + + +template +size_t +TypedAttributeArray::arrayMemUsage() const +{ + if (this->isOutOfCore()) return 0; + + return (mIsUniform ? 1 : this->dataSize()) * sizeof(StorageType); +} + + +template +void +TypedAttributeArray::allocate() +{ + assert(!mData); + if (mIsUniform) { + mData.reset(new StorageType[1]); + } + else { + const size_t size(this->dataSize()); + assert(size > 0); + mData.reset(new StorageType[size]); + } +} + + +template +void +TypedAttributeArray::deallocate() +{ + // detach from file if delay-loaded + if (this->isOutOfCore()) { + this->setOutOfCore(false); + this->mPageHandle.reset(); + } + if (mData) mData.reset(); +} + + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 +template +bool +TypedAttributeArray::valueTypeIsFloatingPoint() const +{ + // TODO: Update to use Traits that correctly handle matrices and quaternions. + + if (std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value) return true; + + using ElementT = typename VecTraits::ElementType; + + // half is not defined as float point as expected, so explicitly handle it + return std::is_floating_point::value || std::is_same::value; +} + + +template +bool +TypedAttributeArray::valueTypeIsClass() const +{ + // half is not defined as a non-class type as expected, so explicitly exclude it + return std::is_class::value && !std::is_same::value; +} + + +template +bool +TypedAttributeArray::valueTypeIsVector() const +{ + return VecTraits::IsVec; +} + + +template +bool +TypedAttributeArray::valueTypeIsQuaternion() const +{ + // TODO: improve performance by making this a compile-time check using type traits + return !this->valueType().compare(0, 4, "quat"); +} + + +template +bool +TypedAttributeArray::valueTypeIsMatrix() const +{ + // TODO: improve performance by making this a compile-time check using type traits + return !this->valueType().compare(0, 3, "mat"); +} +#endif + + +template +size_t +TypedAttributeArray::memUsage() const +{ + return sizeof(*this) + (bool(mData) ? this->arrayMemUsage() : 0); +} + + +template +typename TypedAttributeArray::ValueType +TypedAttributeArray::getUnsafe(Index n) const +{ + assert(n < this->dataSize()); + + ValueType val; + Codec::decode(/*in=*/this->data()[mIsUniform ? 0 : n], /*out=*/val); + return val; +} + + +template +typename TypedAttributeArray::ValueType +TypedAttributeArray::get(Index n) const +{ + if (n >= this->dataSize()) OPENVDB_THROW(IndexError, "Out-of-range access."); + if (this->isOutOfCore()) this->doLoad(); + + return this->getUnsafe(n); +} + + +template +template +void +TypedAttributeArray::getUnsafe(Index n, T& val) const +{ + val = static_cast(this->getUnsafe(n)); +} + + +template +template +void +TypedAttributeArray::get(Index n, T& val) const +{ + val = static_cast(this->get(n)); +} + + +template +typename TypedAttributeArray::ValueType +TypedAttributeArray::getUnsafe(const AttributeArray* array, const Index n) +{ + return static_cast*>(array)->getUnsafe(n); +} + + +template +void +TypedAttributeArray::setUnsafe(Index n, const ValueType& val) +{ + assert(n < this->dataSize()); + assert(!this->isOutOfCore()); + assert(!this->isUniform()); + + // this unsafe method assumes the data is not uniform, however if it is, this redirects the index + // to zero, which is marginally less efficient but ensures not writing to an illegal address + + Codec::encode(/*in=*/val, /*out=*/this->data()[mIsUniform ? 0 : n]); +} + + +template +void +TypedAttributeArray::set(Index n, const ValueType& val) +{ + if (n >= this->dataSize()) OPENVDB_THROW(IndexError, "Out-of-range access."); + if (this->isOutOfCore()) this->doLoad(); + if (this->isUniform()) this->expand(); + + this->setUnsafe(n, val); +} + + +template +template +void +TypedAttributeArray::setUnsafe(Index n, const T& val) +{ + this->setUnsafe(n, static_cast(val)); +} + + +template +template +void +TypedAttributeArray::set(Index n, const T& val) +{ + this->set(n, static_cast(val)); +} + + +template +void +TypedAttributeArray::setUnsafe(AttributeArray* array, const Index n, const ValueType& value) +{ + static_cast*>(array)->setUnsafe(n, value); +} + + +template +void +TypedAttributeArray::set(Index n, const AttributeArray& sourceArray, const Index sourceIndex) +{ + const TypedAttributeArray& sourceTypedArray = static_cast(sourceArray); + + ValueType sourceValue; + sourceTypedArray.get(sourceIndex, sourceValue); + + this->set(n, sourceValue); +} + + +template +void +TypedAttributeArray::expand(bool fill) +{ + if (!mIsUniform) return; + + const StorageType val = this->data()[0]; + + { + tbb::spin_mutex::scoped_lock lock(mMutex); + this->deallocate(); + mIsUniform = false; + this->allocate(); + } + + if (fill) { + for (Index i = 0; i < this->dataSize(); ++i) this->data()[i] = val; + } +} + + +template +bool +TypedAttributeArray::compact() +{ + if (mIsUniform) return true; + + // compaction is not possible if any values are different + const ValueType_ val = this->get(0); + for (Index i = 1; i < this->dataSize(); i++) { + if (!math::isExactlyEqual(this->get(i), val)) return false; + } + + this->collapse(this->get(0)); + return true; +} + + +template +void +TypedAttributeArray::collapse() +{ + this->collapse(zeroVal()); +} + + +template +void +TypedAttributeArray::collapse(const ValueType& uniformValue) +{ + if (!mIsUniform) { + tbb::spin_mutex::scoped_lock lock(mMutex); + this->deallocate(); + mIsUniform = true; + this->allocate(); + } + Codec::encode(uniformValue, this->data()[0]); +} + + +template +void +TypedAttributeArray::collapse(AttributeArray* array, const ValueType& value) +{ + static_cast*>(array)->collapse(value); +} + + +template +void +TypedAttributeArray::fill(const ValueType& value) +{ + if (this->isOutOfCore()) { + tbb::spin_mutex::scoped_lock lock(mMutex); + this->deallocate(); + this->allocate(); + } + + const Index size = mIsUniform ? 1 : this->dataSize(); + for (Index i = 0; i < size; ++i) { + Codec::encode(value, this->data()[i]); + } +} + + +template +void +TypedAttributeArray::fill(AttributeArray* array, const ValueType& value) +{ + static_cast*>(array)->fill(value); +} + + +template +inline bool +TypedAttributeArray::compress() +{ + return false; +} + + +template +inline bool +TypedAttributeArray::compressUnsafe() +{ + return false; +} + + +template +inline bool +TypedAttributeArray::decompress() +{ + return false; +} + + +template +bool +TypedAttributeArray::isOutOfCore() const +{ +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + return mOutOfCore; +#else + return (mFlags & OUTOFCORE); +#endif +} + + +template +void +TypedAttributeArray::setOutOfCore(const bool b) +{ +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + mOutOfCore = b; +#else + if (b) mFlags = static_cast(mFlags | OUTOFCORE); + else mFlags = static_cast(mFlags & ~OUTOFCORE); +#endif +} + + +template +void +TypedAttributeArray::doLoad() const +{ + if (!(this->isOutOfCore())) return; + + TypedAttributeArray* self = + const_cast*>(this); + + // This lock will be contended at most once, after which this buffer + // will no longer be out-of-core. + tbb::spin_mutex::scoped_lock lock(self->mMutex); + this->doLoadUnsafe(); +} + + +template +void +TypedAttributeArray::loadData() const +{ + this->doLoad(); +} + + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 +template +bool +TypedAttributeArray::isDataLoaded() const +{ + return !this->isOutOfCore(); +} +#endif + + +template +void +TypedAttributeArray::read(std::istream& is) +{ + this->readMetadata(is); + this->readBuffers(is); +} + + +template +void +TypedAttributeArray::readMetadata(std::istream& is) +{ + // read data + + Index64 bytes = Index64(0); + is.read(reinterpret_cast(&bytes), sizeof(Index64)); + bytes = bytes - /*flags*/sizeof(Int16) - /*size*/sizeof(Index); + + uint8_t flags = uint8_t(0); + is.read(reinterpret_cast(&flags), sizeof(uint8_t)); + mFlags = flags; + + uint8_t serializationFlags = uint8_t(0); + is.read(reinterpret_cast(&serializationFlags), sizeof(uint8_t)); + + Index size = Index(0); + is.read(reinterpret_cast(&size), sizeof(Index)); + mSize = size; + + // warn if an unknown flag has been set + if (mFlags >= 0x20) { + OPENVDB_LOG_WARN("Unknown attribute flags for VDB file format."); + } + // error if an unknown serialization flag has been set, + // as this will adjust the layout of the data and corrupt the ability to read + if (serializationFlags >= 0x10) { + OPENVDB_THROW(IoError, "Unknown attribute serialization flags for VDB file format."); + } + + // set uniform, compressed and page read state + + mIsUniform = serializationFlags & WRITEUNIFORM; + mUsePagedRead = serializationFlags & WRITEPAGED; + mCompressedBytes = bytes; + mFlags |= PARTIALREAD; // mark data as having been partially read + + // read strided value (set to 1 if array is not strided) + + if (serializationFlags & WRITESTRIDED) { + Index stride = Index(0); + is.read(reinterpret_cast(&stride), sizeof(Index)); + mStrideOrTotalSize = stride; + } + else { + mStrideOrTotalSize = 1; + } +} + + +template +void +TypedAttributeArray::readBuffers(std::istream& is) +{ + if (mUsePagedRead) { + // use readBuffers(PagedInputStream&) for paged buffers + OPENVDB_THROW(IoError, "Cannot read paged AttributeArray buffers."); + } + + tbb::spin_mutex::scoped_lock lock(mMutex); + + this->deallocate(); + + uint8_t bloscCompressed(0); + if (!mIsUniform) is.read(reinterpret_cast(&bloscCompressed), sizeof(uint8_t)); + + assert(mFlags & PARTIALREAD); + std::unique_ptr buffer(new char[mCompressedBytes]); + is.read(buffer.get(), mCompressedBytes); + mCompressedBytes = 0; + mFlags = static_cast(mFlags & ~PARTIALREAD); // mark data read as having completed + + // compressed on-disk + + if (bloscCompressed == uint8_t(1)) { + + // decompress buffer + + const size_t inBytes = this->dataSize() * sizeof(StorageType); + std::unique_ptr newBuffer = compression::bloscDecompress(buffer.get(), inBytes); + if (newBuffer) buffer.reset(newBuffer.release()); + } + + // set data to buffer + + mData.reset(reinterpret_cast(buffer.release())); +} + + +template +void +TypedAttributeArray::readPagedBuffers(compression::PagedInputStream& is) +{ + if (!mUsePagedRead) { + if (!is.sizeOnly()) this->readBuffers(is.getInputStream()); + return; + } + + // If this array is being read from a memory-mapped file, delay loading of its data + // until the data is actually accessed. + io::MappedFile::Ptr mappedFile = io::getMappedFilePtr(is.getInputStream()); + const bool delayLoad = (mappedFile.get() != nullptr); + + if (is.sizeOnly()) + { + size_t compressedBytes(mCompressedBytes); + mCompressedBytes = 0; // if not set to zero, mPageHandle will attempt to destroy invalid memory + mFlags = static_cast(mFlags & ~PARTIALREAD); // mark data read as having completed + assert(!mPageHandle); + mPageHandle = is.createHandle(compressedBytes); + return; + } + + assert(mPageHandle); + + tbb::spin_mutex::scoped_lock lock(mMutex); + + this->deallocate(); + + this->setOutOfCore(delayLoad); + is.read(mPageHandle, std::streamsize(mPageHandle->size()), delayLoad); + + if (!delayLoad) { + std::unique_ptr buffer = mPageHandle->read(); + mData.reset(reinterpret_cast(buffer.release())); + } + + // clear page state + + mUsePagedRead = 0; +} + + +template +void +TypedAttributeArray::write(std::ostream& os) const +{ + this->write(os, /*outputTransient=*/false); +} + + +template +void +TypedAttributeArray::write(std::ostream& os, bool outputTransient) const +{ + this->writeMetadata(os, outputTransient, /*paged=*/false); + this->writeBuffers(os, outputTransient); +} + + +template +void +TypedAttributeArray::writeMetadata(std::ostream& os, bool outputTransient, bool paged) const +{ + if (!outputTransient && this->isTransient()) return; + + if (mFlags & PARTIALREAD) { + OPENVDB_THROW(IoError, "Cannot write out a partially-read AttributeArray."); + } + +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + uint8_t flags(mFlags); +#else + uint8_t flags(mFlags & uint8_t(~OUTOFCORE)); +#endif + uint8_t serializationFlags(0); + Index size(mSize); + Index stride(mStrideOrTotalSize); + bool strideOfOne(this->stride() == 1); + + bool bloscCompression = io::getDataCompression(os) & io::COMPRESS_BLOSC; + + // any compressed data needs to be loaded if out-of-core + if (bloscCompression) this->doLoad(); + + size_t compressedBytes = 0; + + if (!strideOfOne) + { + serializationFlags |= WRITESTRIDED; + } + + if (mIsUniform) + { + serializationFlags |= WRITEUNIFORM; + if (bloscCompression && paged) serializationFlags |= WRITEPAGED; + } + else if (bloscCompression) + { + if (paged) serializationFlags |= WRITEPAGED; + else { + const char* charBuffer = reinterpret_cast(this->data()); + const size_t inBytes = this->arrayMemUsage(); + compressedBytes = compression::bloscCompressedSize(charBuffer, inBytes); + } + } + + Index64 bytes = /*flags*/ sizeof(Int16) + /*size*/ sizeof(Index); + + bytes += (compressedBytes > 0) ? compressedBytes : this->arrayMemUsage(); + + // write data + + os.write(reinterpret_cast(&bytes), sizeof(Index64)); + os.write(reinterpret_cast(&flags), sizeof(uint8_t)); + os.write(reinterpret_cast(&serializationFlags), sizeof(uint8_t)); + os.write(reinterpret_cast(&size), sizeof(Index)); + + // write strided + if (!strideOfOne) os.write(reinterpret_cast(&stride), sizeof(Index)); +} + + +template +void +TypedAttributeArray::writeBuffers(std::ostream& os, bool outputTransient) const +{ + if (!outputTransient && this->isTransient()) return; + + if (mFlags & PARTIALREAD) { + OPENVDB_THROW(IoError, "Cannot write out a partially-read AttributeArray."); + } + + this->doLoad(); + + if (this->isUniform()) { + os.write(reinterpret_cast(this->data()), sizeof(StorageType)); + } + else if (io::getDataCompression(os) & io::COMPRESS_BLOSC) + { + std::unique_ptr compressedBuffer; + size_t compressedBytes = 0; + const char* charBuffer = reinterpret_cast(this->data()); + const size_t inBytes = this->arrayMemUsage(); + compressedBuffer = compression::bloscCompress(charBuffer, inBytes, compressedBytes); + if (compressedBuffer) { + uint8_t bloscCompressed(1); + os.write(reinterpret_cast(&bloscCompressed), sizeof(uint8_t)); + os.write(reinterpret_cast(compressedBuffer.get()), compressedBytes); + } + else { + uint8_t bloscCompressed(0); + os.write(reinterpret_cast(&bloscCompressed), sizeof(uint8_t)); + os.write(reinterpret_cast(this->data()), inBytes); + } + } + else + { + uint8_t bloscCompressed(0); + os.write(reinterpret_cast(&bloscCompressed), sizeof(uint8_t)); + os.write(reinterpret_cast(this->data()), this->arrayMemUsage()); + } +} + + +template +void +TypedAttributeArray::writePagedBuffers(compression::PagedOutputStream& os, bool outputTransient) const +{ + if (!outputTransient && this->isTransient()) return; + + // paged compression only available when Blosc is enabled + bool bloscCompression = io::getDataCompression(os.getOutputStream()) & io::COMPRESS_BLOSC; + if (!bloscCompression) { + if (!os.sizeOnly()) this->writeBuffers(os.getOutputStream(), outputTransient); + return; + } + + if (mFlags & PARTIALREAD) { + OPENVDB_THROW(IoError, "Cannot write out a partially-read AttributeArray."); + } + + this->doLoad(); + + os.write(reinterpret_cast(this->data()), this->arrayMemUsage()); +} + + +template +void +TypedAttributeArray::doLoadUnsafe(const bool /*compression*/) const +{ + if (!(this->isOutOfCore())) return; + + // this function expects the mutex to already be locked + + auto* self = const_cast*>(this); + + assert(self->mPageHandle); + assert(!(self->mFlags & PARTIALREAD)); + + std::unique_ptr buffer = self->mPageHandle->read(); + + self->mData.reset(reinterpret_cast(buffer.release())); + + self->mPageHandle.reset(); + + // clear all write and out-of-core flags + +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + self->mOutOfCore = false; +#else + self->mFlags &= uint8_t(~OUTOFCORE); +#endif +} + + +template +AttributeArray::AccessorBasePtr +TypedAttributeArray::getAccessor() const +{ + // use the faster 'unsafe' get and set methods as attribute handles + // ensure data is uncompressed and in-core when constructed + + return AccessorBasePtr(new AttributeArray::Accessor( + &TypedAttributeArray::getUnsafe, + &TypedAttributeArray::setUnsafe, + &TypedAttributeArray::collapse, + &TypedAttributeArray::fill)); +} + + +template +bool +TypedAttributeArray::isEqual(const AttributeArray& other) const +{ + const TypedAttributeArray* const otherT = dynamic_cast* >(&other); + if(!otherT) return false; + if(this->mSize != otherT->mSize || + this->mStrideOrTotalSize != otherT->mStrideOrTotalSize || + this->mIsUniform != otherT->mIsUniform || + this->attributeType() != this->attributeType()) return false; + + this->doLoad(); + otherT->doLoad(); + + const StorageType *target = this->data(), *source = otherT->data(); + if (!target && !source) return true; + if (!target || !source) return false; + Index n = this->mIsUniform ? 1 : mSize; + while (n && math::isExactlyEqual(*target++, *source++)) --n; + return n == 0; +} + + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 +template +char* +TypedAttributeArray::dataAsByteArray() +{ + return reinterpret_cast(this->data()); +} + + +template +const char* +TypedAttributeArray::dataAsByteArray() const +{ + return reinterpret_cast(this->data()); +} +#endif + + +//////////////////////////////////////// + + +/// Accessor to call unsafe get and set methods based on templated Codec and Value +template +struct AccessorEval +{ + using GetterPtr = ValueType (*)(const AttributeArray* array, const Index n); + using SetterPtr = void (*)(AttributeArray* array, const Index n, const ValueType& value); + + /// Getter that calls to TypedAttributeArray::getUnsafe() + /// @note Functor argument is provided but not required for the generic case + static ValueType get(GetterPtr /*functor*/, const AttributeArray* array, const Index n) { + return TypedAttributeArray::getUnsafe(array, n); + } + + /// Getter that calls to TypedAttributeArray::setUnsafe() + /// @note Functor argument is provided but not required for the generic case + static void set(SetterPtr /*functor*/, AttributeArray* array, const Index n, const ValueType& value) { + TypedAttributeArray::setUnsafe(array, n, value); + } +}; + + +/// Partial specialization when Codec is not known at compile-time to use the supplied functor instead +template +struct AccessorEval +{ + using GetterPtr = ValueType (*)(const AttributeArray* array, const Index n); + using SetterPtr = void (*)(AttributeArray* array, const Index n, const ValueType& value); + + /// Getter that calls the supplied functor + static ValueType get(GetterPtr functor, const AttributeArray* array, const Index n) { + return (*functor)(array, n); + } + + /// Setter that calls the supplied functor + static void set(SetterPtr functor, AttributeArray* array, const Index n, const ValueType& value) { + (*functor)(array, n, value); + } +}; + + +//////////////////////////////////////// + +// AttributeHandle implementation + +template +typename AttributeHandle::Ptr +AttributeHandle::create(const AttributeArray& array, const bool collapseOnDestruction) +{ + return typename AttributeHandle::Ptr( + new AttributeHandle(array, collapseOnDestruction)); +} + +template +AttributeHandle::AttributeHandle(const AttributeArray& array, const bool collapseOnDestruction) + : mArray(&array) + , mStrideOrTotalSize(array.hasConstantStride() ? array.stride() : 1) + , mSize(array.hasConstantStride() ? array.size() : array.dataSize()) + , mCollapseOnDestruction(collapseOnDestruction && array.isStreaming()) +{ + if (!this->compatibleType::value>()) { + OPENVDB_THROW(TypeError, "Cannot bind handle due to incompatible type of AttributeArray."); + } + + // load data if delay-loaded + + mArray->loadData(); + + // bind getter and setter methods + + AttributeArray::AccessorBasePtr accessor = mArray->getAccessor(); + assert(accessor); + + AttributeArray::Accessor* typedAccessor = static_cast*>(accessor.get()); + + mGetter = typedAccessor->mGetter; + mSetter = typedAccessor->mSetter; + mCollapser = typedAccessor->mCollapser; + mFiller = typedAccessor->mFiller; +} + +template +AttributeHandle::~AttributeHandle() +{ + // if enabled, attribute is collapsed on destruction of the handle to save memory + if (mCollapseOnDestruction) const_cast(this->mArray)->collapse(); +} + +template +template +typename std::enable_if::type +AttributeHandle::compatibleType() const +{ + // if codec is unknown, just check the value type + + return mArray->hasValueType(); +} + +template +template +typename std::enable_if::type +AttributeHandle::compatibleType() const +{ + // if the codec is known, check the value type and codec + + return mArray->isType>(); +} + +template +const AttributeArray& AttributeHandle::array() const +{ + assert(mArray); + return *mArray; +} + +template +Index AttributeHandle::index(Index n, Index m) const +{ + Index index = n * mStrideOrTotalSize + m; + assert(index < (mSize * mStrideOrTotalSize)); + return index; +} + +template +ValueType AttributeHandle::get(Index n, Index m) const +{ + return this->get::value>(this->index(n, m)); +} + +template +template +typename std::enable_if::type +AttributeHandle::get(Index index) const +{ + // if the codec is unknown, use the getter functor + + return (*mGetter)(mArray, index); +} + +template +template +typename std::enable_if::type +AttributeHandle::get(Index index) const +{ + // if the codec is known, call the method on the attribute array directly + + return TypedAttributeArray::getUnsafe(mArray, index); +} + +template +bool AttributeHandle::isUniform() const +{ + return mArray->isUniform(); +} + +template +bool AttributeHandle::hasConstantStride() const +{ + return mArray->hasConstantStride(); +} + +//////////////////////////////////////// + +// AttributeWriteHandle implementation + +template +typename AttributeWriteHandle::Ptr +AttributeWriteHandle::create(AttributeArray& array, const bool expand) +{ + return typename AttributeWriteHandle::Ptr( + new AttributeWriteHandle(array, expand)); +} + +template +AttributeWriteHandle::AttributeWriteHandle(AttributeArray& array, const bool expand) + : AttributeHandle(array, /*collapseOnDestruction=*/false) +{ + if (expand) array.expand(); +} + +template +void AttributeWriteHandle::set(Index n, const ValueType& value) +{ + this->set::value>(this->index(n, 0), value); +} + +template +void AttributeWriteHandle::set(Index n, Index m, const ValueType& value) +{ + this->set::value>(this->index(n, m), value); +} + +template +void AttributeWriteHandle::expand(const bool fill) +{ + const_cast(this->mArray)->expand(fill); +} + +template +void AttributeWriteHandle::collapse() +{ + const_cast(this->mArray)->collapse(); +} + +template +bool AttributeWriteHandle::compact() +{ + return const_cast(this->mArray)->compact(); +} + +template +void AttributeWriteHandle::collapse(const ValueType& uniformValue) +{ + this->mCollapser(const_cast(this->mArray), uniformValue); +} + +template +void AttributeWriteHandle::fill(const ValueType& value) +{ + this->mFiller(const_cast(this->mArray), value); +} + +template +template +typename std::enable_if::type +AttributeWriteHandle::set(Index index, const ValueType& value) const +{ + // if the codec is unknown, use the setter functor + + (*this->mSetter)(const_cast(this->mArray), index, value); +} + +template +template +typename std::enable_if::type +AttributeWriteHandle::set(Index index, const ValueType& value) const +{ + // if the codec is known, call the method on the attribute array directly + + TypedAttributeArray::setUnsafe(const_cast(this->mArray), index, value); +} + +template +AttributeArray& AttributeWriteHandle::array() +{ + assert(this->mArray); + return *const_cast(this->mArray); +} + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED diff --git a/openvdb/points/AttributeArrayString.cc b/openvdb/points/AttributeArrayString.cc new file mode 100644 index 00000000..6cd71c4e --- /dev/null +++ b/openvdb/points/AttributeArrayString.cc @@ -0,0 +1,366 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/AttributeArrayString.cc + +#include "AttributeArrayString.h" + +#include +#include + +#include + +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +namespace { + + bool isStringMeta(const Name& key, const Metadata::ConstPtr& meta) + { + // ensure the metadata is StringMetadata + if (meta->typeName() != "string") return false; + // string attribute metadata must have a key that starts "string:" + if (key.compare(0, 7, "string:") != 0) return false; + + return true; + } + + Name getStringKey(const StringIndexType index) + { + return "string:" + std::to_string(index - 1); + } + + StringIndexType getStringIndex(const Name& key) + { + const Name indexStr = key.substr(7, key.size() - 7); + // extract the index as an unsigned integer + return static_cast(std::stoul(indexStr)) + 1; + } + +} // namespace + + +//////////////////////////////////////// + + +// StringMetaInserter implementation + + +StringMetaInserter::StringMetaInserter(MetaMap& metadata) + : mMetadata(metadata) + , mIdBlocks() + , mValues() +{ + // populate the cache + resetCache(); +} + + +void StringMetaInserter::insert(const Name& name) +{ + using IterT = std::vector>::iterator; + + // if name already exists, do nothing + + if (mValues.count(name)) return; + + // look through the id blocks for the next available index + + Index index = 1; + IterT iter = mIdBlocks.begin(); + for (; iter != mIdBlocks.end(); ++iter) { + const Index start = iter->first; + const Index end = start + iter->second; + + if (index < start || index >= end) break; + index = end; + } + + // index now holds the next valid index. if it's 1 (the beginning + // iterator) no initial block exists - add it + + IterT block; + if (iter == mIdBlocks.begin()) { + block = mIdBlocks.insert(iter, {1, 1}); + iter = std::next(block); + } + else { + // accumulate the id block size where the next index is going + block = std::prev(iter); + block->second += 1; + } + + // see if this block and the next block can be compacted + + if (iter != mIdBlocks.end() && + block->second + 1 == iter->first) { + block->second += iter->second; + mIdBlocks.erase(iter); + } + + // insert into metadata + + const Name key = getStringKey(index); + mMetadata.insertMeta(key, StringMetadata(name)); + + // update the cache + + mValues.emplace(name); +} + + +void StringMetaInserter::resetCache() +{ + mValues.clear(); + mIdBlocks.clear(); + + std::vector stringIndices; + + for (auto it = mMetadata.beginMeta(), itEnd = mMetadata.endMeta(); it != itEnd; ++it) { + const Name& key = it->first; + const Metadata::ConstPtr meta = it->second; + + // ensure the metadata is StringMetadata and key starts "string:" + if (!isStringMeta(key, meta)) continue; + + // extract index + stringIndices.emplace_back(getStringIndex(key)); + + // extract value from metadata and add to cache + const StringMetadata* stringMeta = static_cast(meta.get()); + assert(stringMeta); + mValues.insert(stringMeta->value()); + } + + if (stringIndices.empty()) return; + + tbb::parallel_sort(stringIndices.begin(), stringIndices.end()); + + // bucket string indices + + Index key = stringIndices.front(); + Index size = 0; + + // For each id, see if it's adjacent id is sequentially increasing and continue to + // track how many are until we find a value that isn't. Store the start and length + // of each of these blocks. For example, the following container could be created + // consisting of 3 elements: + // key -> size + // ------------- + // 7 -> 1000 (values 7->1007) + // 1020 -> 5 (values 1020->1025) + // 2013 -> 30 (values 2013->2043) + // Note that the end value is exclusive (values 1007, 1025 and 2043 do not exist + // given the above example) + + for (const Index id : stringIndices) { + if (key + size != id) { + assert(size > 0); + mIdBlocks.emplace_back(key, size); + size = 0; + key = id; + } + ++size; + } + + // add the last block + mIdBlocks.emplace_back(key, size); +} + + +//////////////////////////////////////// + +// StringAttributeHandle implementation + + +StringAttributeHandle::Ptr +StringAttributeHandle::create(const AttributeArray& array, const MetaMap& metadata, const bool preserveCompression) +{ + return std::make_shared(array, metadata, preserveCompression); +} + + +StringAttributeHandle::StringAttributeHandle(const AttributeArray& array, + const MetaMap& metadata, + const bool preserveCompression) + : mHandle(array, preserveCompression) + , mMetadata(metadata) +{ + if (!isString(array)) { + OPENVDB_THROW(TypeError, "Cannot create a StringAttributeHandle for an attribute array that is not a string."); + } +} + + +Name StringAttributeHandle::get(Index n, Index m) const +{ + Name name; + this->get(name, n, m); + return name; +} + + +void StringAttributeHandle::get(Name& name, Index n, Index m) const +{ + StringIndexType index = mHandle.get(n, m); + + // index zero is reserved for an empty string + + if (index == 0) { + name = ""; + return; + } + + const Name key = getStringKey(index); + + // key is assumed to exist in metadata + + openvdb::StringMetadata::ConstPtr meta = mMetadata.getMetadata(key); + + if (!meta) { + OPENVDB_THROW(LookupError, "String attribute cannot be found with index - \"" << index << "\"."); + } + + name = meta->value(); +} + +const AttributeArray& StringAttributeHandle::array() const +{ + return mHandle.array(); +} + + +//////////////////////////////////////// + +// StringAttributeWriteHandle implementation + +StringAttributeWriteHandle::Ptr +StringAttributeWriteHandle::create(AttributeArray& array, const MetaMap& metadata, const bool expand) +{ + return std::make_shared(array, metadata, expand); +} + + +StringAttributeWriteHandle::StringAttributeWriteHandle(AttributeArray& array, + const MetaMap& metadata, + const bool expand) + : StringAttributeHandle(array, metadata, /*preserveCompression=*/ false) + , mWriteHandle(array, expand) +{ + // populate the cache + resetCache(); +} + + +void StringAttributeWriteHandle::expand(bool fill) +{ + mWriteHandle.expand(fill); +} + + +void StringAttributeWriteHandle::collapse() +{ + // zero is used for an empty string + mWriteHandle.collapse(0); +} + + +void StringAttributeWriteHandle::collapse(const Name& name) +{ + Index index = getIndex(name); + mWriteHandle.collapse(index); +} + + +bool StringAttributeWriteHandle::compact() +{ + return mWriteHandle.compact(); +} + + +void StringAttributeWriteHandle::fill(const Name& name) +{ + Index index = getIndex(name); + mWriteHandle.fill(index); +} + + +void StringAttributeWriteHandle::set(Index n, const Name& name) +{ + Index index = getIndex(name); + mWriteHandle.set(n, /*stride*/0, index); +} + + +void StringAttributeWriteHandle::set(Index n, Index m, const Name& name) +{ + Index index = getIndex(name); + mWriteHandle.set(n, m, index); +} + + +void StringAttributeWriteHandle::resetCache() +{ + mCache.clear(); + + // re-populate the cache + + for (auto it = mMetadata.beginMeta(), itEnd = mMetadata.endMeta(); it != itEnd; ++it) { + const Name& key = it->first; + const Metadata::Ptr meta = it->second; + + // ensure the metadata is StringMetadata and key starts "string:" + if (!isStringMeta(key, meta)) continue; + + const auto* stringMeta = static_cast(meta.get()); + assert(stringMeta); + + // remove "string:" + Index index = getStringIndex(key); + + // add to the cache + mCache[stringMeta->value()] = index; + } +} + + +AttributeArray& StringAttributeWriteHandle::array() +{ + return mWriteHandle.array(); +} + + +bool StringAttributeWriteHandle::contains(const Name& name) const +{ + // empty strings always have an index at index zero + if (name.empty()) return true; + return mCache.find(name) != mCache.end(); +} + + +Index StringAttributeWriteHandle::getIndex(const Name& name) const +{ + // zero used for an empty string + if (name.empty()) return Index(0); + + auto it = mCache.find(name); + + if (it == mCache.end()) { + OPENVDB_THROW(LookupError, "String does not exist in Metadata, insert it and reset the cache - \"" << name << "\"."); + } + + return it->second; +} + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/points/AttributeArrayString.h b/openvdb/points/AttributeArrayString.h new file mode 100644 index 00000000..be09392f --- /dev/null +++ b/openvdb/points/AttributeArrayString.h @@ -0,0 +1,203 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/AttributeArrayString.h +/// +/// @author Dan Bailey +/// +/// @brief Attribute array storage for string data using Descriptor Metadata. + +#ifndef OPENVDB_POINTS_ATTRIBUTE_ARRAY_STRING_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_ATTRIBUTE_ARRAY_STRING_HAS_BEEN_INCLUDED + +#include "AttributeArray.h" +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +//////////////////////////////////////// + + +using StringIndexType = uint32_t; + + +namespace attribute_traits +{ + template struct StringTypeTrait { using Type = StringIndexType; }; + template<> struct StringTypeTrait { using Type = uint16_t; }; +} + + +template +struct StringCodec +{ + using ValueType = StringIndexType; + + template + struct Storage { using Type = typename attribute_traits::StringTypeTrait::Type; }; + + template static void decode(const StorageType&, ValueType&); + template static void encode(const ValueType&, StorageType&); + static const char* name() { return Truncate ? "str_trnc" : "str"; } +}; + + +using StringAttributeArray = TypedAttributeArray>; + + +//////////////////////////////////////// + + +class OPENVDB_API StringMetaInserter +{ +public: + StringMetaInserter(MetaMap& metadata); + + /// Insert the string into the metadata + void insert(const Name& name); + + /// Reset the cache from the metadata + void resetCache(); + +private: + MetaMap& mMetadata; + std::vector> mIdBlocks; + std::unordered_set mValues; +}; // StringMetaInserter + + +//////////////////////////////////////// + + +template +template +inline void +StringCodec::decode(const StorageType& data, ValueType& val) +{ + val = static_cast(data); +} + + +template +template +inline void +StringCodec::encode(const ValueType& val, StorageType& data) +{ + data = static_cast(val); +} + + +//////////////////////////////////////// + + +inline bool isString(const AttributeArray& array) +{ + return array.isType(); +} + + +//////////////////////////////////////// + + +class OPENVDB_API StringAttributeHandle +{ +public: + using Ptr = std::shared_ptr;//SharedPtr; + + static Ptr create(const AttributeArray& array, const MetaMap& metadata, const bool preserveCompression = true); + + StringAttributeHandle( const AttributeArray& array, + const MetaMap& metadata, + const bool preserveCompression = true); + + Index stride() const { return mHandle.stride(); } + Index size() const { return mHandle.size(); } + + bool isUniform() const { return mHandle.isUniform(); } + bool hasConstantStride() const { return mHandle.hasConstantStride(); } + + Name get(Index n, Index m = 0) const; + void get(Name& name, Index n, Index m = 0) const; + + /// @brief Returns a reference to the array held in the Handle. + const AttributeArray& array() const; + +protected: + AttributeHandle> mHandle; + const MetaMap& mMetadata; +}; // class StringAttributeHandle + + +//////////////////////////////////////// + + +class OPENVDB_API StringAttributeWriteHandle : public StringAttributeHandle +{ +public: + using Ptr = std::shared_ptr;//SharedPtr; + + static Ptr create(AttributeArray& array, const MetaMap& metadata, const bool expand = true); + + StringAttributeWriteHandle( AttributeArray& array, + const MetaMap& metadata, + const bool expand = true); + + /// @brief If this array is uniform, replace it with an array of length size(). + /// @param fill if true, assign the uniform value to each element of the array. + void expand(bool fill = true); + + /// @brief Set membership for the whole array and attempt to collapse + void collapse(); + /// @brief Set membership for the whole array and attempt to collapse + /// @param name Name of the String + void collapse(const Name& name); + + /// Compact the existing array to become uniform if all values are identical + bool compact(); + + /// @brief Fill the existing array with the given value. + /// @note Identical to collapse() except a non-uniform array will not become uniform. + void fill(const Name& name); + + /// Set the value of the index to @a name + void set(Index n, const Name& name); + void set(Index n, Index m, const Name& name); + + /// Reset the value cache from the metadata + void resetCache(); + + /// @brief Returns a reference to the array held in the Write Handle. + AttributeArray& array(); + + /// @brief Returns whether or not the metadata cache contains a given value. + /// @param name Name of the String. + bool contains(const Name& name) const; + +private: + /// Retrieve the index of this string value from the cache + /// @note throws if name does not exist in cache + Index getIndex(const Name& name) const; + + using ValueMap = std::unordered_map; + + ValueMap mCache; + AttributeWriteHandle> mWriteHandle; +}; // class StringAttributeWriteHandle + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_ATTRIBUTE_ARRAY_STRING_HAS_BEEN_INCLUDED + diff --git a/openvdb/points/AttributeGroup.cc b/openvdb/points/AttributeGroup.cc new file mode 100644 index 00000000..f88d4d11 --- /dev/null +++ b/openvdb/points/AttributeGroup.cc @@ -0,0 +1,116 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/AttributeGroup.cc + +#include "AttributeGroup.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +//////////////////////////////////////// + +// GroupHandle implementation + + +GroupHandle::GroupHandle(const GroupAttributeArray& array, const GroupType& offset) + : mArray(array) + , mBitMask(static_cast(1 << offset)) +{ + assert(isGroup(mArray)); + + // load data if delay-loaded + + mArray.loadData(); +} + + +GroupHandle::GroupHandle(const GroupAttributeArray& array, const GroupType& bitMask, + BitMask) + : mArray(array) + , mBitMask(bitMask) +{ + assert(isGroup(mArray)); + + // load data if delay-loaded + + mArray.loadData(); +} + + +bool GroupHandle::get(Index n) const +{ + return (mArray.get(n) & mBitMask) == mBitMask; +} + + +bool GroupHandle::getUnsafe(Index n) const +{ + return (mArray.getUnsafe(n) & mBitMask) == mBitMask; +} + + +//////////////////////////////////////// + +// GroupWriteHandle implementation + + +GroupWriteHandle::GroupWriteHandle(GroupAttributeArray& array, const GroupType& offset) + : GroupHandle(array, offset) +{ + assert(isGroup(mArray)); +} + + +void GroupWriteHandle::set(Index n, bool on) +{ + const GroupType& value = mArray.get(n); + + GroupAttributeArray& array(const_cast(mArray)); + + if (on) array.set(n, value | mBitMask); + else array.set(n, value & ~mBitMask); +} + + +bool GroupWriteHandle::collapse(bool on) +{ + using ValueT = GroupAttributeArray::ValueType; + + GroupAttributeArray& array(const_cast(mArray)); + + array.compact(); + + if (this->isUniform()) { + if (on) array.collapse(static_cast(array.get(0) | mBitMask)); + else array.collapse(static_cast(array.get(0) & ~mBitMask)); + return true; + } + + for (Index i = 0; i < array.size(); i++) { + if (on) array.set(i, static_cast(array.get(i) | mBitMask)); + else array.set(i, static_cast(array.get(i) & ~mBitMask)); + } + + return false; +} + + +bool GroupWriteHandle::compact() +{ + GroupAttributeArray& array(const_cast(mArray)); + + return array.compact(); +} + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/points/AttributeGroup.h b/openvdb/points/AttributeGroup.h new file mode 100644 index 00000000..f99aab5b --- /dev/null +++ b/openvdb/points/AttributeGroup.h @@ -0,0 +1,173 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/AttributeGroup.h +/// +/// @author Dan Bailey +/// +/// @brief Attribute Group access and filtering for iteration. + +#ifndef OPENVDB_POINTS_ATTRIBUTE_GROUP_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_ATTRIBUTE_GROUP_HAS_BEEN_INCLUDED + +#include "AttributeArray.h" +#include "AttributeSet.h" +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +using GroupType = uint8_t; + + +//////////////////////////////////////// + + +struct GroupCodec +{ + using StorageType = GroupType; + using ValueType = GroupType; + + template + struct Storage { using Type = StorageType; }; + + static void decode(const StorageType&, ValueType&); + static void encode(const ValueType&, StorageType&); + static const char* name() { return "grp"; } +}; + + +using GroupAttributeArray = TypedAttributeArray; + + +//////////////////////////////////////// + + +inline void +GroupCodec::decode(const StorageType& data, ValueType& val) +{ + val = data; +} + + +inline void +GroupCodec::encode(const ValueType& val, StorageType& data) +{ + data = val; +} + + +//////////////////////////////////////// + + +inline bool isGroup(const AttributeArray& array) +{ + return array.isType(); +} + + +//////////////////////////////////////// + + +class OPENVDB_API GroupHandle +{ +public: + using Ptr = std::shared_ptr; + + // Dummy class that distinguishes an offset from a bitmask on construction + struct BitMask { }; + + using GroupIndex = std::pair; + + GroupHandle(const GroupAttributeArray& array, const GroupType& offset); + GroupHandle(const GroupAttributeArray& array, const GroupType& bitMask, BitMask); + + Index size() const { return mArray.size(); } + bool isUniform() const { return mArray.isUniform(); } + + bool get(Index n) const; + bool getUnsafe(Index n) const; + +protected: + const GroupAttributeArray& mArray; + const GroupType mBitMask; +}; // class GroupHandle + + +//////////////////////////////////////// + + +class OPENVDB_API GroupWriteHandle : public GroupHandle +{ +public: + using Ptr = std::shared_ptr; + + GroupWriteHandle(GroupAttributeArray& array, const GroupType& offset); + + void set(Index n, bool on); + + /// @brief Set membership for the whole array and attempt to collapse + /// + /// @param on True or false for inclusion in group + /// + /// @note This method guarantees that all attributes will have group membership + /// changed according to the input bool, however compaction will not be performed + /// if other groups that share the same underlying array are non-uniform. + /// The return value indicates if the group array ends up being uniform. + bool collapse(bool on); + + /// Compact the existing array to become uniform if all values are identical + bool compact(); + +}; // class GroupWriteHandle + + +//////////////////////////////////////// + + +/// Index filtering on group membership +class GroupFilter +{ +public: + GroupFilter(const Name& name, const AttributeSet& attributeSet) + : mIndex(attributeSet.groupIndex(name)) { } + + explicit GroupFilter(const AttributeSet::Descriptor::GroupIndex& index) + : mIndex(index) { } + + inline bool initialized() const { return bool(mHandle); } + + static index::State state() { return index::PARTIAL; } + template + static index::State state(const LeafT&) { return index::PARTIAL; } + + template + void reset(const LeafT& leaf) { + mHandle.reset(new GroupHandle(leaf.groupHandle(mIndex))); + } + + template + bool valid(const IterT& iter) const { + assert(mHandle); + return mHandle->getUnsafe(*iter); + } + +private: + const AttributeSet::Descriptor::GroupIndex mIndex; + GroupHandle::Ptr mHandle; +}; // class GroupFilter + + +//////////////////////////////////////// + + +} // namespace points + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_POINTS_ATTRIBUTE_GROUP_HAS_BEEN_INCLUDED diff --git a/openvdb/points/AttributeSet.cc b/openvdb/points/AttributeSet.cc new file mode 100644 index 00000000..406b2eb2 --- /dev/null +++ b/openvdb/points/AttributeSet.cc @@ -0,0 +1,1179 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/AttributeSet.cc + +#include "AttributeSet.h" +#include "AttributeGroup.h" + +#include // std::equal +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +namespace { + // remove the items from the vector corresponding to the indices + template + void eraseIndices(std::vector& vec, + const std::vector& indices) + { + // early-exit if no indices to erase + + if (indices.empty()) return; + + // build the sorted, unique indices to remove + + std::vector toRemove(indices); + std::sort(toRemove.rbegin(), toRemove.rend()); + toRemove.erase(unique(toRemove.begin(), toRemove.end()), toRemove.end()); + + // throw if the largest index is out of range + + if (*toRemove.begin() >= vec.size()) { + OPENVDB_THROW(LookupError, "Cannot erase indices as index is out of range.") + } + + // erase elements from the back + + for (auto it = toRemove.cbegin(); it != toRemove.cend(); ++it) { + vec.erase(vec.begin() + (*it)); + } + } + + // return true if a string begins with a particular substring + bool startsWith(const std::string& str, const std::string& prefix) + { + return str.compare(0, prefix.length(), prefix) == 0; + } +} + +//////////////////////////////////////// + + +// AttributeSet implementation + + +AttributeSet::AttributeSet() + : mDescr(new Descriptor()) +{ +} + + +AttributeSet::AttributeSet(const AttributeSet& attrSet, Index arrayLength, + const AttributeArray::ScopedRegistryLock* lock) + : mDescr(attrSet.descriptorPtr()) + , mAttrs(attrSet.descriptor().size(), AttributeArray::Ptr()) +{ + std::unique_ptr localLock; + if (!lock) { + localLock.reset(new AttributeArray::ScopedRegistryLock); + lock = localLock.get(); + } + + for (const auto& namePos : mDescr->map()) { + const size_t& pos = namePos.second; + const AttributeArray* existingArray = attrSet.getConst(pos); + const bool constantStride = existingArray->hasConstantStride(); + const Index stride = constantStride ? existingArray->stride() : existingArray->dataSize(); + + AttributeArray::Ptr array = AttributeArray::create(mDescr->type(pos), arrayLength, + stride, constantStride, lock); + + // transfer hidden and transient flags + if (existingArray->isHidden()) array->setHidden(true); + if (existingArray->isTransient()) array->setTransient(true); + + mAttrs[pos] = array; + } +} + + +AttributeSet::AttributeSet(const DescriptorPtr& descr, Index arrayLength, + const AttributeArray::ScopedRegistryLock* lock) + : mDescr(descr) + , mAttrs(descr->size(), AttributeArray::Ptr()) +{ + std::unique_ptr localLock; + if (!lock) { + localLock.reset(new AttributeArray::ScopedRegistryLock); + lock = localLock.get(); + } + + for (const auto& namePos : mDescr->map()) { + const size_t& pos = namePos.second; + mAttrs[pos] = AttributeArray::create(mDescr->type(pos), arrayLength, + /*stride=*/1, /*constantStride=*/true, lock); + } +} + + +AttributeSet::AttributeSet(const AttributeSet& rhs) + : mDescr(rhs.mDescr) + , mAttrs(rhs.mAttrs) +{ +} + + +size_t +AttributeSet::memUsage() const +{ + size_t bytes = sizeof(*this) + mDescr->memUsage(); + for (const auto& attr : mAttrs) { + bytes += attr->memUsage(); + } + return bytes; +} + + +size_t +AttributeSet::find(const std::string& name) const +{ + return mDescr->find(name); +} + + +size_t +AttributeSet::replace(const std::string& name, const AttributeArray::Ptr& attr) +{ + const size_t pos = this->find(name); + return pos != INVALID_POS ? this->replace(pos, attr) : INVALID_POS; +} + + +size_t +AttributeSet::replace(size_t pos, const AttributeArray::Ptr& attr) +{ + assert(pos != INVALID_POS); + assert(pos < mAttrs.size()); + + if (attr->type() != mDescr->type(pos)) { + return INVALID_POS; + } + + mAttrs[pos] = attr; + return pos; +} + + +const AttributeArray* +AttributeSet::getConst(const std::string& name) const +{ + const size_t pos = this->find(name); + if (pos < mAttrs.size()) return this->getConst(pos); + return nullptr; +} + + +const AttributeArray* +AttributeSet::get(const std::string& name) const +{ + return this->getConst(name); +} + + +AttributeArray* +AttributeSet::get(const std::string& name) +{ + const size_t pos = this->find(name); + if (pos < mAttrs.size()) return this->get(pos); + return nullptr; +} + + +const AttributeArray* +AttributeSet::getConst(size_t pos) const +{ + assert(pos != INVALID_POS); + assert(pos < mAttrs.size()); + return mAttrs[pos].get(); +} + + +const AttributeArray* +AttributeSet::get(size_t pos) const +{ + assert(pos != INVALID_POS); + assert(pos < mAttrs.size()); + return this->getConst(pos); +} + + +AttributeArray* +AttributeSet::get(size_t pos) +{ + makeUnique(pos); + return mAttrs[pos].get(); +} + + +size_t +AttributeSet::groupOffset(const Name& group) const +{ + return mDescr->groupOffset(group); +} + + +size_t +AttributeSet::groupOffset(const Util::GroupIndex& index) const +{ + return mDescr->groupOffset(index); +} + + +AttributeSet::Descriptor::GroupIndex +AttributeSet::groupIndex(const Name& group) const +{ + return mDescr->groupIndex(group); +} + + +AttributeSet::Descriptor::GroupIndex +AttributeSet::groupIndex(const size_t offset) const +{ + return mDescr->groupIndex(offset); +} + + +bool +AttributeSet::isShared(size_t pos) const +{ + assert(pos != INVALID_POS); + assert(pos < mAttrs.size()); + return !mAttrs[pos].unique(); +} + + +void +AttributeSet::makeUnique(size_t pos) +{ + assert(pos != INVALID_POS); + assert(pos < mAttrs.size()); + if (!mAttrs[pos].unique()) { + mAttrs[pos] = mAttrs[pos]->copy(); + } +} + + +AttributeArray::Ptr +AttributeSet::appendAttribute( const Name& name, + const NamePair& type, + const Index strideOrTotalSize, + const bool constantStride, + Metadata::Ptr defaultValue) +{ + Descriptor::Ptr descriptor = mDescr->duplicateAppend(name, type); + + // store the attribute default value in the descriptor metadata + if (defaultValue) descriptor->setDefaultValue(name, *defaultValue); + + // extract the index from the descriptor + const size_t pos = descriptor->find(name); + + return this->appendAttribute(*mDescr, descriptor, pos, strideOrTotalSize, constantStride); +} + + +AttributeArray::Ptr +AttributeSet::appendAttribute( const Descriptor& expected, DescriptorPtr& replacement, + const size_t pos, const Index strideOrTotalSize, const bool constantStride, + const AttributeArray::ScopedRegistryLock* lock) +{ + // ensure the descriptor is as expected + if (*mDescr != expected) { + OPENVDB_THROW(LookupError, "Cannot append attributes as descriptors do not match.") + } + + assert(replacement->size() >= mDescr->size()); + + const size_t offset = mDescr->size(); + + // extract the array length from the first attribute array if it exists + + const Index arrayLength = offset > 0 ? this->get(0)->size() : 1; + + // extract the type from the descriptor + + const NamePair& type = replacement->type(pos); + + // append the new array + + AttributeArray::Ptr array = AttributeArray::create( + type, arrayLength, strideOrTotalSize, constantStride, lock); + + // if successful, update Descriptor and append the created array + + mDescr = replacement; + + mAttrs.push_back(array); + + return array; +} + + +void +AttributeSet::dropAttributes(const std::vector& pos) +{ + if (pos.empty()) return; + + Descriptor::Ptr descriptor = mDescr->duplicateDrop(pos); + + this->dropAttributes(pos, *mDescr, descriptor); +} + + +void +AttributeSet::dropAttributes( const std::vector& pos, + const Descriptor& expected, DescriptorPtr& replacement) +{ + if (pos.empty()) return; + + // ensure the descriptor is as expected + if (*mDescr != expected) { + OPENVDB_THROW(LookupError, "Cannot drop attributes as descriptors do not match.") + } + + mDescr = replacement; + + eraseIndices(mAttrs, pos); + + // remove any unused default values + + mDescr->pruneUnusedDefaultValues(); +} + + +void +AttributeSet::renameAttributes(const Descriptor& expected, const DescriptorPtr& replacement) +{ + // ensure the descriptor is as expected + if (*mDescr != expected) { + OPENVDB_THROW(LookupError, "Cannot rename attribute as descriptors do not match.") + } + + mDescr = replacement; +} + + +void +AttributeSet::reorderAttributes(const DescriptorPtr& replacement) +{ + if (*mDescr == *replacement) { + this->resetDescriptor(replacement); + return; + } + + if (!mDescr->hasSameAttributes(*replacement)) { + OPENVDB_THROW(LookupError, "Cannot reorder attributes as descriptors do not contain the same attributes.") + } + + AttrArrayVec attrs(replacement->size()); + + // compute target indices for attributes from the given decriptor + for (const auto& namePos : mDescr->map()) { + const size_t index = replacement->find(namePos.first); + attrs[index] = AttributeArray::Ptr(mAttrs[namePos.second]); + } + + // copy the ordering to the member attributes vector and update descriptor to be target + std::copy(attrs.begin(), attrs.end(), mAttrs.begin()); + mDescr = replacement; +} + + +void +AttributeSet::resetDescriptor(const DescriptorPtr& replacement, const bool allowMismatchingDescriptors) +{ + // ensure the descriptors match + if (!allowMismatchingDescriptors && *mDescr != *replacement) { + OPENVDB_THROW(LookupError, "Cannot swap descriptor as replacement does not match.") + } + + mDescr = replacement; +} + + +void +AttributeSet::read(std::istream& is) +{ + this->readDescriptor(is); + this->readMetadata(is); + this->readAttributes(is); +} + + +void +AttributeSet::write(std::ostream& os, bool outputTransient) const +{ + this->writeDescriptor(os, outputTransient); + this->writeMetadata(os, outputTransient); + this->writeAttributes(os, outputTransient); +} + + +void +AttributeSet::readDescriptor(std::istream& is) +{ + mDescr->read(is); +} + + +void +AttributeSet::writeDescriptor(std::ostream& os, bool outputTransient) const +{ + // build a vector of all attribute arrays that have a transient flag + // unless also writing transient attributes + + std::vector transientArrays; + + if (!outputTransient) { + for (size_t i = 0; i < size(); i++) { + const AttributeArray* array = this->getConst(i); + if (array->isTransient()) { + transientArrays.push_back(i); + } + } + } + + // write out a descriptor without transient attributes + + if (transientArrays.empty()) { + mDescr->write(os); + } + else { + Descriptor::Ptr descr = mDescr->duplicateDrop(transientArrays); + descr->write(os); + } +} + + +void +AttributeSet::readMetadata(std::istream& is) +{ + AttrArrayVec(mDescr->size()).swap(mAttrs); // allocate vector + + for (size_t n = 0, N = mAttrs.size(); n < N; ++n) { + mAttrs[n] = AttributeArray::create(mDescr->type(n), 1, 1); + mAttrs[n]->readMetadata(is); + } +} + + +void +AttributeSet::writeMetadata(std::ostream& os, bool outputTransient, bool paged) const +{ + // write attribute metadata + + for (size_t i = 0; i < size(); i++) { + const AttributeArray* array = this->getConst(i); + array->writeMetadata(os, outputTransient, paged); + } +} + + +void +AttributeSet::readAttributes(std::istream& is) +{ + for (size_t i = 0; i < mAttrs.size(); i++) { + mAttrs[i]->readBuffers(is); + } +} + + +void +AttributeSet::writeAttributes(std::ostream& os, bool outputTransient) const +{ + for (auto attr : mAttrs) { + attr->writeBuffers(os, outputTransient); + } +} + + +bool +AttributeSet::operator==(const AttributeSet& other) const { + if(*this->mDescr != *other.mDescr) return false; + if(this->mAttrs.size() != other.mAttrs.size()) return false; + + for (size_t n = 0; n < this->mAttrs.size(); ++n) { + if (*this->mAttrs[n] != *other.mAttrs[n]) return false; + } + return true; +} + +//////////////////////////////////////// + +// AttributeSet::Descriptor implementation + + +AttributeSet::Descriptor::Descriptor() +{ +} + + +AttributeSet::Descriptor::Descriptor(const Descriptor& rhs) + : mNameMap(rhs.mNameMap) + , mTypes(rhs.mTypes) + , mGroupMap(rhs.mGroupMap) + , mMetadata(rhs.mMetadata) +{ +} + + +bool +AttributeSet::Descriptor::operator==(const Descriptor& rhs) const +{ + if (this == &rhs) return true; + + if (mTypes.size() != rhs.mTypes.size() || + mNameMap.size() != rhs.mNameMap.size() || + mGroupMap.size() != rhs.mGroupMap.size()) { + return false; + } + + for (size_t n = 0, N = mTypes.size(); n < N; ++n) { + if (mTypes[n] != rhs.mTypes[n]) return false; + } + + if (this->mMetadata != rhs.mMetadata) return false; + + return std::equal(mGroupMap.begin(), mGroupMap.end(), rhs.mGroupMap.begin()) && + std::equal(mNameMap.begin(), mNameMap.end(), rhs.mNameMap.begin()); +} + + +bool +AttributeSet::Descriptor::hasSameAttributes(const Descriptor& rhs) const +{ + if (this == &rhs) return true; + + if (mTypes.size() != rhs.mTypes.size() || + mNameMap.size() != rhs.mNameMap.size() || + mGroupMap.size() != rhs.mGroupMap.size()) { + return false; + } + + for (const auto& namePos : mNameMap) { + const size_t index = rhs.find(namePos.first); + + if (index == INVALID_POS) return false; + + if (mTypes[namePos.second] != rhs.mTypes[index]) return false; + } + + return std::equal(mGroupMap.begin(), mGroupMap.end(), rhs.mGroupMap.begin()); +} + + +size_t +AttributeSet::Descriptor::count(const NamePair& matchType) const +{ + return std::count(mTypes.begin(), mTypes.end(), matchType); +} + + +size_t +AttributeSet::Descriptor::memUsage() const +{ + size_t bytes = sizeof(NameToPosMap::mapped_type) * this->size(); + for (const auto& namePos : mNameMap) { + bytes += namePos.first.capacity(); + } + + for (const NamePair& type : mTypes) { + bytes += type.first.capacity(); + bytes += type.second.capacity(); + } + + return sizeof(*this) + bytes; +} + + +size_t +AttributeSet::Descriptor::find(const std::string& name) const +{ + auto it = mNameMap.find(name); + if (it != mNameMap.end()) { + return it->second; + } + return INVALID_POS; +} + + +size_t +AttributeSet::Descriptor::rename(const std::string& fromName, const std::string& toName) +{ + if (!validName(toName)) throw RuntimeError("Attribute name contains invalid characters - " + toName); + + size_t pos = INVALID_POS; + + // check if the new name is already used. + auto it = mNameMap.find(toName); + if (it != mNameMap.end()) return pos; + + it = mNameMap.find(fromName); + if (it != mNameMap.end()) { + pos = it->second; + mNameMap.erase(it); + mNameMap[toName] = pos; + + // rename default value if it exists + + std::stringstream ss; + ss << "default:" << fromName; + + Metadata::Ptr defaultValue = mMetadata[ss.str()]; + + if (defaultValue) { + mMetadata.removeMeta(ss.str()); + ss.str(""); + ss << "default:" << toName; + mMetadata.insertMeta(ss.str(), *defaultValue); + } + } + return pos; +} + +size_t +AttributeSet::Descriptor::renameGroup(const std::string& fromName, const std::string& toName) +{ + if (!validName(toName)) throw RuntimeError("Group name contains invalid characters - " + toName); + + size_t pos = INVALID_POS; + + // check if the new name is already used. + auto it = mGroupMap.find(toName); + if (it != mGroupMap.end()) return pos; + + it = mGroupMap.find(fromName); + if (it != mGroupMap.end()) { + pos = it->second; + mGroupMap.erase(it); + mGroupMap[toName] = pos; + } + + return pos; +} + + +const Name& +AttributeSet::Descriptor::valueType(size_t pos) const +{ + // pos is assumed to exist + return this->type(pos).first; +} + + +const NamePair& +AttributeSet::Descriptor::type(size_t pos) const +{ + // assert that pos is valid and in-range + + assert(pos != AttributeSet::INVALID_POS); + assert(pos < mTypes.size()); + + return mTypes[pos]; +} + + +MetaMap& +AttributeSet::Descriptor::getMetadata() +{ + return mMetadata; +} + + +const MetaMap& +AttributeSet::Descriptor::getMetadata() const +{ + return mMetadata; +} + + +bool +AttributeSet::Descriptor::hasDefaultValue(const Name& name) const +{ + std::stringstream ss; + ss << "default:" << name; + + return bool(mMetadata[ss.str()]); +} + + +void +AttributeSet::Descriptor::setDefaultValue(const Name& name, const Metadata& defaultValue) +{ + const size_t pos = find(name); + if (pos == INVALID_POS) { + OPENVDB_THROW(LookupError, "Cannot find attribute name to set default value.") + } + + // check type of metadata matches attribute type + + const Name& valueType = this->valueType(pos); + if (valueType != defaultValue.typeName()) { + OPENVDB_THROW(TypeError, "Mis-matching Default Value Type"); + } + + std::stringstream ss; + ss << "default:" << name; + + mMetadata.insertMeta(ss.str(), defaultValue); +} + + +void +AttributeSet::Descriptor::removeDefaultValue(const Name& name) +{ + std::stringstream ss; + ss << "default:" << name; + + mMetadata.removeMeta(ss.str()); +} + + +void +AttributeSet::Descriptor::pruneUnusedDefaultValues() +{ + // store any default metadata keys for which the attribute name is no longer present + + std::vector metaToErase; + + for (auto it = mMetadata.beginMeta(), itEnd = mMetadata.endMeta(); it != itEnd; ++it) { + const Name name = it->first; + + // ignore non-default metadata + if (!startsWith(name, "default:")) continue; + + const Name defaultName = name.substr(8, it->first.size() - 8); + + if (mNameMap.find(defaultName) == mNameMap.end()) { + metaToErase.push_back(name); + } + } + + // remove this metadata + + for (const Name& name : metaToErase) { + mMetadata.removeMeta(name); + } +} + + +size_t +AttributeSet::Descriptor::insert(const std::string& name, const NamePair& typeName) +{ + if (!validName(name)) throw RuntimeError("Attribute name contains invalid characters - " + name); + + size_t pos = INVALID_POS; + auto it = mNameMap.find(name); + if (it != mNameMap.end()) { + assert(it->second < mTypes.size()); + if (mTypes[it->second] != typeName) { + OPENVDB_THROW(KeyError, + "Cannot insert into a Descriptor with a duplicate name, but different type.") + } + pos = it->second; + } else { + + if (!AttributeArray::isRegistered(typeName)) { + OPENVDB_THROW(KeyError, "Failed to insert '" << name + << "' with unregistered attribute type '" << typeName.first << "_" << typeName.second); + } + + pos = mTypes.size(); + mTypes.push_back(typeName); + mNameMap.insert(it, NameToPosMap::value_type(name, pos)); + } + return pos; +} + +AttributeSet::Descriptor::Ptr +AttributeSet::Descriptor::create(const NameAndTypeVec& attrs, + const NameToPosMap& groupMap, + const MetaMap& metadata) +{ + auto newDescriptor = std::make_shared(); + + for (const NameAndType& attr : attrs) { + newDescriptor->insert(attr.name, attr.type); + } + + newDescriptor->mGroupMap = groupMap; + newDescriptor->mMetadata = metadata; + + return newDescriptor; +} + +AttributeSet::Descriptor::Ptr +AttributeSet::Descriptor::create(const NamePair& positionType) +{ + auto descr = std::make_shared(); + descr->insert("P", positionType); + return descr; +} + +AttributeSet::Descriptor::Ptr +AttributeSet::Descriptor::duplicateAppend(const Name& name, const NamePair& type) const +{ + Inserter attributes; + + this->appendTo(attributes.vec); + attributes.add(NameAndType(name, type)); + + return Descriptor::create(attributes.vec, mGroupMap, mMetadata); +} + + +AttributeSet::Descriptor::Ptr +AttributeSet::Descriptor::duplicateDrop(const std::vector& pos) const +{ + NameAndTypeVec vec; + this->appendTo(vec); + + Descriptor::Ptr descriptor; + + // If groups exist, check to see if those arrays are being dropped + + if (!mGroupMap.empty()) { + + // extract all attribute array group indices and specific groups + // to drop + + std::vector groups, groupsToDrop; + for (size_t i = 0; i < vec.size(); i++) { + if (vec[i].type == GroupAttributeArray::attributeType()) { + groups.push_back(i); + if (std::find(pos.begin(), pos.end(), i) != pos.end()) { + groupsToDrop.push_back(i); + } + } + } + + // drop the indices in indices from vec + + eraseIndices(vec, pos); + + if (!groupsToDrop.empty()) { + + // configure group mapping if group arrays have been dropped + + NameToPosMap droppedGroupMap = mGroupMap; + + const size_t GROUP_BITS = sizeof(GroupType) * CHAR_BIT; + for (auto iter = droppedGroupMap.begin(); iter != droppedGroupMap.end();) { + const size_t groupArrayPos = iter->second / GROUP_BITS; + const size_t arrayPos = groups[groupArrayPos]; + if (std::find(pos.begin(), pos.end(), arrayPos) != pos.end()) { + iter = droppedGroupMap.erase(iter); + } + else { + size_t offset(0); + for (const size_t& idx : groupsToDrop) { + if (idx >= arrayPos) break; + ++offset; + } + iter->second -= (offset * GROUP_BITS); + ++iter; + } + } + + descriptor = Descriptor::create(vec, droppedGroupMap, mMetadata); + + // remove any unused default values + + descriptor->pruneUnusedDefaultValues(); + + return descriptor; + } + } + else { + + // drop the indices in pos from vec + + eraseIndices(vec, pos); + } + + descriptor = Descriptor::create(vec, mGroupMap, mMetadata); + + // remove any unused default values + + descriptor->pruneUnusedDefaultValues(); + + return descriptor; +} + +void +AttributeSet::Descriptor::appendTo(NameAndTypeVec& attrs) const +{ + // build a std::map (ie key and value swapped) + + using PosToNameMap = std::map; + + PosToNameMap posToNameMap; + + for (const auto& namePos : mNameMap) { + posToNameMap[namePos.second] = namePos.first; + } + + // std::map is sorted by key, so attributes can now be inserted in position order + + for (const auto& posName : posToNameMap) { + attrs.emplace_back(posName.second, this->type(posName.first)); + } +} + +bool +AttributeSet::Descriptor::hasGroup(const Name& group) const +{ + return mGroupMap.find(group) != mGroupMap.end(); +} + +void +AttributeSet::Descriptor::setGroup(const Name& group, const size_t offset) +{ + if (!validName(group)) throw RuntimeError("Group name contains invalid characters - " + group); + + mGroupMap[group] = offset; +} + +void +AttributeSet::Descriptor::dropGroup(const Name& group) +{ + mGroupMap.erase(group); +} + +void +AttributeSet::Descriptor::clearGroups() +{ + mGroupMap.clear(); +} + +const Name +AttributeSet::Descriptor::uniqueName(const Name& name) const +{ + auto it = mNameMap.find(name); + if (it == mNameMap.end()) return name; + + std::ostringstream ss; + size_t i(0); + while (it != mNameMap.end()) { + ss.str(""); + ss << name << i++; + it = mNameMap.find(ss.str()); + } + return ss.str(); +} + +const Name +AttributeSet::Descriptor::uniqueGroupName(const Name& name) const +{ + auto it = mGroupMap.find(name); + if (it == mGroupMap.end()) return name; + + std::ostringstream ss; + size_t i(0); + while (it != mGroupMap.end()) { + ss.str(""); + ss << name << i++; + it = mGroupMap.find(ss.str()); + } + return ss.str(); +} + +size_t +AttributeSet::Descriptor::groupOffset(const Name& group) const +{ + const auto it = mGroupMap.find(group); + if (it == mGroupMap.end()) { + return INVALID_POS; + } + return it->second; +} + +size_t +AttributeSet::Descriptor::groupOffset(const Util::GroupIndex& index) const +{ + if (index.first >= mNameMap.size()) { + OPENVDB_THROW(LookupError, "Out of range group index.") + } + + if (mTypes[index.first] != GroupAttributeArray::attributeType()) { + OPENVDB_THROW(LookupError, "Group index invalid.") + } + + // find the relative index in the group attribute arrays + + size_t relativeIndex = 0; + for (const auto& namePos : mNameMap) { + if (namePos.second < index.first && + mTypes[namePos.second] == GroupAttributeArray::attributeType()) { + relativeIndex++; + } + } + + const size_t GROUP_BITS = sizeof(GroupType) * CHAR_BIT; + + return (relativeIndex * GROUP_BITS) + index.second; +} + +AttributeSet::Descriptor::GroupIndex +AttributeSet::Descriptor::groupIndex(const Name& group) const +{ + const size_t offset = this->groupOffset(group); + if (offset == INVALID_POS) { + OPENVDB_THROW(LookupError, "Group not found - " << group << ".") + } + return this->groupIndex(offset); +} + + +AttributeSet::Descriptor::GroupIndex +AttributeSet::Descriptor::groupIndex(const size_t offset) const +{ + // extract all attribute array group indices + + const size_t GROUP_BITS = sizeof(GroupType) * CHAR_BIT; + + std::vector groups; + for (const auto& namePos : mNameMap) { + if (mTypes[namePos.second] == GroupAttributeArray::attributeType()) { + groups.push_back(namePos.second); + } + } + + if (offset >= groups.size() * GROUP_BITS) { + OPENVDB_THROW(LookupError, "Out of range group offset - " << offset << ".") + } + + // adjust relative offset to find offset into the array vector + + std::sort(groups.begin(), groups.end()); + return Util::GroupIndex(groups[offset / GROUP_BITS], + static_cast(offset % GROUP_BITS)); +} + +bool +AttributeSet::Descriptor::validName(const Name& name) +{ + if (name.empty()) return false; + return std::find_if(name.begin(), name.end(), + [&](int c) { return !(isalnum(c) || (c == '_') || (c == '|') || (c == ':')); } ) == name.end(); +} + +void +AttributeSet::Descriptor::parseNames( std::vector& includeNames, + std::vector& excludeNames, + bool& includeAll, + const std::string& nameStr) +{ + includeAll = false; + + std::stringstream tokenStream(nameStr); + + Name token; + while (tokenStream >> token) { + + bool negate = startsWith(token, "^") || startsWith(token, "!"); + + if (negate) { + if (token.length() < 2) throw RuntimeError("Negate character (^) must prefix a name."); + token = token.substr(1, token.length()-1); + if (!validName(token)) throw RuntimeError("Name contains invalid characters - " + token); + excludeNames.push_back(token); + } + else if (!includeAll) { + if (token == "*") { + includeAll = true; + includeNames.clear(); + continue; + } + if (!validName(token)) throw RuntimeError("Name contains invalid characters - " + token); + includeNames.push_back(token); + } + } +} + +void +AttributeSet::Descriptor::parseNames( std::vector& includeNames, + std::vector& excludeNames, + const std::string& nameStr) +{ + bool includeAll = false; + Descriptor::parseNames(includeNames, excludeNames, includeAll, nameStr); +} + +void +AttributeSet::Descriptor::write(std::ostream& os) const +{ + const Index64 arraylength = Index64(mTypes.size()); + os.write(reinterpret_cast(&arraylength), sizeof(Index64)); + + for (const NamePair& np : mTypes) { + writeString(os, np.first); + writeString(os, np.second); + } + + for (auto it = mNameMap.begin(), endIt = mNameMap.end(); it != endIt; ++it) { + writeString(os, it->first); + os.write(reinterpret_cast(&it->second), sizeof(Index64)); + } + + const Index64 grouplength = Index64(mGroupMap.size()); + os.write(reinterpret_cast(&grouplength), sizeof(Index64)); + + for (auto groupIt = mGroupMap.cbegin(), endGroupIt = mGroupMap.cend(); groupIt != endGroupIt; ++groupIt) { + writeString(os, groupIt->first); + os.write(reinterpret_cast(&groupIt->second), sizeof(Index64)); + } + + mMetadata.writeMeta(os); +} + + +void +AttributeSet::Descriptor::read(std::istream& is) +{ + Index64 arraylength = 0; + is.read(reinterpret_cast(&arraylength), sizeof(Index64)); + + std::vector(size_t(arraylength)).swap(mTypes); + + for (NamePair& np : mTypes) { + np.first = readString(is); + np.second = readString(is); + } + + mNameMap.clear(); + std::pair nameAndOffset; + + for (Index64 n = 0; n < arraylength; ++n) { + nameAndOffset.first = readString(is); + if (!validName(nameAndOffset.first)) throw IoError("Attribute name contains invalid characters - " + nameAndOffset.first); + is.read(reinterpret_cast(&nameAndOffset.second), sizeof(Index64)); + mNameMap.insert(nameAndOffset); + } + + Index64 grouplength = 0; + is.read(reinterpret_cast(&grouplength), sizeof(Index64)); + + for (Index64 n = 0; n < grouplength; ++n) { + nameAndOffset.first = readString(is); + if (!validName(nameAndOffset.first)) throw IoError("Group name contains invalid characters - " + nameAndOffset.first); + is.read(reinterpret_cast(&nameAndOffset.second), sizeof(Index64)); + mGroupMap.insert(nameAndOffset); + } + + mMetadata.readMeta(is); +} + + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/points/AttributeSet.h b/openvdb/points/AttributeSet.h new file mode 100644 index 00000000..dc8ba450 --- /dev/null +++ b/openvdb/points/AttributeSet.h @@ -0,0 +1,465 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/AttributeSet.h +/// +/// @authors Dan Bailey, Mihai Alden +/// +/// @brief Set of Attribute Arrays which tracks metadata about each array. + +#ifndef OPENVDB_POINTS_ATTRIBUTE_SET_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_ATTRIBUTE_SET_HAS_BEEN_INCLUDED + +#include "AttributeArray.h" +#include +#include + +#include +#include +#include + + +class TestAttributeSet; + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +//////////////////////////////////////// + + +/// Ordered collection of uniquely-named attribute arrays +class OPENVDB_API AttributeSet +{ +public: + enum { INVALID_POS = std::numeric_limits::max() }; + + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + class Descriptor; + + using DescriptorPtr = std::shared_ptr; + using DescriptorConstPtr = std::shared_ptr; + + ////////// + + struct Util + { + /// Attribute and type name pair. + struct NameAndType { + NameAndType(const std::string& n, const NamePair& t, const Index s = 1) + : name(n), type(t), stride(s) {} + Name name; + NamePair type; + Index stride; + }; + + using NameAndTypeVec = std::vector; + using NameToPosMap = std::map; + using GroupIndex = std::pair; + }; + + ////////// + + AttributeSet(); + + /// Construct a new AttributeSet from the given AttributeSet. + /// @param attributeSet the old attribute set + /// @param arrayLength the desired length of the arrays in the new AttributeSet + /// @param lock an optional scoped registry lock to avoid contention + /// @note This constructor is typically used to resize an existing AttributeSet as + /// it transfers attribute metadata such as hidden and transient flags + AttributeSet(const AttributeSet& attributeSet, Index arrayLength, + const AttributeArray::ScopedRegistryLock* lock = nullptr); + + /// Construct a new AttributeSet from the given Descriptor. + /// @param descriptor stored in the new AttributeSet and used in construction + /// @param arrayLength the desired length of the arrays in the new AttributeSet + /// @param lock an optional scoped registry lock to avoid contention + /// @note Descriptors do not store attribute metadata such as hidden and transient flags + /// which live on the AttributeArrays, so for constructing from an existing AttributeSet + /// use the AttributeSet(const AttributeSet&, Index) constructor instead + AttributeSet(const DescriptorPtr& descriptor, Index arrayLength = 1, + const AttributeArray::ScopedRegistryLock* lock = nullptr); + + /// Shallow copy constructor, the descriptor and attribute arrays will be shared. + AttributeSet(const AttributeSet&); + + /// Disallow copy assignment, since it wouldn't be obvious whether the copy is deep or shallow. + AttributeSet& operator=(const AttributeSet&) = delete; + + //@{ + /// @brief Return a reference to this attribute set's descriptor, which might + /// be shared with other sets. + Descriptor& descriptor() { return *mDescr; } + const Descriptor& descriptor() const { return *mDescr; } + //@} + + /// @brief Return a pointer to this attribute set's descriptor, which might be + /// shared with other sets + DescriptorPtr descriptorPtr() const { return mDescr; } + + /// Return the number of attributes in this set. + size_t size() const { return mAttrs.size(); } + + /// Return the number of bytes of memory used by this attribute set. + size_t memUsage() const; + + /// @brief Return the position of the attribute array whose name is @a name, + /// or @c INVALID_POS if no match is found. + size_t find(const std::string& name) const; + + /// @brief Replace the attribute array whose name is @a name. + /// @return The position of the updated attribute array or @c INVALID_POS + /// if the given name does not exist or if the replacement failed because + /// the new array type does not comply with the descriptor. + size_t replace(const std::string& name, const AttributeArray::Ptr&); + + /// @brief Replace the attribute array stored at position @a pos in this container. + /// @return The position of the updated attribute array or @c INVALID_POS + /// if replacement failed because the new array type does not comply with + /// the descriptor. + size_t replace(size_t pos, const AttributeArray::Ptr&); + + //@{ + /// @brief Return a pointer to the attribute array whose name is @a name or + /// a null pointer if no match is found. + const AttributeArray* getConst(const std::string& name) const; + const AttributeArray* get(const std::string& name) const; + AttributeArray* get(const std::string& name); + //@} + + //@{ + /// @brief Return a pointer to the attribute array stored at position @a pos + /// in this set. + const AttributeArray* getConst(size_t pos) const; + const AttributeArray* get(size_t pos) const; + AttributeArray* get(size_t pos); + //@} + + //@{ + /// @brief Return the group offset from the name or index of the group + /// A group attribute array is a single byte (8-bit), each bit of which + /// can denote a group. The group offset is the position of the bit that + /// denotes the requested group if all group attribute arrays in the set + /// (and only attribute arrays marked as group) were to be laid out linearly + /// according to their order in the set. + size_t groupOffset(const Name& groupName) const; + size_t groupOffset(const Util::GroupIndex& index) const; + //@} + + /// Return the group index from the name of the group + Util::GroupIndex groupIndex(const Name& groupName) const; + /// Return the group index from the offset of the group + /// @note see offset description for groupOffset() + Util::GroupIndex groupIndex(const size_t offset) const; + + /// Return true if the attribute array stored at position @a pos is shared. + bool isShared(size_t pos) const; + /// @brief If the attribute array stored at position @a pos is shared, + /// replace the array with a deep copy of itself that is not + /// shared with anyone else. + void makeUnique(size_t pos); + + /// Append attribute @a attribute (simple method) + AttributeArray::Ptr appendAttribute(const Name& name, + const NamePair& type, + const Index strideOrTotalSize = 1, + const bool constantStride = true, + Metadata::Ptr defaultValue = Metadata::Ptr()); + + /// Append attribute @a attribute (descriptor-sharing) + /// Requires current descriptor to match @a expected + /// On append, current descriptor is replaced with @a replacement + /// Provide a @a lock object to avoid contention from appending in parallel + AttributeArray::Ptr appendAttribute(const Descriptor& expected, DescriptorPtr& replacement, + const size_t pos, const Index strideOrTotalSize = 1, + const bool constantStride = true, + const AttributeArray::ScopedRegistryLock* lock = nullptr); + + /// Drop attributes with @a pos indices (simple method) + /// Creates a new descriptor for this attribute set + void dropAttributes(const std::vector& pos); + + /// Drop attributes with @a pos indices (descriptor-sharing method) + /// Requires current descriptor to match @a expected + /// On drop, current descriptor is replaced with @a replacement + void dropAttributes(const std::vector& pos, + const Descriptor& expected, DescriptorPtr& replacement); + + /// Re-name attributes in set to match a provided descriptor + /// Replaces own descriptor with @a replacement + void renameAttributes(const Descriptor& expected, const DescriptorPtr& replacement); + + /// Re order attribute set to match a provided descriptor + /// Replaces own descriptor with @a replacement + void reorderAttributes(const DescriptorPtr& replacement); + + /// Replace the current descriptor with a @a replacement + /// Note the provided Descriptor must be identical to the replacement + /// unless @a allowMismatchingDescriptors is true (default is false) + void resetDescriptor(const DescriptorPtr& replacement, const bool allowMismatchingDescriptors = false); + + /// Read the entire set from a stream. + void read(std::istream&); + /// Write the entire set to a stream. + /// @param outputTransient if true, write out transient attributes + void write(std::ostream&, bool outputTransient = false) const; + + /// This will read the attribute descriptor from a stream. + void readDescriptor(std::istream&); + /// This will write the attribute descriptor to a stream. + /// @param outputTransient if true, write out transient attributes + void writeDescriptor(std::ostream&, bool outputTransient = false) const; + + /// This will read the attribute metadata from a stream. + void readMetadata(std::istream&); + /// This will write the attribute metadata to a stream. + /// @param outputTransient if true, write out transient attributes + /// @param paged if true, data is written out in pages + void writeMetadata(std::ostream&, bool outputTransient = false, bool paged = false) const; + + /// This will read the attribute data from a stream. + void readAttributes(std::istream&); + /// This will write the attribute data to a stream. + /// @param outputTransient if true, write out transient attributes + void writeAttributes(std::ostream&, bool outputTransient = false) const; + + /// Compare the descriptors and attribute arrays on the attribute sets + /// Exit early if the descriptors do not match + bool operator==(const AttributeSet& other) const; + bool operator!=(const AttributeSet& other) const { return !this->operator==(other); } + +private: + using AttrArrayVec = std::vector; + + DescriptorPtr mDescr; + AttrArrayVec mAttrs; +}; // class AttributeSet + +//////////////////////////////////////// + + +/// A container for ABI=5 to help ease introduction of upcoming features +#if OPENVDB_ABI_VERSION_NUMBER >= 5 +namespace future { + class Container + { + class Element { }; + std::vector> mElements; + }; +} +#endif + + +//////////////////////////////////////// + + +/// @brief An immutable object that stores name, type and AttributeSet position +/// for a constant collection of attribute arrays. +/// @note The attribute name is actually mutable, but the attribute type +/// and position can not be changed after creation. +class OPENVDB_API AttributeSet::Descriptor +{ +public: + using Ptr = std::shared_ptr; + + using NameAndType = Util::NameAndType; + using NameAndTypeVec = Util::NameAndTypeVec; + using GroupIndex = Util::GroupIndex; + using NameToPosMap = Util::NameToPosMap; + using ConstIterator = NameToPosMap::const_iterator; + + /// Utility method to construct a NameAndType sequence. + struct Inserter { + NameAndTypeVec vec; + Inserter& add(const NameAndType& nameAndType) { + vec.push_back(nameAndType); return *this; + } + Inserter& add(const Name& name, const NamePair& type) { + vec.emplace_back(name, type); return *this; + } + Inserter& add(const NameAndTypeVec& other) { + for (NameAndTypeVec::const_iterator it = other.begin(), itEnd = other.end(); it != itEnd; ++it) { + vec.emplace_back(it->name, it->type); + } + return *this; + } + }; + + ////////// + + Descriptor(); + + /// Copy constructor + Descriptor(const Descriptor&); + + /// Create a new descriptor from a position attribute type and assumes "P" (for convenience). + static Ptr create(const NamePair&); + + /// Create a new descriptor as a duplicate with a new attribute appended + Ptr duplicateAppend(const Name& name, const NamePair& type) const; + + /// Create a new descriptor as a duplicate with existing attributes dropped + Ptr duplicateDrop(const std::vector& pos) const; + + /// Return the number of attributes in this descriptor. + size_t size() const { return mTypes.size(); } + + /// Return the number of attributes with this attribute type + size_t count(const NamePair& type) const; + + /// Return the number of bytes of memory used by this attribute set. + size_t memUsage() const; + + /// @brief Return the position of the attribute array whose name is @a name, + /// or @c INVALID_POS if no match is found. + size_t find(const std::string& name) const; + + /// Rename an attribute array + size_t rename(const std::string& fromName, const std::string& toName); + + /// Return the name of the attribute array's type. + const Name& valueType(size_t pos) const; + /// Return the name of the attribute array's type. + const NamePair& type(size_t pos) const; + + /// Retrieve metadata map + MetaMap& getMetadata(); + const MetaMap& getMetadata() const; + + /// Return true if the attribute has a default value + bool hasDefaultValue(const Name& name) const; + /// Get a default value for an existing attribute + template + ValueType getDefaultValue(const Name& name) const + { + const size_t pos = find(name); + if (pos == INVALID_POS) { + OPENVDB_THROW(LookupError, "Cannot find attribute name to set default value.") + } + + std::stringstream ss; + ss << "default:" << name; + + auto metadata = mMetadata.getMetadata>(ss.str()); + + if (metadata) return metadata->value(); + + return zeroVal(); + } + /// Set a default value for an existing attribute + void setDefaultValue(const Name& name, const Metadata& defaultValue); + // Remove the default value if it exists + void removeDefaultValue(const Name& name); + // Prune any default values for which the key is no longer present + void pruneUnusedDefaultValues(); + + /// Return true if this descriptor is equal to the given one. + bool operator==(const Descriptor&) const; + /// Return true if this descriptor is not equal to the given one. + bool operator!=(const Descriptor& rhs) const { return !this->operator==(rhs); } + /// Return true if this descriptor contains the same attributes + /// as the given descriptor, ignoring attribute order + bool hasSameAttributes(const Descriptor& rhs) const; + + /// Return a reference to the name-to-position map. + const NameToPosMap& map() const { return mNameMap; } + /// Return a reference to the name-to-position group map. + const NameToPosMap& groupMap() const { return mGroupMap; } + + /// Return @c true if group exists + bool hasGroup(const Name& group) const; + /// Define a group name to offset mapping + void setGroup(const Name& group, const size_t offset); + /// Drop any mapping keyed by group name + void dropGroup(const Name& group); + /// Clear all groups + void clearGroups(); + /// Rename a group + size_t renameGroup(const std::string& fromName, const std::string& toName); + /// Return a unique name for a group based on given name + const Name uniqueGroupName(const Name& name) const; + + //@{ + /// @brief Return the group offset from the name or index of the group + /// A group attribute array is a single byte (8-bit), each bit of which + /// can denote a group. The group offset is the position of the bit that + /// denotes the requested group if all group attribute arrays in the set + /// (and only attribute arrays marked as group) were to be laid out linearly + /// according to their order in the set. + size_t groupOffset(const Name& groupName) const; + size_t groupOffset(const GroupIndex& index) const; + //@} + + /// Return the group index from the name of the group + GroupIndex groupIndex(const Name& groupName) const; + /// Return the group index from the offset of the group + /// @note see offset description for groupOffset() + GroupIndex groupIndex(const size_t offset) const; + + /// Return a unique name for an attribute array based on given name + const Name uniqueName(const Name& name) const; + + /// Return true if the name is valid + static bool validName(const Name& name); + + /// @brief Extract each name from @a nameStr into @a includeNames, or into @a excludeNames + /// if the name is prefixed with a caret. + /// @param nameStr the input string of names + /// @param includeNames on exit, the list of names that are not prefixed with a caret + /// @param excludeNames on exit, the list of names that are prefixed with a caret + /// @param includeAll on exit, @c true if a "*" wildcard is present in the @a includeNames + static void parseNames( std::vector& includeNames, + std::vector& excludeNames, + bool& includeAll, + const std::string& nameStr); + + /// @brief Extract each name from @a nameStr into @a includeNames, or into @a excludeNames + /// if the name is prefixed with a caret. + static void parseNames( std::vector& includeNames, + std::vector& excludeNames, + const std::string& nameStr); + + /// Serialize this descriptor to the given stream. + void write(std::ostream&) const; + /// Unserialize this transform from the given stream. + void read(std::istream&); + +protected: + /// Append to a vector of names and types from this Descriptor in position order + void appendTo(NameAndTypeVec& attrs) const; + + /// Create a new descriptor from the given attribute and type name pairs + /// and copy the group maps and metamap. + static Ptr create(const NameAndTypeVec&, const NameToPosMap&, const MetaMap&); + + size_t insert(const std::string& name, const NamePair& typeName); + +private: + friend class ::TestAttributeSet; + + NameToPosMap mNameMap; + std::vector mTypes; + NameToPosMap mGroupMap; + MetaMap mMetadata; +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + // as this change is part of an ABI change, there's no good reason to reduce the reserved + // space aside from keeping the memory size of an AttributeSet the same for convenience + // (note that this assumes a typical three-pointer implementation for std::vector) + future::Container mFutureContainer; // occupies 3 reserved slots + int64_t mReserved[5]; // for future use +#else + int64_t mReserved[8]; // for future use +#endif +}; // class Descriptor + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED diff --git a/openvdb/points/IndexFilter.h b/openvdb/points/IndexFilter.h new file mode 100644 index 00000000..b2ab9f50 --- /dev/null +++ b/openvdb/points/IndexFilter.h @@ -0,0 +1,581 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/IndexFilter.h +/// +/// @author Dan Bailey +/// +/// @brief Index filters primarily designed to be used with a FilterIndexIter. +/// +/// Filters must adhere to the interface described in the example below: +/// @code +/// struct MyFilter +/// { +/// // Return true when the filter has been initialized for first use +/// bool initialized() { return true; } +/// +/// // Return index::ALL if all points are valid, index::NONE if no points are valid +/// // and index::PARTIAL if some points are valid +/// index::State state() { return index::PARTIAL; } +/// +/// // Return index::ALL if all points in this leaf are valid, index::NONE if no points +/// // in this leaf are valid and index::PARTIAL if some points in this leaf are valid +/// template +/// index::State state(const LeafT&) { return index::PARTIAL; } +/// +/// // Resets the filter to refer to the specified leaf, all subsequent valid() calls +/// // will be relative to this leaf until reset() is called with a different leaf. +/// // Although a required method, many filters will provide an empty implementation if +/// // there is no leaf-specific logic needed. +/// template void reset(const LeafT&) { } +/// +/// // Returns true if the filter is valid for the supplied iterator +/// template bool valid(const IterT&) { return true; } +/// }; +/// @endcode + +#ifndef OPENVDB_POINTS_INDEX_FILTER_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_INDEX_FILTER_HAS_BEEN_INCLUDED + +#include +#include + +#include +#include + +#include "IndexIterator.h" +#include "AttributeArray.h" +#include "AttributeGroup.h" +#include "AttributeSet.h" + +#include // std::mt19937 +#include // std::iota +#include + + +class TestIndexFilter; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +//////////////////////////////////////// + + + +namespace index_filter_internal { + + +// generate a random subset of n indices from the range [0:m] +template +std::vector +generateRandomSubset(const unsigned int seed, const IntType n, const IntType m) +{ + if (n <= 0) return std::vector(); + + // fill vector with ascending indices + std::vector values(m); + std::iota(values.begin(), values.end(), 0); + if (n >= m) return values; + + // shuffle indices using random generator + + RandGenT randGen(seed); + std::shuffle(values.begin(), values.end(), randGen); + + // resize the container to n elements + values.resize(n); + + // sort the subset of the indices vector that will be used + std::sort(values.begin(), values.end()); + + return values; +} + + +} // namespace index_filter_internal + + +/// Index filtering on active / inactive state of host voxel +template +class ValueMaskFilter +{ +public: + static bool initialized() { return true; } + static index::State state() { return index::PARTIAL; } + template + static index::State state(const LeafT& leaf) + { + if (leaf.isDense()) return On ? index::ALL : index::NONE; + else if (leaf.isEmpty()) return On ? index::NONE : index::ALL; + return index::PARTIAL; + } + + template + void reset(const LeafT&) { } + + template + bool valid(const IterT& iter) const + { + const bool valueOn = iter.isValueOn(); + return On ? valueOn : !valueOn; + } +}; + + +using ActiveFilter = ValueMaskFilter; +using InactiveFilter = ValueMaskFilter; + + +/// Index filtering on multiple group membership for inclusion and exclusion +/// +/// @note include filters are applied first, then exclude filters +class MultiGroupFilter +{ +public: + using NameVector = std::vector; + using IndexVector = std::vector; + using HandleVector = std::vector; + +private: + static IndexVector namesToIndices(const AttributeSet& attributeSet, const NameVector& names) { + IndexVector indices; + for (const auto& name : names) { + try { + indices.emplace_back(attributeSet.groupIndex(name)); + } catch (LookupError&) { + // silently drop group names that don't exist + } + } + return indices; + } + +public: + MultiGroupFilter( const NameVector& include, + const NameVector& exclude, + const AttributeSet& attributeSet) + : mInclude(MultiGroupFilter::namesToIndices(attributeSet, include)) + , mExclude(MultiGroupFilter::namesToIndices(attributeSet, exclude)) { } + + MultiGroupFilter( const IndexVector& include, + const IndexVector& exclude) + : mInclude(include) + , mExclude(exclude) { } + + MultiGroupFilter( const MultiGroupFilter& filter) + : mInclude(filter.mInclude) + , mExclude(filter.mExclude) + , mIncludeHandles(filter.mIncludeHandles) + , mExcludeHandles(filter.mExcludeHandles) + , mInitialized(filter.mInitialized) { } + + inline bool initialized() const { return mInitialized; } + + inline index::State state() const + { + return (mInclude.empty() && mExclude.empty()) ? index::ALL : index::PARTIAL; + } + + template + static index::State state(const LeafT&) { return index::PARTIAL; } + + template + void reset(const LeafT& leaf) { + mIncludeHandles.clear(); + mExcludeHandles.clear(); + for (const auto& i : mInclude) { + mIncludeHandles.emplace_back(leaf.groupHandle(i)); + } + for (const auto& i : mExclude) { + mExcludeHandles.emplace_back(leaf.groupHandle(i)); + } + mInitialized = true; + } + + template + bool valid(const IterT& iter) const { + assert(mInitialized); + // accept no include filters as valid + bool includeValid = mIncludeHandles.empty(); + for (const GroupHandle& handle : mIncludeHandles) { + if (handle.getUnsafe(*iter)) { + includeValid = true; + break; + } + } + if (!includeValid) return false; + for (const GroupHandle& handle : mExcludeHandles) { + if (handle.getUnsafe(*iter)) return false; + } + return true; + } + +private: + IndexVector mInclude; + IndexVector mExclude; + HandleVector mIncludeHandles; + HandleVector mExcludeHandles; + bool mInitialized = false; +}; // class MultiGroupFilter + + +// Random index filtering per leaf +template +class RandomLeafFilter +{ +public: + using SeedCountPair = std::pair; + using LeafMap = std::unordered_map; + + RandomLeafFilter( const PointDataTreeT& tree, + const Index64 targetPoints, + const unsigned int seed = 0) { + Index64 currentPoints = 0; + for (auto iter = tree.cbeginLeaf(); iter; ++iter) { + currentPoints += iter->pointCount(); + } + + const float factor = targetPoints > currentPoints ? 1.0f : float(targetPoints) / float(currentPoints); + + std::mt19937 generator(seed); + std::uniform_int_distribution dist(0, std::numeric_limits::max() - 1); + + Index32 leafCounter = 0; + float totalPointsFloat = 0.0f; + int totalPoints = 0; + for (auto iter = tree.cbeginLeaf(); iter; ++iter) { + // for the last leaf - use the remaining points to reach the target points + if (leafCounter + 1 == tree.leafCount()) { + const int leafPoints = static_cast(targetPoints) - totalPoints; + mLeafMap[iter->origin()] = SeedCountPair(dist(generator), leafPoints); + break; + } + totalPointsFloat += factor * static_cast(iter->pointCount()); + const auto leafPoints = static_cast(math::Floor(totalPointsFloat)); + totalPointsFloat -= static_cast(leafPoints); + totalPoints += leafPoints; + + mLeafMap[iter->origin()] = SeedCountPair(dist(generator), leafPoints); + + leafCounter++; + } + } + + inline bool initialized() const { return mNextIndex == -1; } + + static index::State state() { return index::PARTIAL; } + template + static index::State state(const LeafT&) { return index::PARTIAL; } + + template + void reset(const LeafT& leaf) { + using index_filter_internal::generateRandomSubset; + + auto it = mLeafMap.find(leaf.origin()); + if (it == mLeafMap.end()) { + OPENVDB_THROW(openvdb::KeyError, + "Cannot find leaf origin in map for random filter - " << leaf.origin()); + } + + const SeedCountPair& value = it->second; + const unsigned int seed = static_cast(value.first); + const auto total = static_cast(leaf.pointCount()); + mCount = std::min(value.second, total); + + mIndices = generateRandomSubset(seed, mCount, total); + + mSubsetOffset = -1; + mNextIndex = -1; + } + + inline void next() const { + mSubsetOffset++; + mNextIndex = mSubsetOffset >= mCount ? + std::numeric_limits::max() : + mIndices[mSubsetOffset]; + } + + template + bool valid(const IterT& iter) const { + const int index = *iter; + while (mNextIndex < index) this->next(); + return mNextIndex == index; + } + +protected: + friend class ::TestIndexFilter; + +private: + LeafMap mLeafMap; + std::vector mIndices; + int mCount = 0; + mutable int mSubsetOffset = -1; + mutable int mNextIndex = -1; +}; // class RandomLeafFilter + + +// Hash attribute value for deterministic, but approximate filtering +template +class AttributeHashFilter +{ +public: + using Handle = AttributeHandle; + + AttributeHashFilter(const size_t index, + const double percentage, + const unsigned int seed = 0) + : mIndex(index) + , mFactor(percentage / 100.0) + , mSeed(seed) { } + + AttributeHashFilter(const AttributeHashFilter& filter) + : mIndex(filter.mIndex) + , mFactor(filter.mFactor) + , mSeed(filter.mSeed) + { + if (filter.mIdHandle) mIdHandle.reset(new Handle(*filter.mIdHandle)); + } + + inline bool initialized() const { return bool(mIdHandle); } + + static index::State state() { return index::PARTIAL; } + template + static index::State state(const LeafT&) { return index::PARTIAL; } + + template + void reset(const LeafT& leaf) { + assert(leaf.hasAttribute(mIndex)); + mIdHandle.reset(new Handle(leaf.constAttributeArray(mIndex))); + } + + template + bool valid(const IterT& iter) const { + assert(mIdHandle); + const IntType id = mIdHandle->get(*iter); + const unsigned int seed = mSeed + static_cast(id); + RandGenT generator(seed); + std::uniform_real_distribution dist(0.0, 1.0); + return dist(generator) < mFactor; + } + +private: + const size_t mIndex; + const double mFactor; + const unsigned int mSeed; + typename Handle::UniquePtr mIdHandle; +}; // class AttributeHashFilter + + +template +class LevelSetFilter +{ +public: + using ValueT = typename LevelSetGridT::ValueType; + using Handle = AttributeHandle; + + LevelSetFilter( const LevelSetGridT& grid, + const math::Transform& transform, + const ValueT min, + const ValueT max) + : mAccessor(grid.getConstAccessor()) + , mLevelSetTransform(grid.transform()) + , mTransform(transform) + , mMin(min) + , mMax(max) { } + + LevelSetFilter(const LevelSetFilter& filter) + : mAccessor(filter.mAccessor) + , mLevelSetTransform(filter.mLevelSetTransform) + , mTransform(filter.mTransform) + , mMin(filter.mMin) + , mMax(filter.mMax) + { + if (filter.mPositionHandle) mPositionHandle.reset(new Handle(*filter.mPositionHandle)); + } + + inline bool initialized() const { return bool(mPositionHandle); } + + static index::State state() { return index::PARTIAL; } + template + static index::State state(const LeafT&) { return index::PARTIAL; } + + template + void reset(const LeafT& leaf) { + mPositionHandle.reset(new Handle(leaf.constAttributeArray("P"))); + } + + template + bool valid(const IterT& iter) const { + assert(mPositionHandle); + assert(iter); + + const openvdb::Coord ijk = iter.getCoord(); + const openvdb::Vec3f voxelIndexSpace = ijk.asVec3d(); + + // Retrieve point position in voxel space + const openvdb::Vec3f& pointVoxelSpace = mPositionHandle->get(*iter); + + // Compute point position in index space + const openvdb::Vec3f pointWorldSpace = mTransform.indexToWorld(pointVoxelSpace + voxelIndexSpace); + const openvdb::Vec3f pointIndexSpace = mLevelSetTransform.worldToIndex(pointWorldSpace); + + // Perform level-set sampling + const typename LevelSetGridT::ValueType value = tools::BoxSampler::sample(mAccessor, pointIndexSpace); + + // if min is greater than max, we invert so that values are valid outside of the range (not inside) + const bool invert = mMin > mMax; + + return invert ? (value < mMax || value > mMin) : (value < mMax && value > mMin); + } + +private: + // not a reference to ensure const-accessor is unique per-thread + const typename LevelSetGridT::ConstAccessor mAccessor; + const math::Transform& mLevelSetTransform; + const math::Transform& mTransform; + const ValueT mMin; + const ValueT mMax; + Handle::UniquePtr mPositionHandle; +}; // class LevelSetFilter + + +// BBox index filtering +class BBoxFilter +{ +public: + using Handle = AttributeHandle; + + BBoxFilter(const openvdb::math::Transform& transform, + const openvdb::BBoxd& bboxWS) + : mTransform(transform) + , mBbox(transform.worldToIndex(bboxWS)) { } + + BBoxFilter(const BBoxFilter& filter) + : mTransform(filter.mTransform) + , mBbox(filter.mBbox) + { + if (filter.mPositionHandle) mPositionHandle.reset(new Handle(*filter.mPositionHandle)); + } + + inline bool initialized() const { return bool(mPositionHandle); } + + inline index::State state() const + { + return mBbox.empty() ? index::NONE : index::PARTIAL; + } + template + static index::State state(const LeafT&) { return index::PARTIAL; } + + template + void reset(const LeafT& leaf) { + mPositionHandle.reset(new Handle(leaf.constAttributeArray("P"))); + } + + template + bool valid(const IterT& iter) const { + assert(mPositionHandle); + + const openvdb::Coord ijk = iter.getCoord(); + const openvdb::Vec3f voxelIndexSpace = ijk.asVec3d(); + + // Retrieve point position in voxel space + const openvdb::Vec3f& pointVoxelSpace = mPositionHandle->get(*iter); + + // Compute point position in index space + const openvdb::Vec3f pointIndexSpace = pointVoxelSpace + voxelIndexSpace; + + return mBbox.isInside(pointIndexSpace); + } + +private: + const openvdb::math::Transform& mTransform; + const openvdb::BBoxd mBbox; + Handle::UniquePtr mPositionHandle; +}; // class BBoxFilter + + +// Index filtering based on evaluating both sub-filters +template +class BinaryFilter +{ +public: + BinaryFilter( const T1& filter1, + const T2& filter2) + : mFilter1(filter1) + , mFilter2(filter2) { } + + inline bool initialized() const { return mFilter1.initialized() && mFilter2.initialized(); } + + inline index::State state() const + { + return this->computeState(mFilter1.state(), mFilter2.state()); + } + template + inline index::State state(const LeafT& leaf) const + { + return this->computeState(mFilter1.state(leaf), mFilter2.state(leaf)); + } + + template + void reset(const LeafT& leaf) { + mFilter1.reset(leaf); + mFilter2.reset(leaf); + } + + template + bool valid(const IterT& iter) const { + if (And) return mFilter1.valid(iter) && mFilter2.valid(iter); + return mFilter1.valid(iter) || mFilter2.valid(iter); + } + +private: + inline index::State computeState( index::State state1, + index::State state2) const + { + if (And) { + if (state1 == index::NONE || state2 == index::NONE) return index::NONE; + else if (state1 == index::ALL && state2 == index::ALL) return index::ALL; + } else { + if (state1 == index::NONE && state2 == index::NONE) return index::NONE; + else if (state1 == index::ALL && state2 == index::ALL) return index::ALL; + } + return index::PARTIAL; + } + + T1 mFilter1; + T2 mFilter2; +}; // class BinaryFilter + + +//////////////////////////////////////// + + +template +struct FilterTraits { + static const bool RequiresCoord = false; +}; +template<> +struct FilterTraits { + static const bool RequiresCoord = true; +}; +template +struct FilterTraits> { + static const bool RequiresCoord = true; +}; +template +struct FilterTraits> { + static const bool RequiresCoord = FilterTraits::RequiresCoord || + FilterTraits::RequiresCoord; +}; + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_INDEX_FILTER_HAS_BEEN_INCLUDED diff --git a/openvdb/points/IndexIterator.h b/openvdb/points/IndexIterator.h new file mode 100644 index 00000000..99ca1dd9 --- /dev/null +++ b/openvdb/points/IndexIterator.h @@ -0,0 +1,329 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/IndexIterator.h +/// +/// @author Dan Bailey +/// +/// @brief Index Iterators. + +#ifndef OPENVDB_POINTS_INDEX_ITERATOR_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_INDEX_ITERATOR_HAS_BEEN_INCLUDED + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +/// @brief Count up the number of times the iterator can iterate +/// +/// @param iter the iterator. +/// +/// @note counting by iteration only performed where a dynamic filter is in use, +template +inline Index64 iterCount(const IterT& iter); + + +//////////////////////////////////////// + + +namespace index { +// Enum for informing early-exit optimizations +// PARTIAL - No optimizations are possible +// NONE - No indices to evaluate, can skip computation +// ALL - All indices to evaluate, can skip filtering +enum State +{ + PARTIAL=0, + NONE, + ALL +}; +} + + +/// @brief A no-op filter that can be used when iterating over all indices +/// @see points/IndexFilter.h for the documented interface for an index filter +class NullFilter +{ +public: + static bool initialized() { return true; } + static index::State state() { return index::ALL; } + template + static index::State state(const LeafT&) { return index::ALL; } + + template void reset(const LeafT&) { } + template static bool valid(const IterT&) { return true; } +}; // class NullFilter + + +/// @brief A forward iterator over array indices in a single voxel +class ValueVoxelCIter +{ +public: + struct Parent + { + Parent() = default; + explicit Parent(Index32 offset): mOffset(offset) { } + Index32 getValue(unsigned /*offset*/) const { return mOffset; } + private: + Index32 mOffset = 0; + }; // struct Parent + + using NodeType = Parent; + + ValueVoxelCIter() = default; + ValueVoxelCIter(Index32 prevOffset, Index32 offset) + : mOffset(offset), mParent(prevOffset) {} + ValueVoxelCIter(const ValueVoxelCIter& other) + : mOffset(other.mOffset), mParent(other.mParent), mValid(other.mValid) {} + + /// @brief Return the item to which this iterator is currently pointing. + Index32 operator*() { return mOffset; } + Index32 operator*() const { return mOffset; } + + /// @brief Advance to the next (valid) item (prefix). + ValueVoxelCIter& operator++() { mValid = false; return *this; } + + operator bool() const { return mValid; } + bool test() const { return mValid; } + Index32 end() const { return mOffset+1; } + + void reset(Index32 /*item*/, Index32 /*end*/) {} + + Parent& parent() { return mParent; } + Index32 offset() { return mOffset; } + inline bool next() { this->operator++(); return this->test(); } + + /// @brief For efficiency, Coord and active state assumed to be readily available + /// when iterating over indices of a single voxel + Coord getCoord [[noreturn]] () const { + OPENVDB_THROW(RuntimeError, "ValueVoxelCIter does not provide a valid Coord."); + } + void getCoord [[noreturn]] (Coord& /*coord*/) const { + OPENVDB_THROW(RuntimeError, "ValueVoxelCIter does not provide a valid Coord."); + } + bool isValueOn [[noreturn]] () const { + OPENVDB_THROW(RuntimeError, "ValueVoxelCIter does not test if voxel is active."); + } + + /// @{ + /// @brief Equality operators + bool operator==(const ValueVoxelCIter& other) const { return mOffset == other.mOffset; } + bool operator!=(const ValueVoxelCIter& other) const { return !this->operator==(other); } + /// @} + +private: + Index32 mOffset = 0; + Parent mParent; + mutable bool mValid = true; +}; // class ValueVoxelCIter + + +/// @brief A forward iterator over array indices with filtering +/// IteratorT can be either IndexIter or ValueIndexIter (or some custom index iterator) +/// FilterT should be a struct or class with a valid() method than can be evaluated per index +/// Here's a simple filter example that only accepts even indices: +/// +/// struct EvenIndexFilter +/// { +/// bool valid(const Index32 offset) const { +/// return (offset % 2) == 0; +/// } +/// }; +/// +template +class IndexIter +{ +public: + /// @brief A forward iterator over array indices from a value iterator (such as ValueOnCIter) + class ValueIndexIter + { + public: + ValueIndexIter(const IteratorT& iter) + : mIter(iter), mParent(&mIter.parent()) + { + if (mIter) { + assert(mParent); + Index32 start = (mIter.offset() > 0 ? + Index32(mParent->getValue(mIter.offset() - 1)) : Index32(0)); + this->reset(start, *mIter); + if (mItem >= mEnd) this->operator++(); + } + } + ValueIndexIter(const ValueIndexIter& other) + : mEnd(other.mEnd), mItem(other.mItem), mIter(other.mIter), mParent(other.mParent) + { + assert(mParent); + } + ValueIndexIter& operator=(const ValueIndexIter&) = default; + + inline Index32 end() const { return mEnd; } + + inline void reset(Index32 item, Index32 end) { + mItem = item; + mEnd = end; + } + + /// @brief Returns the item to which this iterator is currently pointing. + inline Index32 operator*() { assert(mIter); return mItem; } + inline Index32 operator*() const { assert(mIter); return mItem; } + + /// @brief Return @c true if this iterator is not yet exhausted. + inline operator bool() const { return mIter; } + inline bool test() const { return mIter; } + + /// @brief Advance to the next (valid) item (prefix). + inline ValueIndexIter& operator++() { + ++mItem; + while (mItem >= mEnd && mIter.next()) { + assert(mParent); + this->reset(mParent->getValue(mIter.offset() - 1), *mIter); + } + return *this; + } + + /// @brief Advance to the next (valid) item. + inline bool next() { this->operator++(); return this->test(); } + inline bool increment() { this->next(); return this->test(); } + + /// Return the coordinates of the item to which the value iterator is pointing. + inline Coord getCoord() const { assert(mIter); return mIter.getCoord(); } + /// Return in @a xyz the coordinates of the item to which the value iterator is pointing. + inline void getCoord(Coord& xyz) const { assert(mIter); xyz = mIter.getCoord(); } + + /// @brief Return @c true if this iterator is pointing to an active value. + inline bool isValueOn() const { assert(mIter); return mIter.isValueOn(); } + + /// Return the const value iterator + inline const IteratorT& valueIter() const { return mIter; } + + /// @brief Equality operators + bool operator==(const ValueIndexIter& other) const { return mItem == other.mItem; } + bool operator!=(const ValueIndexIter& other) const { return !this->operator==(other); } + + private: + Index32 mEnd = 0; + Index32 mItem = 0; + IteratorT mIter; + const typename IteratorT::NodeType* mParent; + }; // ValueIndexIter + + IndexIter(const IteratorT& iterator, const FilterT& filter) + : mIterator(iterator) + , mFilter(filter) + { + if (!mFilter.initialized()) { + OPENVDB_THROW(RuntimeError, + "Filter needs to be initialized before constructing the iterator."); + } + if (mIterator) { + this->reset(*mIterator, mIterator.end()); + } + } + IndexIter(const IndexIter& other) + : mIterator(other.mIterator) + , mFilter(other.mFilter) + { + if (!mFilter.initialized()) { + OPENVDB_THROW(RuntimeError, + "Filter needs to be initialized before constructing the iterator."); + } + } + IndexIter& operator=(const IndexIter& other) + { + if (&other != this) { + mIterator = other.mIterator; + mFilter = other.mFilter; + if (!mFilter.initialized()) { + OPENVDB_THROW(RuntimeError, + "Filter needs to be initialized before constructing the iterator."); + } + } + return *this; + } + + Index32 end() const { return mIterator.end(); } + + /// @brief Reset the begining and end of the iterator. + void reset(Index32 begin, Index32 end) { + mIterator.reset(begin, end); + while (mIterator.test() && !mFilter.template valid(mIterator)) { + ++mIterator; + } + } + + /// @brief Returns the item to which this iterator is currently pointing. + Index32 operator*() { assert(mIterator); return *mIterator; } + Index32 operator*() const { assert(mIterator); return *mIterator; } + + /// @brief Return @c true if this iterator is not yet exhausted. + operator bool() const { return mIterator.test(); } + bool test() const { return mIterator.test(); } + + /// @brief Advance to the next (valid) item (prefix). + IndexIter& operator++() { + while (true) { + ++mIterator; + if (!mIterator.test() || mFilter.template valid(mIterator)) { + break; + } + } + return *this; + } + + /// @brief Advance to the next (valid) item (postfix). + IndexIter operator++(int /*dummy*/) { + IndexIter newIterator(*this); + this->operator++(); + return newIterator; + } + + /// @brief Advance to the next (valid) item. + bool next() { this->operator++(); return this->test(); } + bool increment() { this->next(); return this->test(); } + + /// Return the const filter + inline const FilterT& filter() const { return mFilter; } + + /// Return the coordinates of the item to which the value iterator is pointing. + inline Coord getCoord() const { assert(mIterator); return mIterator.getCoord(); } + /// Return in @a xyz the coordinates of the item to which the value iterator is pointing. + inline void getCoord(Coord& xyz) const { assert(mIterator); xyz = mIterator.getCoord(); } + + /// @brief Return @c true if the value iterator is pointing to an active value. + inline bool isValueOn() const { assert(mIterator); return mIterator.valueIter().isValueOn(); } + + /// @brief Equality operators + bool operator==(const IndexIter& other) const { return mIterator == other.mIterator; } + bool operator!=(const IndexIter& other) const { return !this->operator==(other); } + +private: + ValueIndexIter mIterator; + FilterT mFilter; +}; // class IndexIter + + +//////////////////////////////////////// + + +template +inline Index64 iterCount(const IterT& iter) +{ + Index64 size = 0; + for (IterT newIter(iter); newIter; ++newIter, ++size) { } + return size; +} + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_INDEX_ITERATOR_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointAdvect.h b/openvdb/points/PointAdvect.h new file mode 100644 index 00000000..53d6242b --- /dev/null +++ b/openvdb/points/PointAdvect.h @@ -0,0 +1,247 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Dan Bailey +/// +/// @file points/PointAdvect.h +/// +/// @brief Ability to advect VDB Points through a velocity field. + +#ifndef OPENVDB_POINTS_POINT_ADVECT_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_ADVECT_HAS_BEEN_INCLUDED + +#include +#include +#include + +#include +#include +#include +#include + +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +/// @brief Advect points in a PointDataGrid through a velocity grid +/// @param points the PointDataGrid containing the points to be advected. +/// @param velocity a velocity grid to be sampled. +/// @param integrationOrder the integration scheme to use (1 is forward euler, 4 is runge-kutta 4th) +/// @param dt delta time. +/// @param timeSteps number of advection steps to perform. +/// @param advectFilter an optional advection index filter (moves a subset of the points) +/// @param filter an optional index filter (deletes a subset of the points) +/// @param cached caches velocity interpolation for faster performance, disable to use +/// less memory (default is on). +template +inline void advectPoints(PointDataGridT& points, const VelGridT& velocity, + const Index integrationOrder, const double dt, const Index timeSteps, + const AdvectFilterT& advectFilter = NullFilter(), + const FilterT& filter = NullFilter(), + const bool cached = true); + + +//////////////////////////////////////// + + +namespace point_advect_internal { + +enum IntegrationOrder { + INTEGRATION_ORDER_FWD_EULER = 1, + INTEGRATION_ORDER_RK_2ND, + INTEGRATION_ORDER_RK_3RD, + INTEGRATION_ORDER_RK_4TH +}; + +template +class AdvectionDeformer +{ +public: + using IntegratorT = openvdb::tools::VelocityIntegrator; + + AdvectionDeformer(const VelGridT& velocityGrid, const double timeStep, const int steps, + const FilterT& filter) + : mIntegrator(velocityGrid) + , mTimeStep(timeStep) + , mSteps(steps) + , mFilter(filter) { } + + template + void reset(const LeafT& leaf, size_t /*idx*/) + { + mFilter.reset(leaf); + } + + template + void apply(Vec3d& position, const IndexIterT& iter) const + { + if (mFilter.valid(iter)) { + for (int n = 0; n < mSteps; ++n) { + mIntegrator.template rungeKutta( + static_cast(mTimeStep), position); + } + } + } + +private: + IntegratorT mIntegrator; + double mTimeStep; + const int mSteps; + FilterT mFilter; +}; // class AdvectionDeformer + + +template +struct AdvectionOp +{ + using CachedDeformerT = CachedDeformer; + + AdvectionOp(PointDataGridT& points, const VelGridT& velocity, + const Index integrationOrder, const double timeStep, const Index steps, + const AdvectFilterT& advectFilter, + const FilterT& filter) + : mPoints(points) + , mVelocity(velocity) + , mIntegrationOrder(integrationOrder) + , mTimeStep(timeStep) + , mSteps(steps) + , mAdvectFilter(advectFilter) + , mFilter(filter) { } + + void cache() + { + mCachedDeformer.reset(new CachedDeformerT(mCache)); + (*this)(true); + } + + void advect() + { + (*this)(false); + } + +private: + template + void resolveIntegrationOrder(bool buildCache) + { + const auto leaf = mPoints.constTree().cbeginLeaf(); + if (!leaf) return; + + // move points according to the pre-computed cache + if (!buildCache && mCachedDeformer) { + movePoints(mPoints, *mCachedDeformer, mFilter); + return; + } + + NullFilter nullFilter; + + if (buildCache) { + // disable group filtering from the advection deformer and perform group filtering + // in the cache deformer instead, this restricts the cache to just containing + // positions from points which are both deforming *and* are not being deleted + AdvectionDeformer deformer( + mVelocity, mTimeStep, mSteps, nullFilter); + if (mFilter.state() == index::ALL && mAdvectFilter.state() == index::ALL) { + mCachedDeformer->evaluate(mPoints, deformer, nullFilter); + } else { + BinaryFilter binaryFilter( + mAdvectFilter, mFilter); + mCachedDeformer->evaluate(mPoints, deformer, binaryFilter); + } + } + else { + // revert to NullFilter if all points are being evaluated + if (mAdvectFilter.state() == index::ALL) { + AdvectionDeformer deformer( + mVelocity, mTimeStep, mSteps, nullFilter); + movePoints(mPoints, deformer, mFilter); + } + else { + AdvectionDeformer deformer( + mVelocity, mTimeStep, mSteps, mAdvectFilter); + movePoints(mPoints, deformer, mFilter); + } + } + } + + template + void resolveStaggered(bool buildCache) + { + if (mIntegrationOrder == INTEGRATION_ORDER_FWD_EULER) { + resolveIntegrationOrder<1, Staggered>(buildCache); + } else if (mIntegrationOrder == INTEGRATION_ORDER_RK_2ND) { + resolveIntegrationOrder<2, Staggered>(buildCache); + } else if (mIntegrationOrder == INTEGRATION_ORDER_RK_3RD) { + resolveIntegrationOrder<3, Staggered>(buildCache); + } else if (mIntegrationOrder == INTEGRATION_ORDER_RK_4TH) { + resolveIntegrationOrder<4, Staggered>(buildCache); + } + } + + void operator()(bool buildCache) + { + // early-exit if no leafs + if (mPoints.constTree().leafCount() == 0) return; + + if (mVelocity.getGridClass() == openvdb::GRID_STAGGERED) { + resolveStaggered(buildCache); + } else { + resolveStaggered(buildCache); + } + } + + PointDataGridT& mPoints; + const VelGridT& mVelocity; + const Index mIntegrationOrder; + const double mTimeStep; + const Index mSteps; + const AdvectFilterT& mAdvectFilter; + const FilterT& mFilter; + CachedDeformerT::Cache mCache; + std::unique_ptr mCachedDeformer; +}; // struct AdvectionOp + +} // namespace point_advect_internal + + +//////////////////////////////////////// + + +template +inline void advectPoints(PointDataGridT& points, const VelGridT& velocity, + const Index integrationOrder, const double timeStep, const Index steps, + const AdvectFilterT& advectFilter, + const FilterT& filter, + const bool cached) +{ + using namespace point_advect_internal; + + if (steps == 0) return; + + if (integrationOrder > 4) { + throw ValueError{"Unknown integration order for advecting points."}; + } + + AdvectionOp op( + points, velocity, integrationOrder, timeStep, steps, + advectFilter, filter); + + // if caching is enabled, sample the velocity field using a CachedDeformer to store the + // intermediate positions before moving the points, this uses more memory but typically + // results in faster overall performance + if (cached) op.cache(); + + // advect the points + op.advect(); +} + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_POINT_ADVECT_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointAttribute.h b/openvdb/points/PointAttribute.h new file mode 100644 index 00000000..4282a95c --- /dev/null +++ b/openvdb/points/PointAttribute.h @@ -0,0 +1,549 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Dan Bailey, Khang Ngo +/// +/// @file points/PointAttribute.h +/// +/// @brief Point attribute manipulation in a VDB Point Grid. + +#ifndef OPENVDB_POINTS_POINT_ATTRIBUTE_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_ATTRIBUTE_HAS_BEEN_INCLUDED + +#include + +#include "AttributeArrayString.h" +#include "AttributeSet.h" +#include "AttributeGroup.h" +#include "PointDataGrid.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + +namespace point_attribute_internal { + +template +struct Default +{ + static inline ValueType value() { return zeroVal(); } +}; + +} // namespace point_attribute_internal + + +/// @brief Appends a new attribute to the VDB tree +/// (this method does not require a templated AttributeType) +/// +/// @param tree the PointDataTree to be appended to. +/// @param name name for the new attribute. +/// @param type the type of the attibute. +/// @param strideOrTotalSize the stride of the attribute +/// @param constantStride if @c false, stride is interpreted as total size of the array +/// @param metaDefaultValue metadata default attribute value +/// @param hidden mark attribute as hidden +/// @param transient mark attribute as transient +template +inline void appendAttribute(PointDataTreeT& tree, + const Name& name, + const NamePair& type, + const Index strideOrTotalSize = 1, + const bool constantStride = true, + Metadata::Ptr metaDefaultValue = Metadata::Ptr(), + const bool hidden = false, + const bool transient = false); + +/// @brief Appends a new attribute to the VDB tree. +/// +/// @param tree the PointDataTree to be appended to. +/// @param name name for the new attribute +/// @param uniformValue the initial value of the attribute +/// @param strideOrTotalSize the stride of the attribute +/// @param constantStride if @c false, stride is interpreted as total size of the array +/// @param metaDefaultValue metadata default attribute value +/// @param hidden mark attribute as hidden +/// @param transient mark attribute as transient +template +inline void appendAttribute(PointDataTreeT& tree, + const std::string& name, + const ValueType& uniformValue = + point_attribute_internal::Default::value(), + const Index strideOrTotalSize = 1, + const bool constantStride = true, + Metadata::Ptr metaDefaultValue = Metadata::Ptr(), + const bool hidden = false, + const bool transient = false); + +/// @brief Collapse the attribute into a uniform value +/// +/// @param tree the PointDataTree in which to collapse the attribute. +/// @param name name for the attribute. +/// @param uniformValue value of the attribute +template +inline void collapseAttribute( PointDataTreeT& tree, + const Name& name, + const ValueType& uniformValue = + point_attribute_internal::Default::value()); + +/// @brief Drops attributes from the VDB tree. +/// +/// @param tree the PointDataTree to be dropped from. +/// @param indices indices of the attributes to drop. +template +inline void dropAttributes( PointDataTreeT& tree, + const std::vector& indices); + +/// @brief Drops attributes from the VDB tree. +/// +/// @param tree the PointDataTree to be dropped from. +/// @param names names of the attributes to drop. +template +inline void dropAttributes( PointDataTreeT& tree, + const std::vector& names); + +/// @brief Drop one attribute from the VDB tree (convenience method). +/// +/// @param tree the PointDataTree to be dropped from. +/// @param index index of the attribute to drop. +template +inline void dropAttribute( PointDataTreeT& tree, + const size_t& index); + +/// @brief Drop one attribute from the VDB tree (convenience method). +/// +/// @param tree the PointDataTree to be dropped from. +/// @param name name of the attribute to drop. +template +inline void dropAttribute( PointDataTreeT& tree, + const Name& name); + +/// @brief Rename attributes in a VDB tree. +/// +/// @param tree the PointDataTree. +/// @param oldNames a list of old attribute names to rename from. +/// @param newNames a list of new attribute names to rename to. +/// +/// @note Number of oldNames must match the number of newNames. +/// +/// @note Duplicate names and renaming group attributes are not allowed. +template +inline void renameAttributes(PointDataTreeT& tree, + const std::vector& oldNames, + const std::vector& newNames); + +/// @brief Rename an attribute in a VDB tree. +/// +/// @param tree the PointDataTree. +/// @param oldName the old attribute name to rename from. +/// @param newName the new attribute name to rename to. +/// +/// @note newName must not already exist and must not be a group attribute. +template +inline void renameAttribute(PointDataTreeT& tree, + const Name& oldName, + const Name& newName); + +/// @brief Compact attributes in a VDB tree (if possible). +/// +/// @param tree the PointDataTree. +template +inline void compactAttributes(PointDataTreeT& tree); + + +//////////////////////////////////////// + + +namespace point_attribute_internal { + + +template +inline void collapseAttribute(AttributeArray& array, + const AttributeSet::Descriptor&, const ValueType& uniformValue) +{ + AttributeWriteHandle handle(array); + handle.collapse(uniformValue); +} + + +inline void collapseAttribute(AttributeArray& array, + const AttributeSet::Descriptor& descriptor, const Name& uniformValue) +{ + StringAttributeWriteHandle handle(array, descriptor.getMetadata()); + handle.collapse(uniformValue); +} + + +//////////////////////////////////////// + + +template +struct AttributeTypeConversion +{ + static const NamePair& type() { + return TypedAttributeArray::attributeType(); + } +}; + + +template +struct AttributeTypeConversion +{ + static const NamePair& type() { return StringAttributeArray::attributeType(); } +}; + + +//////////////////////////////////////// + + +template +struct MetadataStorage +{ + static void add(PointDataTreeT&, const ValueType&) {} + + template + static void add(PointDataTreeT&, const AttributeListType&) {} +}; + + +template +struct MetadataStorage +{ + static void add(PointDataTreeT& tree, const Name& uniformValue) { + MetaMap& metadata = makeDescriptorUnique(tree)->getMetadata(); + StringMetaInserter inserter(metadata); + inserter.insert(uniformValue); + } + + template + static void add(PointDataTreeT& tree, const AttributeListType& data) { + MetaMap& metadata = makeDescriptorUnique(tree)->getMetadata(); + StringMetaInserter inserter(metadata); + Name value; + + for (size_t i = 0; i < data.size(); i++) { + data.get(value, i); + inserter.insert(value); + } + } +}; + + +} // namespace point_attribute_internal + + +//////////////////////////////////////// + + +template +inline void appendAttribute(PointDataTreeT& tree, + const Name& name, + const NamePair& type, + const Index strideOrTotalSize, + const bool constantStride, + Metadata::Ptr metaDefaultValue, + const bool hidden, + const bool transient) +{ + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + // do not append a non-unique attribute + + const auto& descriptor = iter->attributeSet().descriptor(); + const size_t index = descriptor.find(name); + + if (index != AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, + "Cannot append an attribute with a non-unique name - " << name << "."); + } + + // create a new attribute descriptor + + auto newDescriptor = descriptor.duplicateAppend(name, type); + + // store the attribute default value in the descriptor metadata + + if (metaDefaultValue) { + newDescriptor->setDefaultValue(name, *metaDefaultValue); + } + + // extract new pos + + const size_t pos = newDescriptor->find(name); + + // acquire registry lock to avoid locking when appending attributes in parallel + + AttributeArray::ScopedRegistryLock lock; + + // insert attributes using the new descriptor + + tree::LeafManager leafManager(tree); + leafManager.foreach( + [&](typename PointDataTree::LeafNodeType& leaf, size_t /*idx*/) { + auto expected = leaf.attributeSet().descriptorPtr(); + + auto attribute = leaf.appendAttribute(*expected, newDescriptor, + pos, strideOrTotalSize, constantStride, &lock); + + if (hidden) attribute->setHidden(true); + if (transient) attribute->setTransient(true); + }, /*threaded=*/ true + ); +} + + +//////////////////////////////////////// + + +template +inline void appendAttribute(PointDataTreeT& tree, + const std::string& name, + const ValueType& uniformValue, + const Index strideOrTotalSize, + const bool constantStride, + Metadata::Ptr metaDefaultValue, + const bool hidden, + const bool transient) +{ + static_assert(!std::is_base_of::value, + "ValueType must not be derived from AttributeArray"); + + using point_attribute_internal::AttributeTypeConversion; + using point_attribute_internal::Default; + using point_attribute_internal::MetadataStorage; + + appendAttribute(tree, name, AttributeTypeConversion::type(), + strideOrTotalSize, constantStride, metaDefaultValue, hidden, transient); + + if (!math::isExactlyEqual(uniformValue, Default::value())) { + MetadataStorage::add(tree, uniformValue); + collapseAttribute(tree, name, uniformValue); + } +} + + +//////////////////////////////////////// + + +template +inline void collapseAttribute( PointDataTreeT& tree, + const Name& name, + const ValueType& uniformValue) +{ + static_assert(!std::is_base_of::value, + "ValueType must not be derived from AttributeArray"); + + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const auto& descriptor = iter->attributeSet().descriptor(); + + // throw if attribute name does not exist + + const size_t index = descriptor.find(name); + if (index == AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, "Cannot find attribute name in PointDataTree."); + } + + tree::LeafManager leafManager(tree); + leafManager.foreach( + [&](typename PointDataTree::LeafNodeType& leaf, size_t /*idx*/) { + assert(leaf.hasAttribute(index)); + AttributeArray& array = leaf.attributeArray(index); + point_attribute_internal::collapseAttribute( + array, descriptor, uniformValue); + }, /*threaded=*/true + ); +} + + +//////////////////////////////////////// + + +template +inline void dropAttributes( PointDataTreeT& tree, + const std::vector& indices) +{ + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const auto& descriptor = iter->attributeSet().descriptor(); + + // throw if position index present in the indices as this attribute is mandatory + + const size_t positionIndex = descriptor.find("P"); + if (positionIndex!= AttributeSet::INVALID_POS && + std::find(indices.begin(), indices.end(), positionIndex) != indices.end()) { + OPENVDB_THROW(KeyError, "Cannot drop mandatory position attribute."); + } + + // insert attributes using the new descriptor + + auto newDescriptor = descriptor.duplicateDrop(indices); + + tree::LeafManager leafManager(tree); + leafManager.foreach( + [&](typename PointDataTree::LeafNodeType& leaf, size_t /*idx*/) { + auto expected = leaf.attributeSet().descriptorPtr(); + leaf.dropAttributes(indices, *expected, newDescriptor); + }, /*threaded=*/true + ); +} + + +//////////////////////////////////////// + + +template +inline void dropAttributes( PointDataTreeT& tree, + const std::vector& names) +{ + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + const AttributeSet::Descriptor& descriptor = attributeSet.descriptor(); + + std::vector indices; + + for (const Name& name : names) { + const size_t index = descriptor.find(name); + + // do not attempt to drop an attribute that does not exist + if (index == AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, + "Cannot drop an attribute that does not exist - " << name << "."); + } + + indices.push_back(index); + } + + dropAttributes(tree, indices); +} + + +//////////////////////////////////////// + + +template +inline void dropAttribute( PointDataTreeT& tree, + const size_t& index) +{ + std::vector indices{index}; + dropAttributes(tree, indices); +} + + +template +inline void dropAttribute( PointDataTreeT& tree, + const Name& name) +{ + std::vector names{name}; + dropAttributes(tree, names); +} + + +//////////////////////////////////////// + + +template +inline void renameAttributes( PointDataTreeT& tree, + const std::vector& oldNames, + const std::vector& newNames) +{ + if (oldNames.size() != newNames.size()) { + OPENVDB_THROW(ValueError, "Mis-matching sizes of name vectors, cannot rename attributes."); + } + + using Descriptor = AttributeSet::Descriptor; + + auto iter = tree.beginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + const Descriptor::Ptr descriptor = attributeSet.descriptorPtr(); + auto newDescriptor = std::make_shared(*descriptor); + + for (size_t i = 0; i < oldNames.size(); i++) { + const Name& oldName = oldNames[i]; + if (descriptor->find(oldName) == AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, "Cannot find requested attribute - " << oldName << "."); + } + + const Name& newName = newNames[i]; + if (descriptor->find(newName) != AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, + "Cannot rename attribute as new name already exists - " << newName << "."); + } + + const AttributeArray* array = attributeSet.getConst(oldName); + assert(array); + + if (isGroup(*array)) { + OPENVDB_THROW(KeyError, "Cannot rename group attribute - " << oldName << "."); + } + + newDescriptor->rename(oldName, newName); + } + + for (; iter; ++iter) { + iter->renameAttributes(*descriptor, newDescriptor); + } +} + + +template +inline void renameAttribute(PointDataTreeT& tree, + const Name& oldName, + const Name& newName) +{ + renameAttributes(tree, {oldName}, {newName}); +} + + +//////////////////////////////////////// + + +template +inline void compactAttributes(PointDataTreeT& tree) +{ + auto iter = tree.beginLeaf(); + if (!iter) return; + + tree::LeafManager leafManager(tree); + leafManager.foreach( + [&](typename PointDataTree::LeafNodeType& leaf, size_t /*idx*/) { + leaf.compactAttributes(); + }, /*threaded=*/ true + ); +} + + +//////////////////////////////////////// + + +template +OPENVDB_DEPRECATED inline void bloscCompressAttribute( PointDataTreeT&, + const Name&) +{ + // in-memory compression is no longer supported +} + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_POINT_ATTRIBUTE_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointConversion.h b/openvdb/points/PointConversion.h new file mode 100644 index 00000000..8ef97e93 --- /dev/null +++ b/openvdb/points/PointConversion.h @@ -0,0 +1,1103 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Dan Bailey, Nick Avramoussis +/// +/// @file points/PointConversion.h +/// +/// @brief Convert points and attributes to and from VDB Point Data grids. + +#ifndef OPENVDB_POINTS_POINT_CONVERSION_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_CONVERSION_HAS_BEEN_INCLUDED + +#include + +#include +#include +#include + +#include "AttributeArrayString.h" +#include "AttributeSet.h" +#include "IndexFilter.h" +#include "PointAttribute.h" +#include "PointDataGrid.h" +#include "PointGroup.h" + +#include + +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +/// @brief Localises points with position into a @c PointDataGrid into two stages: +/// allocation of the leaf attribute data and population of the positions. +/// +/// @param pointIndexGrid a PointIndexGrid into the points. +/// @param positions list of world space point positions. +/// @param xform world to index space transform. +/// @param positionDefaultValue metadata default position value +/// +/// @note The position data must be supplied in a Point-Partitioner compatible +/// data structure. A convenience PointAttributeVector class is offered. +/// +/// @note The position data is populated separately to perform world space to +/// voxel space conversion and apply quantisation. +/// +/// @note A @c PointIndexGrid to the points must be supplied to perform this +/// operation. Typically this is built implicitly by the PointDataGrid constructor. + +template< + typename CompressionT, + typename PointDataGridT, + typename PositionArrayT, + typename PointIndexGridT> +inline typename PointDataGridT::Ptr +createPointDataGrid(const PointIndexGridT& pointIndexGrid, + const PositionArrayT& positions, + const math::Transform& xform, + Metadata::Ptr positionDefaultValue = Metadata::Ptr()); + + +/// @brief Convenience method to create a @c PointDataGrid from a std::vector of +/// point positions. +/// +/// @param positions list of world space point positions. +/// @param xform world to index space transform. +/// @param positionDefaultValue metadata default position value +/// +/// @note This method implicitly wraps the std::vector for a Point-Partitioner compatible +/// data structure and creates the required @c PointIndexGrid to the points. + +template +inline typename PointDataGridT::Ptr +createPointDataGrid(const std::vector& positions, + const math::Transform& xform, + Metadata::Ptr positionDefaultValue = Metadata::Ptr()); + + +/// @brief Stores point attribute data in an existing @c PointDataGrid attribute. +/// +/// @param tree the PointDataGrid to be populated. +/// @param pointIndexTree a PointIndexTree into the points. +/// @param attributeName the name of the VDB Points attribute to be populated. +/// @param data a wrapper to the attribute data. +/// @param stride the stride of the attribute +/// @param insertMetadata true if strings are to be automatically inserted as metadata. +/// +/// @note A @c PointIndexGrid to the points must be supplied to perform this +/// operation. This is required to ensure the same point index ordering. +template +inline void +populateAttribute( PointDataTreeT& tree, + const PointIndexTreeT& pointIndexTree, + const openvdb::Name& attributeName, + const PointArrayT& data, + const Index stride = 1, + const bool insertMetadata = true); + +/// @brief Convert the position attribute from a Point Data Grid +/// +/// @param positionAttribute the position attribute to be populated. +/// @param grid the PointDataGrid to be converted. +/// @param pointOffsets a vector of cumulative point offsets for each leaf +/// @param startOffset a value to shift all the point offsets by +/// @param filter an index filter +/// @param inCoreOnly true if out-of-core leaf nodes are to be ignored +/// + +template +inline void +convertPointDataGridPosition( PositionAttribute& positionAttribute, + const PointDataGridT& grid, + const std::vector& pointOffsets, + const Index64 startOffset, + const FilterT& filter = NullFilter(), + const bool inCoreOnly = false); + + +/// @brief Convert the attribute from a PointDataGrid +/// +/// @param attribute the attribute to be populated. +/// @param tree the PointDataTree to be converted. +/// @param pointOffsets a vector of cumulative point offsets for each leaf. +/// @param startOffset a value to shift all the point offsets by +/// @param arrayIndex the index in the Descriptor of the array to be converted. +/// @param stride the stride of the attribute +/// @param filter an index filter +/// @param inCoreOnly true if out-of-core leaf nodes are to be ignored +template +inline void +convertPointDataGridAttribute( TypedAttribute& attribute, + const PointDataTreeT& tree, + const std::vector& pointOffsets, + const Index64 startOffset, + const unsigned arrayIndex, + const Index stride = 1, + const FilterT& filter = NullFilter(), + const bool inCoreOnly = false); + + +/// @brief Convert the group from a PointDataGrid +/// +/// @param group the group to be populated. +/// @param tree the PointDataTree to be converted. +/// @param pointOffsets a vector of cumulative point offsets for each leaf +/// @param startOffset a value to shift all the point offsets by +/// @param index the group index to be converted. +/// @param filter an index filter +/// @param inCoreOnly true if out-of-core leaf nodes are to be ignored +/// + +template +inline void +convertPointDataGridGroup( Group& group, + const PointDataTreeT& tree, + const std::vector& pointOffsets, + const Index64 startOffset, + const AttributeSet::Descriptor::GroupIndex index, + const FilterT& filter = NullFilter(), + const bool inCoreOnly = false); + +/// @ brief Given a container of world space positions and a target points per voxel, +/// compute a uniform voxel size that would best represent the storage of the points in a grid. +/// This voxel size is typically used for conversion of the points into a PointDataGrid. +/// +/// @param positions array of world space positions +/// @param pointsPerVoxel the target number of points per voxel, must be positive and non-zero +/// @param transform voxel size will be computed using this optional transform if provided +/// @param decimalPlaces for readability, truncate voxel size to this number of decimals +/// @param interrupter an optional interrupter +/// +/// @note if none or one point provided in positions, the default voxel size of 0.1 will be returned +/// +template +inline float +computeVoxelSize( const PositionWrapper& positions, + const uint32_t pointsPerVoxel, + const math::Mat4d transform = math::Mat4d::identity(), + const Index decimalPlaces = 5, + InterrupterT* const interrupter = nullptr); + + +//////////////////////////////////////// + + +/// @brief Point-partitioner compatible STL vector attribute wrapper for convenience +template +class PointAttributeVector { +public: + using PosType = ValueType; + using value_type= ValueType; + + PointAttributeVector(const std::vector& data, + const Index stride = 1) + : mData(data) + , mStride(stride) { } + + size_t size() const { return mData.size(); } + void getPos(size_t n, ValueType& xyz) const { xyz = mData[n]; } + void get(ValueType& value, size_t n) const { value = mData[n]; } + void get(ValueType& value, size_t n, openvdb::Index m) const { value = mData[n * mStride + m]; } + +private: + const std::vector& mData; + const Index mStride; +}; // PointAttributeVector + + +//////////////////////////////////////// + + +namespace point_conversion_internal { + + +// ConversionTraits to create the relevant Attribute Handles from a LeafNode +template struct ConversionTraits +{ + using Handle = AttributeHandle; + using WriteHandle = AttributeWriteHandle; + static T zero() { return zeroVal(); } + template + static typename Handle::Ptr handleFromLeaf(LeafT& leaf, Index index) { + const AttributeArray& array = leaf.constAttributeArray(index); + return Handle::create(array); + } + template + static typename WriteHandle::Ptr writeHandleFromLeaf(LeafT& leaf, Index index) { + AttributeArray& array = leaf.attributeArray(index); + return WriteHandle::create(array); + } +}; // ConversionTraits +template <> struct ConversionTraits +{ + using Handle = StringAttributeHandle; + using WriteHandle = StringAttributeWriteHandle; + static openvdb::Name zero() { return ""; } + template + static typename Handle::Ptr handleFromLeaf(LeafT& leaf, Index index) { + const AttributeArray& array = leaf.constAttributeArray(index); + const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor(); + return Handle::create(array, descriptor.getMetadata()); + } + template + static typename WriteHandle::Ptr writeHandleFromLeaf(LeafT& leaf, Index index) { + AttributeArray& array = leaf.attributeArray(index); + const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor(); + return WriteHandle::create(array, descriptor.getMetadata()); + } +}; // ConversionTraits + +template< typename PointDataTreeType, + typename PointIndexTreeType, + typename AttributeListType> +struct PopulateAttributeOp { + + using LeafManagerT = typename tree::LeafManager; + using LeafRangeT = typename LeafManagerT::LeafRange; + using PointIndexLeafNode = typename PointIndexTreeType::LeafNodeType; + using IndexArray = typename PointIndexLeafNode::IndexArray; + using ValueType = typename AttributeListType::value_type; + using HandleT = typename ConversionTraits::WriteHandle; + + PopulateAttributeOp(const PointIndexTreeType& pointIndexTree, + const AttributeListType& data, + const size_t index, + const Index stride = 1) + : mPointIndexTree(pointIndexTree) + , mData(data) + , mIndex(index) + , mStride(stride) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const { + + for (auto leaf = range.begin(); leaf; ++leaf) { + + // obtain the PointIndexLeafNode (using the origin of the current leaf) + + const PointIndexLeafNode* pointIndexLeaf = + mPointIndexTree.probeConstLeaf(leaf->origin()); + + if (!pointIndexLeaf) continue; + + typename HandleT::Ptr attributeWriteHandle = + ConversionTraits::writeHandleFromLeaf(*leaf, static_cast(mIndex)); + + Index64 index = 0; + + const IndexArray& indices = pointIndexLeaf->indices(); + + for (const Index64 leafIndex: indices) + { + ValueType value; + for (Index i = 0; i < mStride; i++) { + mData.get(value, leafIndex, i); + attributeWriteHandle->set(static_cast(index), i, value); + } + index++; + } + + // attempt to compact the array + + attributeWriteHandle->compact(); + } + } + + ////////// + + const PointIndexTreeType& mPointIndexTree; + const AttributeListType& mData; + const size_t mIndex; + const Index mStride; +}; + +template +struct ConvertPointDataGridPositionOp { + + using LeafNode = typename PointDataTreeType::LeafNodeType; + using ValueType = typename Attribute::ValueType; + using HandleT = typename Attribute::Handle; + using SourceHandleT = AttributeHandle; + using LeafManagerT = typename tree::LeafManager; + using LeafRangeT = typename LeafManagerT::LeafRange; + + ConvertPointDataGridPositionOp( Attribute& attribute, + const std::vector& pointOffsets, + const Index64 startOffset, + const math::Transform& transform, + const size_t index, + const FilterT& filter, + const bool inCoreOnly) + : mAttribute(attribute) + , mPointOffsets(pointOffsets) + , mStartOffset(startOffset) + , mTransform(transform) + , mIndex(index) + , mFilter(filter) + , mInCoreOnly(inCoreOnly) + { + // only accept Vec3f as ValueType + static_assert(VecTraits::Size == 3 && + std::is_floating_point::value, + "ValueType is not Vec3f"); + } + + template + void convert(IterT& iter, HandleT& targetHandle, + SourceHandleT& sourceHandle, Index64& offset) const + { + for (; iter; ++iter) { + const Vec3d xyz = iter.getCoord().asVec3d(); + const Vec3d pos = sourceHandle.get(*iter); + targetHandle.set(static_cast(offset++), /*stride=*/0, + mTransform.indexToWorld(pos + xyz)); + } + } + + void operator()(const LeafRangeT& range) const + { + HandleT pHandle(mAttribute); + + for (auto leaf = range.begin(); leaf; ++leaf) { + + assert(leaf.pos() < mPointOffsets.size()); + + if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue; + + Index64 offset = mStartOffset; + + if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1]; + + auto handle = SourceHandleT::create(leaf->constAttributeArray(mIndex)); + + if (mFilter.state() == index::ALL) { + auto iter = leaf->beginIndexOn(); + convert(iter, pHandle, *handle, offset); + } + else { + auto iter = leaf->beginIndexOn(mFilter); + convert(iter, pHandle, *handle, offset); + } + } + } + + ////////// + + Attribute& mAttribute; + const std::vector& mPointOffsets; + const Index64 mStartOffset; + const math::Transform& mTransform; + const size_t mIndex; + const FilterT& mFilter; + const bool mInCoreOnly; +}; // ConvertPointDataGridPositionOp + + +template +struct ConvertPointDataGridAttributeOp { + + using LeafNode = typename PointDataTreeType::LeafNodeType; + using ValueType = typename Attribute::ValueType; + using HandleT = typename Attribute::Handle; + using SourceHandleT = typename ConversionTraits::Handle; + using LeafManagerT = typename tree::LeafManager; + using LeafRangeT = typename LeafManagerT::LeafRange; + + ConvertPointDataGridAttributeOp(Attribute& attribute, + const std::vector& pointOffsets, + const Index64 startOffset, + const size_t index, + const Index stride, + const FilterT& filter, + const bool inCoreOnly) + : mAttribute(attribute) + , mPointOffsets(pointOffsets) + , mStartOffset(startOffset) + , mIndex(index) + , mStride(stride) + , mFilter(filter) + , mInCoreOnly(inCoreOnly) { } + + template + void convert(IterT& iter, HandleT& targetHandle, + SourceHandleT& sourceHandle, Index64& offset) const + { + if (sourceHandle.isUniform()) { + const ValueType uniformValue(sourceHandle.get(0)); + for (; iter; ++iter) { + for (Index i = 0; i < mStride; i++) { + targetHandle.set(static_cast(offset), i, uniformValue); + } + offset++; + } + } + else { + for (; iter; ++iter) { + for (Index i = 0; i < mStride; i++) { + targetHandle.set(static_cast(offset), i, + sourceHandle.get(*iter, /*stride=*/i)); + } + offset++; + } + } + } + + void operator()(const LeafRangeT& range) const + { + HandleT pHandle(mAttribute); + + for (auto leaf = range.begin(); leaf; ++leaf) { + + assert(leaf.pos() < mPointOffsets.size()); + + if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue; + + Index64 offset = mStartOffset; + + if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1]; + + typename SourceHandleT::Ptr handle = ConversionTraits::handleFromLeaf( + *leaf, static_cast(mIndex)); + + if (mFilter.state() == index::ALL) { + auto iter = leaf->beginIndexOn(); + convert(iter, pHandle, *handle, offset); + } else { + auto iter = leaf->beginIndexOn(mFilter); + convert(iter, pHandle, *handle, offset); + } + } + } + + ////////// + + Attribute& mAttribute; + const std::vector& mPointOffsets; + const Index64 mStartOffset; + const size_t mIndex; + const Index mStride; + const FilterT& mFilter; + const bool mInCoreOnly; +}; // ConvertPointDataGridAttributeOp + +template +struct ConvertPointDataGridGroupOp { + + using LeafNode = typename PointDataTreeType::LeafNodeType; + using GroupIndex = AttributeSet::Descriptor::GroupIndex; + using LeafManagerT = typename tree::LeafManager; + using LeafRangeT = typename LeafManagerT::LeafRange; + + ConvertPointDataGridGroupOp(Group& group, + const std::vector& pointOffsets, + const Index64 startOffset, + const AttributeSet::Descriptor::GroupIndex index, + const FilterT& filter, + const bool inCoreOnly) + : mGroup(group) + , mPointOffsets(pointOffsets) + , mStartOffset(startOffset) + , mIndex(index) + , mFilter(filter) + , mInCoreOnly(inCoreOnly) { } + + template + void convert(IterT& iter, const GroupAttributeArray& groupArray, Index64& offset) const + { + const auto bitmask = static_cast(1 << mIndex.second); + + if (groupArray.isUniform()) { + if (groupArray.get(0) & bitmask) { + for (; iter; ++iter) { + mGroup.setOffsetOn(static_cast(offset)); + offset++; + } + } + } + else { + for (; iter; ++iter) { + if (groupArray.get(*iter) & bitmask) { + mGroup.setOffsetOn(static_cast(offset)); + } + offset++; + } + } + } + + void operator()(const LeafRangeT& range) const + { + for (auto leaf = range.begin(); leaf; ++leaf) { + + assert(leaf.pos() < mPointOffsets.size()); + + if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue; + + Index64 offset = mStartOffset; + + if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1]; + + const AttributeArray& array = leaf->constAttributeArray(mIndex.first); + assert(isGroup(array)); + const GroupAttributeArray& groupArray = GroupAttributeArray::cast(array); + + if (mFilter.state() == index::ALL) { + auto iter = leaf->beginIndexOn(); + convert(iter, groupArray, offset); + } + else { + auto iter = leaf->beginIndexOn(mFilter); + convert(iter, groupArray, offset); + } + } + } + + ////////// + + Group& mGroup; + const std::vector& mPointOffsets; + const Index64 mStartOffset; + const GroupIndex mIndex; + const FilterT& mFilter; + const bool mInCoreOnly; +}; // ConvertPointDataGridGroupOp + +template +struct CalculatePositionBounds +{ + CalculatePositionBounds(const PositionArrayT& positions, + const math::Mat4d& inverse) + : mPositions(positions) + , mInverseMat(inverse) + , mMin(std::numeric_limits::max()) + , mMax(-std::numeric_limits::max()) {} + + CalculatePositionBounds(const CalculatePositionBounds& other, tbb::split) + : mPositions(other.mPositions) + , mInverseMat(other.mInverseMat) + , mMin(std::numeric_limits::max()) + , mMax(-std::numeric_limits::max()) {} + + void operator()(const tbb::blocked_range& range) { + Vec3R pos; + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + mPositions.getPos(n, pos); + pos = mInverseMat.transform(pos); + mMin = math::minComponent(mMin, pos); + mMax = math::maxComponent(mMax, pos); + } + } + + void join(const CalculatePositionBounds& other) { + mMin = math::minComponent(mMin, other.mMin); + mMax = math::maxComponent(mMax, other.mMax); + } + + BBoxd getBoundingBox() const { + return BBoxd(mMin, mMax); + } + +private: + const PositionArrayT& mPositions; + const math::Mat4d& mInverseMat; + Vec3R mMin, mMax; +}; + +} // namespace point_conversion_internal + + +//////////////////////////////////////// + + +template +inline typename PointDataGridT::Ptr +createPointDataGrid(const PointIndexGridT& pointIndexGrid, const PositionArrayT& positions, + const math::Transform& xform, Metadata::Ptr positionDefaultValue) +{ + using PointDataTreeT = typename PointDataGridT::TreeType; + using LeafT = typename PointDataTree::LeafNodeType; + using PointIndexLeafT = typename PointIndexGridT::TreeType::LeafNodeType; + using PointIndexT = typename PointIndexLeafT::ValueType; + using LeafManagerT = typename tree::LeafManager; + using PositionAttributeT = TypedAttributeArray; + + const NamePair positionType = PositionAttributeT::attributeType(); + + // construct the Tree using a topology copy of the PointIndexGrid + + const auto& pointIndexTree = pointIndexGrid.tree(); + typename PointDataTreeT::Ptr treePtr(new PointDataTreeT(pointIndexTree)); + + // create attribute descriptor from position type + + auto descriptor = AttributeSet::Descriptor::create(positionType); + + // add default value for position if provided + + if (positionDefaultValue) descriptor->setDefaultValue("P", *positionDefaultValue); + + // retrieve position index + + const size_t positionIndex = descriptor->find("P"); + assert(positionIndex != AttributeSet::INVALID_POS); + + // acquire registry lock to avoid locking when appending attributes in parallel + + AttributeArray::ScopedRegistryLock lock; + + // populate position attribute + + LeafManagerT leafManager(*treePtr); + leafManager.foreach( + [&](LeafT& leaf, size_t /*idx*/) { + + // obtain the PointIndexLeafNode (using the origin of the current leaf) + + const auto* pointIndexLeaf = pointIndexTree.probeConstLeaf(leaf.origin()); + assert(pointIndexLeaf); + + // initialise the attribute storage + + Index pointCount(static_cast(pointIndexLeaf->indices().size())); + leaf.initializeAttributes(descriptor, pointCount, &lock); + + // create write handle for position + + auto attributeWriteHandle = AttributeWriteHandle::create( + leaf.attributeArray(positionIndex)); + + Index index = 0; + + const PointIndexT + *begin = static_cast(nullptr), + *end = static_cast(nullptr); + + // iterator over every active voxel in the point index leaf + + for (auto iter = pointIndexLeaf->cbeginValueOn(); iter; ++iter) { + + // find the voxel center + + const Coord& ijk = iter.getCoord(); + const Vec3d& positionCellCenter(ijk.asVec3d()); + + // obtain pointers for this voxel from begin to end in the indices array + + pointIndexLeaf->getIndices(ijk, begin, end); + + while (begin < end) { + + typename PositionArrayT::value_type positionWorldSpace; + positions.getPos(*begin, positionWorldSpace); + + // compute the index-space position and then subtract the voxel center + + const Vec3d positionIndexSpace = xform.worldToIndex(positionWorldSpace); + const Vec3f positionVoxelSpace(positionIndexSpace - positionCellCenter); + + attributeWriteHandle->set(index++, positionVoxelSpace); + + ++begin; + } + } + }, + /*threaded=*/true); + + auto grid = PointDataGridT::create(treePtr); + grid->setTransform(xform.copy()); + return grid; +} + + +//////////////////////////////////////// + + +template +inline typename PointDataGridT::Ptr +createPointDataGrid(const std::vector& positions, + const math::Transform& xform, + Metadata::Ptr positionDefaultValue) +{ + const PointAttributeVector pointList(positions); + + tools::PointIndexGrid::Ptr pointIndexGrid = tools::createPointIndexGrid(pointList, xform); + return createPointDataGrid(*pointIndexGrid, pointList, xform, positionDefaultValue); +} + + +//////////////////////////////////////// + + +template +inline void +populateAttribute(PointDataTreeT& tree, const PointIndexTreeT& pointIndexTree, + const openvdb::Name& attributeName, const PointArrayT& data, const Index stride, + const bool insertMetadata) +{ + using point_conversion_internal::PopulateAttributeOp; + using ValueType = typename PointArrayT::value_type; + + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const size_t index = iter->attributeSet().find(attributeName); + + if (index == AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, "Attribute not found to populate - " << attributeName << "."); + } + + if (insertMetadata) { + point_attribute_internal::MetadataStorage::add(tree, data); + } + + // populate attribute + + typename tree::LeafManager leafManager(tree); + + PopulateAttributeOp populate(pointIndexTree, data, index, stride); + tbb::parallel_for(leafManager.leafRange(), populate); +} + + +//////////////////////////////////////// + + +template +inline void +convertPointDataGridPosition( PositionAttribute& positionAttribute, + const PointDataGridT& grid, + const std::vector& pointOffsets, + const Index64 startOffset, + const FilterT& filter, + const bool inCoreOnly) +{ + using TreeType = typename PointDataGridT::TreeType; + using LeafManagerT = typename tree::LeafManager; + + using point_conversion_internal::ConvertPointDataGridPositionOp; + + const TreeType& tree = grid.tree(); + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const size_t positionIndex = iter->attributeSet().find("P"); + + positionAttribute.expand(); + LeafManagerT leafManager(tree); + ConvertPointDataGridPositionOp convert( + positionAttribute, pointOffsets, startOffset, grid.transform(), positionIndex, + filter, inCoreOnly); + tbb::parallel_for(leafManager.leafRange(), convert); + positionAttribute.compact(); +} + + +//////////////////////////////////////// + + +template +inline void +convertPointDataGridAttribute( TypedAttribute& attribute, + const PointDataTreeT& tree, + const std::vector& pointOffsets, + const Index64 startOffset, + const unsigned arrayIndex, + const Index stride, + const FilterT& filter, + const bool inCoreOnly) +{ + using LeafManagerT = typename tree::LeafManager; + + using point_conversion_internal::ConvertPointDataGridAttributeOp; + + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + attribute.expand(); + LeafManagerT leafManager(tree); + ConvertPointDataGridAttributeOp convert( + attribute, pointOffsets, startOffset, arrayIndex, stride, + filter, inCoreOnly); + tbb::parallel_for(leafManager.leafRange(), convert); + attribute.compact(); +} + + +//////////////////////////////////////// + + +template +inline void +convertPointDataGridGroup( Group& group, + const PointDataTreeT& tree, + const std::vector& pointOffsets, + const Index64 startOffset, + const AttributeSet::Descriptor::GroupIndex index, + const FilterT& filter, + const bool inCoreOnly) +{ + using LeafManagerT= typename tree::LeafManager; + + using point_conversion_internal::ConvertPointDataGridGroupOp; + + auto iter = tree.cbeginLeaf(); + if (!iter) return; + + LeafManagerT leafManager(tree); + ConvertPointDataGridGroupOp convert( + group, pointOffsets, startOffset, index, + filter, inCoreOnly); + tbb::parallel_for(leafManager.leafRange(), convert); + + // must call this after modifying point groups in parallel + + group.finalize(); +} + +template +inline float +computeVoxelSize( const PositionWrapper& positions, + const uint32_t pointsPerVoxel, + const math::Mat4d transform, + const Index decimalPlaces, + InterrupterT* const interrupter) +{ + using namespace point_conversion_internal; + + struct Local { + + static bool voxelSizeFromVolume(const double volume, + const size_t estimatedVoxelCount, + float& voxelSize) + { + // dictated by the math::ScaleMap limit + static const double minimumVoxelVolume(3e-15); + static const double maximumVoxelVolume(std::numeric_limits::max()); + + double voxelVolume = volume / static_cast(estimatedVoxelCount); + bool valid = true; + + if (voxelVolume < minimumVoxelVolume) { + voxelVolume = minimumVoxelVolume; + valid = false; + } + else if (voxelVolume > maximumVoxelVolume) { + voxelVolume = maximumVoxelVolume; + valid = false; + } + + voxelSize = static_cast(math::Pow(voxelVolume, 1.0/3.0)); + return valid; + } + + static float truncate(const float voxelSize, Index decPlaces) + { + float truncatedVoxelSize = voxelSize; + + // attempt to truncate from decPlaces -> 11 + for (int i = decPlaces; i < 11; i++) { + truncatedVoxelSize = static_cast(math::Truncate(double(voxelSize), i)); + if (truncatedVoxelSize != 0.0f) break; + } + + return truncatedVoxelSize; + } + }; + + if (pointsPerVoxel == 0) OPENVDB_THROW(ValueError, "Points per voxel cannot be zero."); + + // constructed with the default voxel size as specified by openvdb interface values + + float voxelSize(0.1f); + + const size_t numPoints = positions.size(); + + // return the default voxel size if we have zero or only 1 point + + if (numPoints <= 1) return voxelSize; + + size_t targetVoxelCount(numPoints / size_t(pointsPerVoxel)); + if (targetVoxelCount == 0) targetVoxelCount++; + + // calculate the world space, transform-oriented bounding box + + math::Mat4d inverseTransform = transform.inverse(); + inverseTransform = math::unit(inverseTransform); + + tbb::blocked_range range(0, numPoints); + CalculatePositionBounds calculateBounds(positions, inverseTransform); + tbb::parallel_reduce(range, calculateBounds); + + BBoxd bbox = calculateBounds.getBoundingBox(); + + // return default size if points are coincident + + if (bbox.min() == bbox.max()) return voxelSize; + + double volume = bbox.volume(); + + // handle points that are collinear or coplanar by expanding the volume + + if (math::isApproxZero(volume)) { + Vec3d extents = bbox.extents().sorted().reversed(); + if (math::isApproxZero(extents[1])) { + // colinear (maxExtent^3) + volume = extents[0]*extents[0]*extents[0]; + } + else { + // coplanar (maxExtent*nextMaxExtent^2) + volume = extents[0]*extents[1]*extents[1]; + } + } + + double previousVolume = volume; + + if (!Local::voxelSizeFromVolume(volume, targetVoxelCount, voxelSize)) { + OPENVDB_LOG_DEBUG("Out of range, clamping voxel size."); + return voxelSize; + } + + size_t previousVoxelCount(0); + size_t voxelCount(1); + + if (interrupter) interrupter->start("Computing voxel size"); + + while (voxelCount > previousVoxelCount) + { + math::Transform::Ptr newTransform; + + if (!math::isIdentity(transform)) + { + // if using a custom transform, pre-scale by coefficients + // which define the new voxel size + + math::Mat4d matrix(transform); + matrix.preScale(Vec3d(voxelSize) / math::getScale(matrix)); + newTransform = math::Transform::createLinearTransform(matrix); + } + else + { + newTransform = math::Transform::createLinearTransform(voxelSize); + } + + // create a mask grid of the points from the calculated voxel size + // this is the same function call as tools::createPointMask() which has + // been duplicated to provide an interrupter + + MaskGrid::Ptr mask = createGrid(false); + mask->setTransform(newTransform); + tools::PointsToMask pointMaskOp(*mask, interrupter); + pointMaskOp.addPoints(positions); + + if (interrupter && util::wasInterrupted(interrupter)) break; + + previousVoxelCount = voxelCount; + voxelCount = mask->activeVoxelCount(); + volume = math::Pow3(voxelSize) * static_cast(voxelCount); + + // stop if no change in the volume or the volume has increased + + if (volume >= previousVolume) break; + previousVolume = volume; + + const float previousVoxelSize = voxelSize; + + // compute the new voxel size and if invalid return the previous value + + if (!Local::voxelSizeFromVolume(volume, targetVoxelCount, voxelSize)) { + voxelSize = previousVoxelSize; + break; + } + + // halt convergence if the voxel size has decreased by less + // than 10% in this iteration + + if (voxelSize / previousVoxelSize > 0.9f) break; + } + + if (interrupter) interrupter->end(); + + // truncate the voxel size for readability and return the value + + return Local::truncate(voxelSize, decimalPlaces); +} + + +//////////////////////////////////////// + + +// deprecated functions + + +template +OPENVDB_DEPRECATED +inline void +convertPointDataGridPosition( PositionAttribute& positionAttribute, + const PointDataGridT& grid, + const std::vector& pointOffsets, + const Index64 startOffset, + const std::vector& includeGroups, + const std::vector& excludeGroups, + const bool inCoreOnly = false) +{ + auto leaf = grid.tree().cbeginLeaf(); + if (!leaf) return; + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + convertPointDataGridPosition(positionAttribute, grid, pointOffsets, startOffset, + filter, inCoreOnly); +} + + +template +OPENVDB_DEPRECATED +inline void +convertPointDataGridAttribute( TypedAttribute& attribute, + const PointDataTreeT& tree, + const std::vector& pointOffsets, + const Index64 startOffset, + const unsigned arrayIndex, + const Index stride, + const std::vector& includeGroups, + const std::vector& excludeGroups, + const bool inCoreOnly = false) +{ + auto leaf = tree.cbeginLeaf(); + if (!leaf) return; + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + convertPointDataGridAttribute(attribute, tree, pointOffsets, startOffset, + arrayIndex, stride, filter, inCoreOnly); +} + + +template +OPENVDB_DEPRECATED +inline void +convertPointDataGridGroup( Group& group, + const PointDataTreeT& tree, + const std::vector& pointOffsets, + const Index64 startOffset, + const AttributeSet::Descriptor::GroupIndex index, + const std::vector& includeGroups, + const std::vector& excludeGroups, + const bool inCoreOnly = false) +{ + auto leaf = tree.cbeginLeaf(); + if (!leaf) return; + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + convertPointDataGridGroup(group, tree, pointOffsets, startOffset, + index, filter, inCoreOnly); +} + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_POINT_CONVERSION_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointCount.h b/openvdb/points/PointCount.h new file mode 100644 index 00000000..c113b42a --- /dev/null +++ b/openvdb/points/PointCount.h @@ -0,0 +1,325 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/PointCount.h +/// +/// @author Dan Bailey +/// +/// @brief Methods for counting points in VDB Point grids. + +#ifndef OPENVDB_POINTS_POINT_COUNT_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_COUNT_HAS_BEEN_INCLUDED + +#include + +#include "PointDataGrid.h" +#include "PointMask.h" +#include "IndexFilter.h" + +#include + +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +/// @brief Count the total number of points in a PointDataTree +/// @param tree the PointDataTree in which to count the points +/// @param filter an optional index filter +/// @param inCoreOnly if true, points in out-of-core leaf nodes are not counted +/// @param threaded enable or disable threading (threading is enabled by default) +template +inline Index64 pointCount( const PointDataTreeT& tree, + const FilterT& filter = NullFilter(), + const bool inCoreOnly = false, + const bool threaded = true); + + +/// @brief Populate an array of cumulative point offsets per leaf node. +/// @param pointOffsets array of offsets to be populated +/// @param tree the PointDataTree from which to populate the offsets +/// @param filter an optional index filter +/// @param inCoreOnly if true, points in out-of-core leaf nodes are ignored +/// @param threaded enable or disable threading (threading is enabled by default) +/// @return The final cumulative point offset. +template +inline Index64 pointOffsets(std::vector& pointOffsets, + const PointDataTreeT& tree, + const FilterT& filter = NullFilter(), + const bool inCoreOnly = false, + const bool threaded = true); + + +/// @brief Generate a new grid with voxel values to store the number of points per voxel +/// @param grid the PointDataGrid to use to compute the count grid +/// @param filter an optional index filter +/// @note The return type of the grid must be an integer or floating-point scalar grid. +template ::Type, + typename FilterT = NullFilter> +inline typename GridT::Ptr +pointCountGrid( const PointDataGridT& grid, + const FilterT& filter = NullFilter()); + + +/// @brief Generate a new grid that uses the supplied transform with voxel values to store the +/// number of points per voxel. +/// @param grid the PointDataGrid to use to compute the count grid +/// @param transform the transform to use to compute the count grid +/// @param filter an optional index filter +/// @note The return type of the grid must be an integer or floating-point scalar grid. +template ::Type, + typename FilterT = NullFilter> +inline typename GridT::Ptr +pointCountGrid( const PointDataGridT& grid, + const openvdb::math::Transform& transform, + const FilterT& filter = NullFilter()); + + +//////////////////////////////////////// + + +template +Index64 pointCount(const PointDataTreeT& tree, + const FilterT& filter, + const bool inCoreOnly, + const bool threaded) +{ + using LeafManagerT = tree::LeafManager; + using LeafRangeT = typename LeafManagerT::LeafRange; + + auto countLambda = + [&filter, &inCoreOnly] (const LeafRangeT& range, Index64 sum) -> Index64 { + for (const auto& leaf : range) { + if (inCoreOnly && leaf.buffer().isOutOfCore()) continue; + auto state = filter.state(leaf); + if (state == index::ALL) { + sum += leaf.pointCount(); + } else if (state != index::NONE) { + sum += iterCount(leaf.beginIndexAll(filter)); + } + } + return sum; + }; + + LeafManagerT leafManager(tree); + if (threaded) { + return tbb::parallel_reduce(leafManager.leafRange(), Index64(0), countLambda, + [] (Index64 n, Index64 m) -> Index64 { return n + m; }); + } + else { + return countLambda(leafManager.leafRange(), Index64(0)); + } +} + + +template +Index64 pointOffsets( std::vector& pointOffsets, + const PointDataTreeT& tree, + const FilterT& filter, + const bool inCoreOnly, + const bool threaded) +{ + using LeafT = typename PointDataTreeT::LeafNodeType; + using LeafManagerT = typename tree::LeafManager; + + // allocate and zero values in point offsets array + + pointOffsets.assign(tree.leafCount(), Index64(0)); + + // compute total points per-leaf + + LeafManagerT leafManager(tree); + leafManager.foreach( + [&pointOffsets, &filter, &inCoreOnly](const LeafT& leaf, size_t pos) { + if (inCoreOnly && leaf.buffer().isOutOfCore()) return; + auto state = filter.state(leaf); + if (state == index::ALL) { + pointOffsets[pos] = leaf.pointCount(); + } else if (state != index::NONE) { + pointOffsets[pos] = iterCount(leaf.beginIndexAll(filter)); + } + }, + threaded); + + // turn per-leaf totals into cumulative leaf totals + + Index64 pointOffset(pointOffsets[0]); + for (size_t n = 1; n < pointOffsets.size(); n++) { + pointOffset += pointOffsets[n]; + pointOffsets[n] = pointOffset; + } + + return pointOffset; +} + + +template +typename GridT::Ptr +pointCountGrid( const PointDataGridT& points, + const FilterT& filter) +{ + static_assert( std::is_integral::value || + std::is_floating_point::value, + "openvdb::points::pointCountGrid must return an integer or floating-point scalar grid"); + + // This is safe because the PointDataGrid can only be modified by the deformer + using AdapterT = TreeAdapter; + auto& nonConstPoints = const_cast(points); + + return point_mask_internal::convertPointsToScalar( + nonConstPoints, filter); +} + + +template +typename GridT::Ptr +pointCountGrid( const PointDataGridT& points, + const openvdb::math::Transform& transform, + const FilterT& filter) +{ + static_assert( std::is_integral::value || + std::is_floating_point::value, + "openvdb::points::pointCountGrid must return an integer or floating-point scalar grid"); + + // This is safe because the PointDataGrid can only be modified by the deformer + using AdapterT = TreeAdapter; + auto& nonConstPoints = const_cast(points); + + NullDeformer deformer; + return point_mask_internal::convertPointsToScalar( + nonConstPoints, transform, filter, deformer); +} + + +//////////////////////////////////////// + + +// deprecated functions + + +template +OPENVDB_DEPRECATED +inline Index64 pointCount(const PointDataTreeT& tree, const bool inCoreOnly) +{ + NullFilter filter; + return pointCount(tree, filter, inCoreOnly); +} + + +template +OPENVDB_DEPRECATED +inline Index64 activePointCount(const PointDataTreeT& tree, const bool inCoreOnly = true) +{ + ActiveFilter filter; + return pointCount(tree, filter, inCoreOnly); +} + + +template +OPENVDB_DEPRECATED +inline Index64 inactivePointCount(const PointDataTreeT& tree, const bool inCoreOnly = true) +{ + InactiveFilter filter; + return pointCount(tree, filter, inCoreOnly); +} + + +template +OPENVDB_DEPRECATED +inline Index64 groupPointCount(const PointDataTreeT& tree, const Name& name, + const bool inCoreOnly = true) +{ + auto iter = tree.cbeginLeaf(); + if (!iter || !iter->attributeSet().descriptor().hasGroup(name)) { + return Index64(0); + } + GroupFilter filter(name, iter->attributeSet()); + return pointCount(tree, filter, inCoreOnly); +} + + +template +OPENVDB_DEPRECATED +inline Index64 activeGroupPointCount(const PointDataTreeT& tree, const Name& name, + const bool inCoreOnly = true) +{ + auto iter = tree.cbeginLeaf(); + if (!iter || !iter->attributeSet().descriptor().hasGroup(name)) { + return Index64(0); + } + BinaryFilter filter(GroupFilter(name, iter->attributeSet()), ActiveFilter()); + return pointCount(tree, filter, inCoreOnly); +} + + +template +OPENVDB_DEPRECATED +inline Index64 inactiveGroupPointCount(const PointDataTreeT& tree, const Name& name, + const bool inCoreOnly = true) +{ + auto iter = tree.cbeginLeaf(); + if (!iter || !iter->attributeSet().descriptor().hasGroup(name)) { + return Index64(0); + } + BinaryFilter filter(GroupFilter(name, iter->attributeSet()), InactiveFilter()); + return pointCount(tree, filter, inCoreOnly); +} + + +template +OPENVDB_DEPRECATED +inline Index64 getPointOffsets(std::vector& offsets, const PointDataTreeT& tree, + const std::vector& includeGroups, + const std::vector& excludeGroups, + const bool inCoreOnly = false) +{ + MultiGroupFilter filter(includeGroups, excludeGroups, tree.cbeginLeaf()->attributeSet()); + return pointOffsets(offsets, tree, filter, inCoreOnly); +} + + +template ::Type> +OPENVDB_DEPRECATED +inline typename GridT::Ptr +pointCountGrid(const PointDataGridT& grid, + const std::vector& includeGroups, + const std::vector& excludeGroups) +{ + auto leaf = grid.tree().cbeginLeaf(); + if (!leaf) return GridT::create(0); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + return pointCountGrid(grid, filter); +} + + +template ::Type> +OPENVDB_DEPRECATED +inline typename GridT::Ptr +pointCountGrid(const PointDataGridT& grid, + const openvdb::math::Transform& transform, + const std::vector& includeGroups, + const std::vector& excludeGroups) +{ + auto leaf = grid.tree().cbeginLeaf(); + if (!leaf) return GridT::create(0); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + return pointCountGrid(grid, transform, filter); +} + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_POINT_COUNT_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointDataGrid.h b/openvdb/points/PointDataGrid.h new file mode 100644 index 00000000..1053ea83 --- /dev/null +++ b/openvdb/points/PointDataGrid.h @@ -0,0 +1,1736 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Dan Bailey +/// +/// @file points/PointDataGrid.h +/// +/// @brief Attribute-owned data structure for points. Point attributes are +/// stored in leaf nodes and ordered by voxel for fast random and +/// sequential access. + +#ifndef OPENVDB_POINTS_POINT_DATA_GRID_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_DATA_GRID_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include "AttributeArray.h" +#include "AttributeArrayString.h" +#include "AttributeGroup.h" +#include "AttributeSet.h" +#include "StreamCompression.h" +#include // std::memcpy +#include +#include +#include +#include // std::is_same +#include // std::pair, std::make_pair +#include + +#include //for boost::mpl::vector +#include +#include + +class TestPointDataLeaf; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +namespace io +{ + +/// @brief openvdb::io::readCompressedValues specialized on PointDataIndex32 arrays to +/// ignore the value mask, use a larger block size and use 16-bit size instead of 64-bit +template<> +inline void +readCompressedValues( std::istream& is, PointDataIndex32* destBuf, Index destCount, + const util::NodeMask<3>& /*valueMask*/, bool /*fromHalf*/) +{ + using compression::bloscDecompress; + + const bool seek = destBuf == nullptr; + + const size_t destBytes = destCount*sizeof(PointDataIndex32); + const size_t maximumBytes = std::numeric_limits::max(); + if (destBytes >= maximumBytes) { + OPENVDB_THROW(openvdb::IoError, "Cannot read more than " << + maximumBytes << " bytes in voxel values.") + } + + uint16_t bytes16; + + const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(is); + + if (seek && meta) { + // buffer size temporarily stored in the StreamMetadata pass + // to avoid having to perform an expensive disk read for 2-bytes + bytes16 = static_cast(meta->pass()); + // seek over size of the compressed buffer + is.seekg(sizeof(uint16_t), std::ios_base::cur); + } + else { + // otherwise read from disk + is.read(reinterpret_cast(&bytes16), sizeof(uint16_t)); + } + + if (bytes16 == std::numeric_limits::max()) { + // read or seek uncompressed data + if (seek) { + is.seekg(destBytes, std::ios_base::cur); + } + else { + is.read(reinterpret_cast(destBuf), destBytes); + } + } + else { + // read or seek uncompressed data + if (seek) { + is.seekg(int(bytes16), std::ios_base::cur); + } + else { + // decompress into the destination buffer + std::unique_ptr bloscBuffer(new char[int(bytes16)]); + is.read(bloscBuffer.get(), bytes16); + std::unique_ptr buffer = bloscDecompress( bloscBuffer.get(), + destBytes, + /*resize=*/false); + std::memcpy(destBuf, buffer.get(), destBytes); + } + } +} + +/// @brief openvdb::io::writeCompressedValues specialized on PointDataIndex32 arrays to +/// ignore the value mask, use a larger block size and use 16-bit size instead of 64-bit +template<> +inline void +writeCompressedValues( std::ostream& os, PointDataIndex32* srcBuf, Index srcCount, + const util::NodeMask<3>& /*valueMask*/, + const util::NodeMask<3>& /*childMask*/, bool /*toHalf*/) +{ + using compression::bloscCompress; + + const size_t srcBytes = srcCount*sizeof(PointDataIndex32); + const size_t maximumBytes = std::numeric_limits::max(); + if (srcBytes >= maximumBytes) { + OPENVDB_THROW(openvdb::IoError, "Cannot write more than " << + maximumBytes << " bytes in voxel values.") + } + + const char* charBuffer = reinterpret_cast(srcBuf); + + size_t compressedBytes; + std::unique_ptr buffer = bloscCompress( charBuffer, srcBytes, + compressedBytes, /*resize=*/false); + + if (compressedBytes > 0) { + auto bytes16 = static_cast(compressedBytes); // clamp to 16-bit unsigned integer + os.write(reinterpret_cast(&bytes16), sizeof(uint16_t)); + os.write(reinterpret_cast(buffer.get()), compressedBytes); + } + else { + auto bytes16 = static_cast(maximumBytes); // max value indicates uncompressed + os.write(reinterpret_cast(&bytes16), sizeof(uint16_t)); + os.write(reinterpret_cast(srcBuf), srcBytes); + } +} + +template +inline void +writeCompressedValuesSize(std::ostream& os, const T* srcBuf, Index srcCount) +{ + using compression::bloscCompressedSize; + + const size_t srcBytes = srcCount*sizeof(T); + const size_t maximumBytes = std::numeric_limits::max(); + if (srcBytes >= maximumBytes) { + OPENVDB_THROW(openvdb::IoError, "Cannot write more than " << + maximumBytes << " bytes in voxel values.") + } + + const char* charBuffer = reinterpret_cast(srcBuf); + + // calculate voxel buffer size after compression + size_t compressedBytes = bloscCompressedSize(charBuffer, srcBytes); + + if (compressedBytes > 0) { + auto bytes16 = static_cast(compressedBytes); // clamp to 16-bit unsigned integer + os.write(reinterpret_cast(&bytes16), sizeof(uint16_t)); + } + else { + auto bytes16 = static_cast(maximumBytes); // max value indicates uncompressed + os.write(reinterpret_cast(&bytes16), sizeof(uint16_t)); + } +} + +} // namespace io + + +// forward declaration +namespace tree { + template struct SameLeafConfig; +} + + +//////////////////////////////////////// + + +namespace points { + + +// forward declaration +template class PointDataLeafNode; + +/// @brief Point index tree configured to match the default VDB configurations. +using PointDataTree = tree::Tree, 4>, 5>>>; + + +/// @brief Point data grid. +using PointDataGrid = Grid; + + +/// @brief Deep copy the descriptor across all leaf nodes. +/// +/// @param tree the PointDataTree. +/// +/// @return the new descriptor. +/// +/// @note This method will fail if the Descriptors in the tree are not all identical. +template +inline AttributeSet::Descriptor::Ptr +makeDescriptorUnique(PointDataTreeT& tree); + + +/// @brief Toggle the streaming mode on all attributes in the tree to collapse the attributes +/// after deconstructing a bound AttributeHandle to each array. This results in better +/// memory efficiency when the data is streamed into another data structure +/// (typically for rendering). +/// +/// @param tree the PointDataTree. +/// @param on @c true to enable streaming +/// +/// @note Multiple threads cannot safely access the same AttributeArray when using streaming. +template +inline void +setStreamingMode(PointDataTreeT& tree, bool on = true); + + +/// @brief Sequentially pre-fetch all delayed-load voxel and attribute data from disk in order +/// to accelerate subsequent random access. +/// +/// @param tree the PointDataTree. +/// @param position if enabled, prefetch the position attribute (default is on) +/// @param otherAttributes if enabled, prefetch all other attributes (default is on) +template +inline void +prefetch(PointDataTreeT& tree, bool position = true, bool otherAttributes = true); + + +//////////////////////////////////////// + + +template +class PointDataLeafNode : public tree::LeafNode, io::MultiPass { + +public: + using LeafNodeType = PointDataLeafNode; + using Ptr = std::shared_ptr; + + using ValueType = T; + using ValueTypePair = std::pair; + using IndexArray = std::vector; + + using Descriptor = AttributeSet::Descriptor; + + //////////////////////////////////////// + + // The following methods had to be copied from the LeafNode class + // to make the derived PointDataLeafNode class compatible with the tree structure. + + using BaseLeaf = tree::LeafNode; + using NodeMaskType = util::NodeMask; + + using BaseLeaf::LOG2DIM; + using BaseLeaf::TOTAL; + using BaseLeaf::DIM; + using BaseLeaf::NUM_VALUES; + using BaseLeaf::NUM_VOXELS; + using BaseLeaf::SIZE; + using BaseLeaf::LEVEL; + + /// Default constructor + PointDataLeafNode() + : mAttributeSet(new AttributeSet) { } + + ~PointDataLeafNode() = default; + + /// Construct using deep copy of other PointDataLeafNode + explicit PointDataLeafNode(const PointDataLeafNode& other) + : BaseLeaf(other) + , mAttributeSet(new AttributeSet(*other.mAttributeSet)) { } + + /// Construct using supplied origin, value and active status + explicit + PointDataLeafNode(const Coord& coords, const T& value = zeroVal(), bool active = false) + : BaseLeaf(coords, zeroVal(), active) + , mAttributeSet(new AttributeSet) { assertNonModifiableUnlessZero(value); } + + /// Construct using supplied origin, value and active status + /// use attribute map from another PointDataLeafNode + PointDataLeafNode(const PointDataLeafNode& other, const Coord& coords, + const T& value = zeroVal(), bool active = false) + : BaseLeaf(coords, zeroVal(), active) + , mAttributeSet(new AttributeSet(*other.mAttributeSet)) + { + assertNonModifiableUnlessZero(value); + } + + // Copy-construct from a PointIndexLeafNode with the same configuration but a different ValueType. + template + PointDataLeafNode(const tools::PointIndexLeafNode& other) + : BaseLeaf(other) + , mAttributeSet(new AttributeSet) { } + + // Copy-construct from a LeafNode with the same configuration but a different ValueType. + // Used for topology copies - explicitly sets the value (background) to zeroVal + template + PointDataLeafNode(const tree::LeafNode& other, const T& value, TopologyCopy) + : BaseLeaf(other, zeroVal(), TopologyCopy()) + , mAttributeSet(new AttributeSet) { assertNonModifiableUnlessZero(value); } + + // Copy-construct from a LeafNode with the same configuration but a different ValueType. + // Used for topology copies - explicitly sets the on and off value (background) to zeroVal + template + PointDataLeafNode(const tree::LeafNode& other, const T& /*offValue*/, const T& /*onValue*/, TopologyCopy) + : BaseLeaf(other, zeroVal(), zeroVal(), TopologyCopy()) + , mAttributeSet(new AttributeSet) { } + + PointDataLeafNode(PartialCreate, const Coord& coords, + const T& value = zeroVal(), bool active = false) + : BaseLeaf(PartialCreate(), coords, value, active) + , mAttributeSet(new AttributeSet) { assertNonModifiableUnlessZero(value); } + +public: + + /// Retrieve the attribute set. + const AttributeSet& attributeSet() const { return *mAttributeSet; } + + /// @brief Create a new attribute set. Existing attributes will be removed. + void initializeAttributes(const Descriptor::Ptr& descriptor, const Index arrayLength, + const AttributeArray::ScopedRegistryLock* lock = nullptr); + /// @brief Clear the attribute set. + void clearAttributes(const bool updateValueMask = true, + const AttributeArray::ScopedRegistryLock* lock = nullptr); + + /// @brief Returns @c true if an attribute with this index exists. + /// @param pos Index of the attribute + bool hasAttribute(const size_t pos) const; + /// @brief Returns @c true if an attribute with this name exists. + /// @param attributeName Name of the attribute + bool hasAttribute(const Name& attributeName) const; + + /// @brief Append an attribute to the leaf. + /// @param expected Existing descriptor is expected to match this parameter. + /// @param replacement New descriptor to replace the existing one. + /// @param pos Index of the new attribute in the descriptor replacement. + /// @param strideOrTotalSize Stride of the attribute array (if constantStride), total size otherwise + /// @param constantStride if @c false, stride is interpreted as total size of the array + /// @param lock an optional scoped registry lock to avoid contention + AttributeArray::Ptr appendAttribute(const Descriptor& expected, Descriptor::Ptr& replacement, + const size_t pos, const Index strideOrTotalSize = 1, + const bool constantStride = true, + const AttributeArray::ScopedRegistryLock* lock = nullptr); + + /// @brief Drop list of attributes. + /// @param pos vector of attribute indices to drop + /// @param expected Existing descriptor is expected to match this parameter. + /// @param replacement New descriptor to replace the existing one. + void dropAttributes(const std::vector& pos, + const Descriptor& expected, Descriptor::Ptr& replacement); + /// @brief Reorder attribute set. + /// @param replacement New descriptor to replace the existing one. + void reorderAttributes(const Descriptor::Ptr& replacement); + /// @brief Rename attributes in attribute set (order must remain the same). + /// @param expected Existing descriptor is expected to match this parameter. + /// @param replacement New descriptor to replace the existing one. + void renameAttributes(const Descriptor& expected, Descriptor::Ptr& replacement); + /// @brief Compact all attributes in attribute set. + void compactAttributes(); + + /// @brief Replace the underlying attribute set with the given @a attributeSet. + /// @details This leaf will assume ownership of the given attribute set. The descriptors must + /// match and the voxel offsets values will need updating if the point order is different. + /// @throws ValueError if @a allowMismatchingDescriptors is @c false and the descriptors + /// do not match + void replaceAttributeSet(AttributeSet* attributeSet, bool allowMismatchingDescriptors = false); + + /// @brief Replace the descriptor with a new one + /// The new Descriptor must exactly match the old one + void resetDescriptor(const Descriptor::Ptr& replacement); + + /// @brief Sets all of the voxel offset values on this leaf, from the given vector + /// of @a offsets. If @a updateValueMask is true, then the active value mask will + /// be updated so voxels with points are active and empty voxels are inactive. + void setOffsets(const std::vector& offsets, const bool updateValueMask = true); + + /// @brief Throws an error if the voxel values on this leaf are not monotonically + /// increasing or within the bounds of the attribute arrays + void validateOffsets() const; + + /// @brief Read-write attribute array reference from index + /// @details Attribute arrays can be shared across leaf nodes, so non-const + /// access will deep-copy the array to make it unique. Always prefer + /// accessing const arrays where possible to eliminate this copying. + /// { + AttributeArray& attributeArray(const size_t pos); + const AttributeArray& attributeArray(const size_t pos) const; + const AttributeArray& constAttributeArray(const size_t pos) const; + /// } + /// @brief Read-write attribute array reference from name + /// @details Attribute arrays can be shared across leaf nodes, so non-const + /// access will deep-copy the array to make it unique. Always prefer + /// accessing const arrays where possible to eliminate this copying. + /// { + AttributeArray& attributeArray(const Name& attributeName); + const AttributeArray& attributeArray(const Name& attributeName) const; + const AttributeArray& constAttributeArray(const Name& attributeName) const; + /// } + + /// @brief Read-only group handle from group index + GroupHandle groupHandle(const AttributeSet::Descriptor::GroupIndex& index) const; + /// @brief Read-only group handle from group name + GroupHandle groupHandle(const Name& group) const; + /// @brief Read-write group handle from group index + GroupWriteHandle groupWriteHandle(const AttributeSet::Descriptor::GroupIndex& index); + /// @brief Read-write group handle from group name + GroupWriteHandle groupWriteHandle(const Name& name); + + /// @brief Compute the total point count for the leaf + Index64 pointCount() const; + /// @brief Compute the total active (on) point count for the leaf + Index64 onPointCount() const; + /// @brief Compute the total inactive (off) point count for the leaf + Index64 offPointCount() const; + /// @brief Compute the point count in a specific group for the leaf + Index64 groupPointCount(const Name& groupName) const; + + /// @brief Activate voxels with non-zero points, deactivate voxels with zero points. + void updateValueMask(); + + //////////////////////////////////////// + + void setOffsetOn(Index offset, const ValueType& val); + void setOffsetOnly(Index offset, const ValueType& val); + + /// @brief Return @c true if the given node (which may have a different @c ValueType + /// than this node) has the same active value topology as this node. + template + bool hasSameTopology(const PointDataLeafNode* other) const { + return BaseLeaf::hasSameTopology(other); + } + + /// Check for buffer, state and origin equivalence first. + /// If this returns true, do a deeper comparison on the attribute set to check + bool operator==(const PointDataLeafNode& other) const { + if(BaseLeaf::operator==(other) != true) return false; + return (*this->mAttributeSet == *other.mAttributeSet); + } + + bool operator!=(const PointDataLeafNode& other) const { return !(other == *this); } + + void addLeaf(PointDataLeafNode*) {} + template + void addLeafAndCache(PointDataLeafNode*, AccessorT&) {} + + //@{ + /// @brief Return a pointer to this node. + PointDataLeafNode* touchLeaf(const Coord&) { return this; } + template + PointDataLeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + PointDataLeafNode* probeLeaf(const Coord&) { return this; } + template + PointDataLeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + //@} + + //@{ + /// @brief Return a @const pointer to this node. + const PointDataLeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const PointDataLeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } + template + const PointDataLeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const PointDataLeafNode* probeLeaf(const Coord&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + // I/O methods + + void readTopology(std::istream& is, bool fromHalf = false); + void writeTopology(std::ostream& os, bool toHalf = false) const; + + Index buffers() const; + + void readBuffers(std::istream& is, bool fromHalf = false); + void readBuffers(std::istream& is, const CoordBBox&, bool fromHalf = false); + void writeBuffers(std::ostream& os, bool toHalf = false) const; + + + Index64 memUsage() const; + + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; + + /// @brief Return the bounding box of this node, i.e., the full index space + /// spanned by this leaf node. + CoordBBox getNodeBoundingBox() const; + + //////////////////////////////////////// + + // Disable all write methods to avoid unintentional changes + // to the point-array offsets. + + void assertNonmodifiable() { + assert(false && "Cannot modify voxel values in a PointDataTree."); + } + + // some methods silently ignore attempts to modify the + // point-array offsets if a zero value is used + + void assertNonModifiableUnlessZero(const ValueType& value) { + if (value != zeroVal()) this->assertNonmodifiable(); + } + + void setActiveState(const Coord& xyz, bool on) { BaseLeaf::setActiveState(xyz, on); } + void setActiveState(Index offset, bool on) { BaseLeaf::setActiveState(offset, on); } + + void setValueOnly(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOnly(Index, const ValueType&) { assertNonmodifiable(); } + + void setValueOff(const Coord& xyz) { BaseLeaf::setValueOff(xyz); } + void setValueOff(Index offset) { BaseLeaf::setValueOff(offset); } + + void setValueOff(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOff(Index, const ValueType&) { assertNonmodifiable(); } + + void setValueOn(const Coord& xyz) { BaseLeaf::setValueOn(xyz); } + void setValueOn(Index offset) { BaseLeaf::setValueOn(offset); } + + void setValueOn(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOn(Index, const ValueType&) { assertNonmodifiable(); } + + void setValue(const Coord&, const ValueType&) { assertNonmodifiable(); } + + void setValuesOn() { BaseLeaf::setValuesOn(); } + void setValuesOff() { BaseLeaf::setValuesOff(); } + + template + void modifyValue(Index, const ModifyOp&) { assertNonmodifiable(); } + + template + void modifyValue(const Coord&, const ModifyOp&) { assertNonmodifiable(); } + + template + void modifyValueAndActiveState(const Coord&, const ModifyOp&) { assertNonmodifiable(); } + + // clipping is not yet supported + void clip(const CoordBBox&, const ValueType& value) { assertNonModifiableUnlessZero(value); } + + void fill(const CoordBBox&, const ValueType&, bool); + void fill(const ValueType& value) { assertNonModifiableUnlessZero(value); } + void fill(const ValueType&, bool); + + template + void setValueOnlyAndCache(const Coord&, const ValueType&, AccessorT&) {assertNonmodifiable();} + + template + void modifyValueAndActiveStateAndCache(const Coord&, const ModifyOp&, AccessorT&) { + assertNonmodifiable(); + } + + template + void setValueOffAndCache(const Coord&, const ValueType&, AccessorT&) { assertNonmodifiable(); } + + template + void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& parent) { + BaseLeaf::setActiveStateAndCache(xyz, on, parent); + } + + void resetBackground(const ValueType&, const ValueType& newBackground) { + assertNonModifiableUnlessZero(newBackground); + } + + void signedFloodFill(const ValueType&) { assertNonmodifiable(); } + void signedFloodFill(const ValueType&, const ValueType&) { assertNonmodifiable(); } + + void negate() { assertNonmodifiable(); } + + friend class ::TestPointDataLeaf; + + using ValueOn = typename BaseLeaf::ValueOn; + using ValueOff = typename BaseLeaf::ValueOff; + using ValueAll = typename BaseLeaf::ValueAll; + +private: + std::unique_ptr mAttributeSet; + uint16_t mVoxelBufferSize = 0; + +protected: + using ChildOn = typename BaseLeaf::ChildOn; + using ChildOff = typename BaseLeaf::ChildOff; + using ChildAll = typename BaseLeaf::ChildAll; + + using MaskOnIterator = typename NodeMaskType::OnIterator; + using MaskOffIterator = typename NodeMaskType::OffIterator; + using MaskDenseIterator = typename NodeMaskType::DenseIterator; + + // During topology-only construction, access is needed + // to protected/private members of other template instances. + template friend class PointDataLeafNode; + + friend class tree::IteratorBase; + friend class tree::IteratorBase; + friend class tree::IteratorBase; + +public: + /// @brief Leaf value voxel iterator + ValueVoxelCIter beginValueVoxel(const Coord& ijk) const; + +public: + +#if defined(_MSC_VER) && (_MSC_VER < 1914) + using ValueOnIter = typename BaseLeaf::ValueIter< + MaskOnIterator, PointDataLeafNode, const ValueType, ValueOn>; + using ValueOnCIter = typename BaseLeaf::ValueIter< + MaskOnIterator, const PointDataLeafNode, const ValueType, ValueOn>; + using ValueOffIter = typename BaseLeaf::ValueIter< + MaskOffIterator, PointDataLeafNode, const ValueType, ValueOff>; + using ValueOffCIter = typename BaseLeaf::ValueIter< + MaskOffIterator,const PointDataLeafNode,const ValueType,ValueOff>; + using ValueAllIter = typename BaseLeaf::ValueIter< + MaskDenseIterator, PointDataLeafNode, const ValueType, ValueAll>; + using ValueAllCIter = typename BaseLeaf::ValueIter< + MaskDenseIterator,const PointDataLeafNode,const ValueType,ValueAll>; + using ChildOnIter = typename BaseLeaf::ChildIter< + MaskOnIterator, PointDataLeafNode, ChildOn>; + using ChildOnCIter = typename BaseLeaf::ChildIter< + MaskOnIterator, const PointDataLeafNode, ChildOn>; + using ChildOffIter = typename BaseLeaf::ChildIter< + MaskOffIterator, PointDataLeafNode, ChildOff>; + using ChildOffCIter = typename BaseLeaf::ChildIter< + MaskOffIterator, const PointDataLeafNode, ChildOff>; + using ChildAllIter = typename BaseLeaf::DenseIter< + PointDataLeafNode, ValueType, ChildAll>; + using ChildAllCIter = typename BaseLeaf::DenseIter< + const PointDataLeafNode, const ValueType, ChildAll>; +#else + using ValueOnIter = typename BaseLeaf::template ValueIter< + MaskOnIterator, PointDataLeafNode, const ValueType, ValueOn>; + using ValueOnCIter = typename BaseLeaf::template ValueIter< + MaskOnIterator, const PointDataLeafNode, const ValueType, ValueOn>; + using ValueOffIter = typename BaseLeaf::template ValueIter< + MaskOffIterator, PointDataLeafNode, const ValueType, ValueOff>; + using ValueOffCIter = typename BaseLeaf::template ValueIter< + MaskOffIterator,const PointDataLeafNode,const ValueType,ValueOff>; + using ValueAllIter = typename BaseLeaf::template ValueIter< + MaskDenseIterator, PointDataLeafNode, const ValueType, ValueAll>; + using ValueAllCIter = typename BaseLeaf::template ValueIter< + MaskDenseIterator,const PointDataLeafNode,const ValueType,ValueAll>; + using ChildOnIter = typename BaseLeaf::template ChildIter< + MaskOnIterator, PointDataLeafNode, ChildOn>; + using ChildOnCIter = typename BaseLeaf::template ChildIter< + MaskOnIterator, const PointDataLeafNode, ChildOn>; + using ChildOffIter = typename BaseLeaf::template ChildIter< + MaskOffIterator, PointDataLeafNode, ChildOff>; + using ChildOffCIter = typename BaseLeaf::template ChildIter< + MaskOffIterator, const PointDataLeafNode, ChildOff>; + using ChildAllIter = typename BaseLeaf::template DenseIter< + PointDataLeafNode, ValueType, ChildAll>; + using ChildAllCIter = typename BaseLeaf::template DenseIter< + const PointDataLeafNode, const ValueType, ChildAll>; +#endif + + using IndexVoxelIter = IndexIter; + using IndexAllIter = IndexIter; + using IndexOnIter = IndexIter; + using IndexOffIter = IndexIter; + + /// @brief Leaf index iterator + IndexAllIter beginIndexAll() const + { + NullFilter filter; + return this->beginIndex(filter); + } + IndexOnIter beginIndexOn() const + { + NullFilter filter; + return this->beginIndex(filter); + } + IndexOffIter beginIndexOff() const + { + NullFilter filter; + return this->beginIndex(filter); + } + + template + IndexIter beginIndex(const FilterT& filter) const; + + /// @brief Filtered leaf index iterator + template + IndexIter beginIndexAll(const FilterT& filter) const + { + return this->beginIndex(filter); + } + template + IndexIter beginIndexOn(const FilterT& filter) const + { + return this->beginIndex(filter); + } + template + IndexIter beginIndexOff(const FilterT& filter) const + { + return this->beginIndex(filter); + } + + /// @brief Leaf index iterator from voxel + IndexVoxelIter beginIndexVoxel(const Coord& ijk) const; + + /// @brief Filtered leaf index iterator from voxel + template + IndexIter beginIndexVoxel(const Coord& ijk, const FilterT& filter) const; + +#define VMASK_ this->getValueMask() + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(VMASK_.beginOn(), this); } + ValueOnCIter beginValueOn() const { return ValueOnCIter(VMASK_.beginOn(), this); } + ValueOnIter beginValueOn() { return ValueOnIter(VMASK_.beginOn(), this); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(VMASK_.beginOff(), this); } + ValueOffCIter beginValueOff() const { return ValueOffCIter(VMASK_.beginOff(), this); } + ValueOffIter beginValueOff() { return ValueOffIter(VMASK_.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(VMASK_.beginDense(), this); } + ValueAllCIter beginValueAll() const { return ValueAllCIter(VMASK_.beginDense(), this); } + ValueAllIter beginValueAll() { return ValueAllIter(VMASK_.beginDense(), this); } + + ValueOnCIter cendValueOn() const { return ValueOnCIter(VMASK_.endOn(), this); } + ValueOnCIter endValueOn() const { return ValueOnCIter(VMASK_.endOn(), this); } + ValueOnIter endValueOn() { return ValueOnIter(VMASK_.endOn(), this); } + ValueOffCIter cendValueOff() const { return ValueOffCIter(VMASK_.endOff(), this); } + ValueOffCIter endValueOff() const { return ValueOffCIter(VMASK_.endOff(), this); } + ValueOffIter endValueOff() { return ValueOffIter(VMASK_.endOff(), this); } + ValueAllCIter cendValueAll() const { return ValueAllCIter(VMASK_.endDense(), this); } + ValueAllCIter endValueAll() const { return ValueAllCIter(VMASK_.endDense(), this); } + ValueAllIter endValueAll() { return ValueAllIter(VMASK_.endDense(), this); } + + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnCIter beginChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnIter beginChildOn() { return ChildOnIter(VMASK_.endOn(), this); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffCIter beginChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffIter beginChildOff() { return ChildOffIter(VMASK_.endOff(), this); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(VMASK_.beginDense(), this); } + ChildAllCIter beginChildAll() const { return ChildAllCIter(VMASK_.beginDense(), this); } + ChildAllIter beginChildAll() { return ChildAllIter(VMASK_.beginDense(), this); } + + ChildOnCIter cendChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnCIter endChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnIter endChildOn() { return ChildOnIter(VMASK_.endOn(), this); } + ChildOffCIter cendChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffCIter endChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffIter endChildOff() { return ChildOffIter(VMASK_.endOff(), this); } + ChildAllCIter cendChildAll() const { return ChildAllCIter(VMASK_.endDense(), this); } + ChildAllCIter endChildAll() const { return ChildAllCIter(VMASK_.endDense(), this); } + ChildAllIter endChildAll() { return ChildAllIter(VMASK_.endDense(), this); } +#undef VMASK_ +}; // struct PointDataLeafNode + +//////////////////////////////////////// + +// PointDataLeafNode implementation + +template +inline void +PointDataLeafNode::initializeAttributes(const Descriptor::Ptr& descriptor, const Index arrayLength, + const AttributeArray::ScopedRegistryLock* lock) +{ + if (descriptor->size() != 1 || + descriptor->find("P") == AttributeSet::INVALID_POS || + descriptor->valueType(0) != typeNameAsString()) + { + OPENVDB_THROW(IndexError, "Initializing attributes only allowed with one Vec3f position attribute."); + } + + mAttributeSet.reset(new AttributeSet(descriptor, arrayLength, lock)); +} + +template +inline void +PointDataLeafNode::clearAttributes(const bool updateValueMask, + const AttributeArray::ScopedRegistryLock* lock) +{ + mAttributeSet.reset(new AttributeSet(*mAttributeSet, 0, lock)); + + // zero voxel values + + this->buffer().fill(ValueType(0)); + + // if updateValueMask, also de-activate all voxels + + if (updateValueMask) this->setValuesOff(); +} + +template +inline bool +PointDataLeafNode::hasAttribute(const size_t pos) const +{ + return pos < mAttributeSet->size(); +} + +template +inline bool +PointDataLeafNode::hasAttribute(const Name& attributeName) const +{ + const size_t pos = mAttributeSet->find(attributeName); + return pos != AttributeSet::INVALID_POS; +} + +template +inline AttributeArray::Ptr +PointDataLeafNode::appendAttribute( const Descriptor& expected, Descriptor::Ptr& replacement, + const size_t pos, const Index strideOrTotalSize, + const bool constantStride, + const AttributeArray::ScopedRegistryLock* lock) +{ + return mAttributeSet->appendAttribute( + expected, replacement, pos, strideOrTotalSize, constantStride, lock); +} + +template +inline void +PointDataLeafNode::dropAttributes(const std::vector& pos, + const Descriptor& expected, Descriptor::Ptr& replacement) +{ + mAttributeSet->dropAttributes(pos, expected, replacement); +} + +template +inline void +PointDataLeafNode::reorderAttributes(const Descriptor::Ptr& replacement) +{ + mAttributeSet->reorderAttributes(replacement); +} + +template +inline void +PointDataLeafNode::renameAttributes(const Descriptor& expected, Descriptor::Ptr& replacement) +{ + mAttributeSet->renameAttributes(expected, replacement); +} + +template +inline void +PointDataLeafNode::compactAttributes() +{ + for (size_t i = 0; i < mAttributeSet->size(); i++) { + AttributeArray* array = mAttributeSet->get(i); + array->compact(); + } +} + +template +inline void +PointDataLeafNode::replaceAttributeSet(AttributeSet* attributeSet, bool allowMismatchingDescriptors) +{ + if (!attributeSet) { + OPENVDB_THROW(ValueError, "Cannot replace with a null attribute set"); + } + + if (!allowMismatchingDescriptors && mAttributeSet->descriptor() != attributeSet->descriptor()) { + OPENVDB_THROW(ValueError, "Attribute set descriptors are not equal."); + } + + mAttributeSet.reset(attributeSet); +} + +template +inline void +PointDataLeafNode::resetDescriptor(const Descriptor::Ptr& replacement) +{ + mAttributeSet->resetDescriptor(replacement); +} + +template +inline void +PointDataLeafNode::setOffsets(const std::vector& offsets, const bool updateValueMask) +{ + if (offsets.size() != LeafNodeType::NUM_VALUES) { + OPENVDB_THROW(ValueError, "Offset vector size doesn't match number of voxels.") + } + + for (Index index = 0; index < offsets.size(); ++index) { + setOffsetOnly(index, offsets[index]); + } + + if (updateValueMask) this->updateValueMask(); +} + +template +inline void +PointDataLeafNode::validateOffsets() const +{ + // Ensure all of the offset values are monotonically increasing + for (Index index = 1; index < BaseLeaf::SIZE; ++index) { + if (this->getValue(index-1) > this->getValue(index)) { + OPENVDB_THROW(ValueError, "Voxel offset values are not monotonically increasing"); + } + } + + // Ensure all attribute arrays are of equal length + for (size_t attributeIndex = 1; attributeIndex < mAttributeSet->size(); ++attributeIndex ) { + if (mAttributeSet->getConst(attributeIndex-1)->size() != mAttributeSet->getConst(attributeIndex)->size()) { + OPENVDB_THROW(ValueError, "Attribute arrays have inconsistent length"); + } + } + + // Ensure the last voxel's offset value matches the size of each attribute array + if (mAttributeSet->size() > 0 && this->getValue(BaseLeaf::SIZE-1) != mAttributeSet->getConst(0)->size()) { + OPENVDB_THROW(ValueError, "Last voxel offset value does not match attribute array length"); + } +} + +template +inline AttributeArray& +PointDataLeafNode::attributeArray(const size_t pos) +{ + if (pos >= mAttributeSet->size()) OPENVDB_THROW(LookupError, "Attribute Out Of Range - " << pos); + return *mAttributeSet->get(pos); +} + +template +inline const AttributeArray& +PointDataLeafNode::attributeArray(const size_t pos) const +{ + if (pos >= mAttributeSet->size()) OPENVDB_THROW(LookupError, "Attribute Out Of Range - " << pos); + return *mAttributeSet->getConst(pos); +} + +template +inline const AttributeArray& +PointDataLeafNode::constAttributeArray(const size_t pos) const +{ + return this->attributeArray(pos); +} + +template +inline AttributeArray& +PointDataLeafNode::attributeArray(const Name& attributeName) +{ + const size_t pos = mAttributeSet->find(attributeName); + if (pos == AttributeSet::INVALID_POS) OPENVDB_THROW(LookupError, "Attribute Not Found - " << attributeName); + return *mAttributeSet->get(pos); +} + +template +inline const AttributeArray& +PointDataLeafNode::attributeArray(const Name& attributeName) const +{ + const size_t pos = mAttributeSet->find(attributeName); + if (pos == AttributeSet::INVALID_POS) OPENVDB_THROW(LookupError, "Attribute Not Found - " << attributeName); + return *mAttributeSet->getConst(pos); +} + +template +inline const AttributeArray& +PointDataLeafNode::constAttributeArray(const Name& attributeName) const +{ + return this->attributeArray(attributeName); +} + +template +inline GroupHandle +PointDataLeafNode::groupHandle(const AttributeSet::Descriptor::GroupIndex& index) const +{ + const AttributeArray& array = this->attributeArray(index.first); + assert(isGroup(array)); + + const GroupAttributeArray& groupArray = GroupAttributeArray::cast(array); + + return GroupHandle(groupArray, index.second); +} + +template +inline GroupHandle +PointDataLeafNode::groupHandle(const Name& name) const +{ + const AttributeSet::Descriptor::GroupIndex index = this->attributeSet().groupIndex(name); + return this->groupHandle(index); +} + +template +inline GroupWriteHandle +PointDataLeafNode::groupWriteHandle(const AttributeSet::Descriptor::GroupIndex& index) +{ + AttributeArray& array = this->attributeArray(index.first); + assert(isGroup(array)); + + GroupAttributeArray& groupArray = GroupAttributeArray::cast(array); + + return GroupWriteHandle(groupArray, index.second); +} + +template +inline GroupWriteHandle +PointDataLeafNode::groupWriteHandle(const Name& name) +{ + const AttributeSet::Descriptor::GroupIndex index = this->attributeSet().groupIndex(name); + return this->groupWriteHandle(index); +} + +template +template +inline IndexIter +PointDataLeafNode::beginIndex(const FilterT& filter) const +{ + // generate no-op iterator if filter evaluates no indices + + if (filter.state() == index::NONE) { + return IndexIter(ValueIterT(), filter); + } + + // copy filter to ensure thread-safety + + FilterT newFilter(filter); + newFilter.reset(*this); + + using IterTraitsT = tree::IterTraits; + + // construct the value iterator and reset the filter to use this leaf + + ValueIterT valueIter = IterTraitsT::begin(*this); + + return IndexIter(valueIter, newFilter); +} + +template +inline ValueVoxelCIter +PointDataLeafNode::beginValueVoxel(const Coord& ijk) const +{ + const Index index = LeafNodeType::coordToOffset(ijk); + assert(index < BaseLeaf::SIZE); + const ValueType end = this->getValue(index); + const ValueType start = (index == 0) ? ValueType(0) : this->getValue(index - 1); + return ValueVoxelCIter(start, end); +} + +template +inline typename PointDataLeafNode::IndexVoxelIter +PointDataLeafNode::beginIndexVoxel(const Coord& ijk) const +{ + ValueVoxelCIter iter = this->beginValueVoxel(ijk); + return IndexVoxelIter(iter, NullFilter()); +} + +template +template +inline IndexIter +PointDataLeafNode::beginIndexVoxel(const Coord& ijk, const FilterT& filter) const +{ + ValueVoxelCIter iter = this->beginValueVoxel(ijk); + FilterT newFilter(filter); + newFilter.reset(*this); + return IndexIter(iter, newFilter); +} + +template +inline Index64 +PointDataLeafNode::pointCount() const +{ + return this->getLastValue(); +} + +template +inline Index64 +PointDataLeafNode::onPointCount() const +{ + if (this->isEmpty()) return 0; + else if (this->isDense()) return this->pointCount(); + return iterCount(this->beginIndexOn()); +} + +template +inline Index64 +PointDataLeafNode::offPointCount() const +{ + if (this->isEmpty()) return this->pointCount(); + else if (this->isDense()) return 0; + return iterCount(this->beginIndexOff()); +} + +template +inline Index64 +PointDataLeafNode::groupPointCount(const Name& groupName) const +{ + if (!this->attributeSet().descriptor().hasGroup(groupName)) { + return Index64(0); + } + GroupFilter filter(groupName, this->attributeSet()); + if (filter.state() == index::ALL) { + return this->pointCount(); + } else { + return iterCount(this->beginIndexAll(filter)); + } +} + +template +inline void +PointDataLeafNode::updateValueMask() +{ + ValueType start = 0, end = 0; + for (Index n = 0; n < LeafNodeType::NUM_VALUES; n++) { + end = this->getValue(n); + this->setValueMask(n, (end - start) > 0); + start = end; + } +} + +template +inline void +PointDataLeafNode::setOffsetOn(Index offset, const ValueType& val) +{ + this->buffer().setValue(offset, val); + this->setValueMaskOn(offset); +} + +template +inline void +PointDataLeafNode::setOffsetOnly(Index offset, const ValueType& val) +{ + this->buffer().setValue(offset, val); +} + +template +inline void +PointDataLeafNode::readTopology(std::istream& is, bool fromHalf) +{ + BaseLeaf::readTopology(is, fromHalf); +} + +template +inline void +PointDataLeafNode::writeTopology(std::ostream& os, bool toHalf) const +{ + BaseLeaf::writeTopology(os, toHalf); +} + +template +inline Index +PointDataLeafNode::buffers() const +{ + return Index( /*voxel buffer sizes*/ 1 + + /*voxel buffers*/ 1 + + /*attribute metadata*/ 1 + + /*attribute uniform values*/ mAttributeSet->size() + + /*attribute buffers*/ mAttributeSet->size() + + /*cleanup*/ 1); +} + +template +inline void +PointDataLeafNode::readBuffers(std::istream& is, bool fromHalf) +{ + this->readBuffers(is, CoordBBox::inf(), fromHalf); +} + +template +inline void +PointDataLeafNode::readBuffers(std::istream& is, const CoordBBox& /*bbox*/, bool fromHalf) +{ + struct Local + { + static void destroyPagedStream(const io::StreamMetadata::AuxDataMap& auxData, const Index index) + { + // if paged stream exists, delete it + std::string key("paged:" + std::to_string(index)); + auto it = auxData.find(key); + if (it != auxData.end()) { + (const_cast(auxData)).erase(it); + } + } + + static compression::PagedInputStream& getOrInsertPagedStream( const io::StreamMetadata::AuxDataMap& auxData, + const Index index) + { + std::string key("paged:" + std::to_string(index)); + auto it = auxData.find(key); + if (it != auxData.end()) { + return *(boost::any_cast(it->second)); + } + else { + compression::PagedInputStream::Ptr pagedStream = std::make_shared(); + (const_cast(auxData))[key] = pagedStream; + return *pagedStream; + } + } + + static bool hasMatchingDescriptor(const io::StreamMetadata::AuxDataMap& auxData) + { + std::string matchingKey("hasMatchingDescriptor"); + auto itMatching = auxData.find(matchingKey); + return itMatching != auxData.end(); + } + + static void clearMatchingDescriptor(const io::StreamMetadata::AuxDataMap& auxData) + { + std::string matchingKey("hasMatchingDescriptor"); + std::string descriptorKey("descriptorPtr"); + auto itMatching = auxData.find(matchingKey); + auto itDescriptor = auxData.find(descriptorKey); + if (itMatching != auxData.end()) (const_cast(auxData)).erase(itMatching); + if (itDescriptor != auxData.end()) (const_cast(auxData)).erase(itDescriptor); + } + + static void insertDescriptor( const io::StreamMetadata::AuxDataMap& auxData, + const Descriptor::Ptr descriptor) + { + std::string descriptorKey("descriptorPtr"); + std::string matchingKey("hasMatchingDescriptor"); + auto itMatching = auxData.find(matchingKey); + if (itMatching == auxData.end()) { + // if matching bool is not found, insert "true" and the descriptor + (const_cast(auxData))[matchingKey] = true; + (const_cast(auxData))[descriptorKey] = descriptor; + } + } + + static AttributeSet::Descriptor::Ptr retrieveMatchingDescriptor(const io::StreamMetadata::AuxDataMap& auxData) + { + std::string descriptorKey("descriptorPtr"); + auto itDescriptor = auxData.find(descriptorKey); + assert(itDescriptor != auxData.end()); + const Descriptor::Ptr descriptor = boost::any_cast(itDescriptor->second); + return descriptor; + } + }; + + const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(is); + + if (!meta) { + OPENVDB_THROW(IoError, "Cannot read in a PointDataLeaf without StreamMetadata."); + } + + const Index pass(static_cast(meta->pass())); + const Index maximumPass(static_cast(meta->pass() >> 16)); + + const Index attributes = (maximumPass - 4) / 2; + + if (pass == 0) { + // pass 0 - voxel data sizes + is.read(reinterpret_cast(&mVoxelBufferSize), sizeof(uint16_t)); + Local::clearMatchingDescriptor(meta->auxData()); + } + else if (pass == 1) { + // pass 1 - descriptor and attribute metadata + if (Local::hasMatchingDescriptor(meta->auxData())) { + AttributeSet::Descriptor::Ptr descriptor = Local::retrieveMatchingDescriptor(meta->auxData()); + mAttributeSet->resetDescriptor(descriptor, /*allowMismatchingDescriptors=*/true); + } + else { + uint8_t header; + is.read(reinterpret_cast(&header), sizeof(uint8_t)); + mAttributeSet->readDescriptor(is); + if (header & uint8_t(1)) { + AttributeSet::DescriptorPtr descriptor = mAttributeSet->descriptorPtr(); + Local::insertDescriptor(meta->auxData(), descriptor); + } + // a forwards-compatibility mechanism for future use, + // if a 0x2 bit is set, read and skip over a specific number of bytes + if (header & uint8_t(2)) { + uint64_t bytesToSkip; + is.read(reinterpret_cast(&bytesToSkip), sizeof(uint64_t)); + if (bytesToSkip > uint64_t(0)) { + auto metadata = io::getStreamMetadataPtr(is); + if (metadata && metadata->seekable()) { + is.seekg(bytesToSkip, std::ios_base::cur); + } + else { + std::vector tempData(bytesToSkip); + is.read(reinterpret_cast(&tempData[0]), bytesToSkip); + } + } + } + // this reader is only able to read headers with 0x1 and 0x2 bits set + if (header > uint8_t(3)) { + OPENVDB_THROW(IoError, "Unrecognised header flags in PointDataLeafNode"); + } + } + mAttributeSet->readMetadata(is); + } + else if (pass < (attributes + 2)) { + // pass 2...n+2 - attribute uniform values + const size_t attributeIndex = pass - 2; + AttributeArray* array = attributeIndex < mAttributeSet->size() ? + mAttributeSet->get(attributeIndex) : nullptr; + if (array) { + compression::PagedInputStream& pagedStream = + Local::getOrInsertPagedStream(meta->auxData(), static_cast(attributeIndex)); + pagedStream.setInputStream(is); + pagedStream.setSizeOnly(true); + array->readPagedBuffers(pagedStream); + } + } + else if (pass == attributes + 2) { + // pass n+2 - voxel data + + const Index passValue(meta->pass()); + + // StreamMetadata pass variable used to temporarily store voxel buffer size + io::StreamMetadata& nonConstMeta = const_cast(*meta); + nonConstMeta.setPass(mVoxelBufferSize); + + // readBuffers() calls readCompressedValues specialization above + BaseLeaf::readBuffers(is, fromHalf); + + // pass now reset to original value + nonConstMeta.setPass(passValue); + } + else if (pass < (attributes*2 + 3)) { + // pass n+2..2n+2 - attribute buffers + const Index attributeIndex = pass - attributes - 3; + AttributeArray* array = attributeIndex < mAttributeSet->size() ? + mAttributeSet->get(attributeIndex) : nullptr; + if (array) { + compression::PagedInputStream& pagedStream = + Local::getOrInsertPagedStream(meta->auxData(), attributeIndex); + pagedStream.setInputStream(is); + pagedStream.setSizeOnly(false); + array->readPagedBuffers(pagedStream); + } + // cleanup paged stream reference in auxiliary metadata + if (pass > attributes + 3) { + Local::destroyPagedStream(meta->auxData(), attributeIndex-1); + } + } + else if (pass < buffers()) { + // pass 2n+3 - cleanup last paged stream + const Index attributeIndex = pass - attributes - 4; + Local::destroyPagedStream(meta->auxData(), attributeIndex); + } +} + +template +inline void +PointDataLeafNode::writeBuffers(std::ostream& os, bool toHalf) const +{ + struct Local + { + static void destroyPagedStream(const io::StreamMetadata::AuxDataMap& auxData, const Index index) + { + // if paged stream exists, flush and delete it + std::string key("paged:" + std::to_string(index)); + auto it = auxData.find(key); + if (it != auxData.end()) { + compression::PagedOutputStream& stream = *(boost::any_cast(it->second)); + stream.flush(); + (const_cast(auxData)).erase(it); + } + } + + static compression::PagedOutputStream& getOrInsertPagedStream( const io::StreamMetadata::AuxDataMap& auxData, + const Index index) + { + std::string key("paged:" + std::to_string(index)); + auto it = auxData.find(key); + if (it != auxData.end()) { + return *(boost::any_cast(it->second)); + } + else { + compression::PagedOutputStream::Ptr pagedStream = std::make_shared(); + (const_cast(auxData))[key] = pagedStream; + return *pagedStream; + } + } + + static void insertDescriptor( const io::StreamMetadata::AuxDataMap& auxData, + const Descriptor::Ptr descriptor) + { + std::string descriptorKey("descriptorPtr"); + std::string matchingKey("hasMatchingDescriptor"); + auto itMatching = auxData.find(matchingKey); + auto itDescriptor = auxData.find(descriptorKey); + if (itMatching == auxData.end()) { + // if matching bool is not found, insert "true" and the descriptor + (const_cast(auxData))[matchingKey] = true; + assert(itDescriptor == auxData.end()); + (const_cast(auxData))[descriptorKey] = descriptor; + } + else { + // if matching bool is found and is false, early exit (a previous descriptor did not match) + bool matching = boost::any_cast(itMatching->second); + if (!matching) return; + assert(itDescriptor != auxData.end()); + // if matching bool is true, check whether the existing descriptor matches the current one and set + // matching bool to false if not + const Descriptor::Ptr existingDescriptor = boost::any_cast(itDescriptor->second); + if (*existingDescriptor != *descriptor) { + (const_cast(auxData))[matchingKey] = false; + } + } + } + + static bool hasMatchingDescriptor(const io::StreamMetadata::AuxDataMap& auxData) + { + std::string matchingKey("hasMatchingDescriptor"); + auto itMatching = auxData.find(matchingKey); + // if matching key is not found, no matching descriptor + if (itMatching == auxData.end()) return false; + // if matching key is found and is false, no matching descriptor + if (!boost::any_cast(itMatching->second)) return false; + return true; + } + + static AttributeSet::Descriptor::Ptr retrieveMatchingDescriptor(const io::StreamMetadata::AuxDataMap& auxData) + { + std::string descriptorKey("descriptorPtr"); + auto itDescriptor = auxData.find(descriptorKey); + // if matching key is true, however descriptor is not found, it has already been retrieved + if (itDescriptor == auxData.end()) return nullptr; + // otherwise remove it and return it + const Descriptor::Ptr descriptor = boost::any_cast(itDescriptor->second); + (const_cast(auxData)).erase(itDescriptor); + return descriptor; + } + + static void clearMatchingDescriptor(const io::StreamMetadata::AuxDataMap& auxData) + { + std::string matchingKey("hasMatchingDescriptor"); + std::string descriptorKey("descriptorPtr"); + auto itMatching = auxData.find(matchingKey); + auto itDescriptor = auxData.find(descriptorKey); + if (itMatching != auxData.end()) (const_cast(auxData)).erase(itMatching); + if (itDescriptor != auxData.end()) (const_cast(auxData)).erase(itDescriptor); + } + }; + + const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(os); + + if (!meta) { + OPENVDB_THROW(IoError, "Cannot write out a PointDataLeaf without StreamMetadata."); + } + + const Index pass(static_cast(meta->pass())); + + // leaf traversal analysis deduces the number of passes to perform for this leaf + // then updates the leaf traversal value to ensure all passes will be written + + if (meta->countingPasses()) { + const Index requiredPasses = this->buffers(); + if (requiredPasses > pass) { + meta->setPass(requiredPasses); + } + return; + } + + const Index maximumPass(static_cast(meta->pass() >> 16)); + const Index attributes = (maximumPass - 4) / 2; + + if (pass == 0) { + // pass 0 - voxel data sizes + io::writeCompressedValuesSize(os, this->buffer().data(), SIZE); + // track if descriptor is shared or not + Local::insertDescriptor(meta->auxData(), mAttributeSet->descriptorPtr()); + } + else if (pass == 1) { + // pass 1 - descriptor and attribute metadata + bool matchingDescriptor = Local::hasMatchingDescriptor(meta->auxData()); + if (matchingDescriptor) { + AttributeSet::Descriptor::Ptr descriptor = Local::retrieveMatchingDescriptor(meta->auxData()); + if (descriptor) { + // write a header to indicate a shared descriptor + uint8_t header(1); + os.write(reinterpret_cast(&header), sizeof(uint8_t)); + mAttributeSet->writeDescriptor(os, /*transient=*/false); + } + } + else { + // write a header to indicate a non-shared descriptor + uint8_t header(0); + os.write(reinterpret_cast(&header), sizeof(uint8_t)); + mAttributeSet->writeDescriptor(os, /*transient=*/false); + } + mAttributeSet->writeMetadata(os, /*transient=*/false, /*paged=*/true); + } + else if (pass < attributes + 2) { + // pass 2...n+2 - attribute buffer sizes + const Index attributeIndex = pass - 2; + // destroy previous paged stream + if (pass > 2) { + Local::destroyPagedStream(meta->auxData(), attributeIndex-1); + } + const AttributeArray* array = attributeIndex < mAttributeSet->size() ? + mAttributeSet->getConst(attributeIndex) : nullptr; + if (array) { + compression::PagedOutputStream& pagedStream = + Local::getOrInsertPagedStream(meta->auxData(), attributeIndex); + pagedStream.setOutputStream(os); + pagedStream.setSizeOnly(true); + array->writePagedBuffers(pagedStream, /*outputTransient*/false); + } + } + else if (pass == attributes + 2) { + const Index attributeIndex = pass - 3; + Local::destroyPagedStream(meta->auxData(), attributeIndex); + // pass n+2 - voxel data + BaseLeaf::writeBuffers(os, toHalf); + } + else if (pass < (attributes*2 + 3)) { + // pass n+3...2n+3 - attribute buffers + const Index attributeIndex = pass - attributes - 3; + // destroy previous paged stream + if (pass > attributes + 2) { + Local::destroyPagedStream(meta->auxData(), attributeIndex-1); + } + const AttributeArray* array = attributeIndex < mAttributeSet->size() ? + mAttributeSet->getConst(attributeIndex) : nullptr; + if (array) { + compression::PagedOutputStream& pagedStream = + Local::getOrInsertPagedStream(meta->auxData(), attributeIndex); + pagedStream.setOutputStream(os); + pagedStream.setSizeOnly(false); + array->writePagedBuffers(pagedStream, /*outputTransient*/false); + } + } + else if (pass < buffers()) { + Local::clearMatchingDescriptor(meta->auxData()); + // pass 2n+3 - cleanup last paged stream + const Index attributeIndex = pass - attributes - 4; + Local::destroyPagedStream(meta->auxData(), attributeIndex); + } +} + +template +inline Index64 +PointDataLeafNode::memUsage() const +{ + return BaseLeaf::memUsage() + mAttributeSet->memUsage(); +} + +template +inline void +PointDataLeafNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + BaseLeaf::evalActiveBoundingBox(bbox, visitVoxels); +} + +template +inline CoordBBox +PointDataLeafNode::getNodeBoundingBox() const +{ + return BaseLeaf::getNodeBoundingBox(); +} + +template +inline void +PointDataLeafNode::fill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + if (!this->allocate()) return; + + this->assertNonModifiableUnlessZero(value); + + // active state is permitted to be updated + + for (Int32 x = bbox.min().x(); x <= bbox.max().x(); ++x) { + const Index offsetX = (x & (DIM-1u)) << 2*Log2Dim; + for (Int32 y = bbox.min().y(); y <= bbox.max().y(); ++y) { + const Index offsetXY = offsetX + ((y & (DIM-1u)) << Log2Dim); + for (Int32 z = bbox.min().z(); z <= bbox.max().z(); ++z) { + const Index offset = offsetXY + (z & (DIM-1u)); + this->setValueMask(offset, active); + } + } + } +} + +template +inline void +PointDataLeafNode::fill(const ValueType& value, bool active) +{ + this->assertNonModifiableUnlessZero(value); + + // active state is permitted to be updated + + if (active) this->setValuesOn(); + else this->setValuesOff(); +} + + +//////////////////////////////////////// + + +template +inline AttributeSet::Descriptor::Ptr +makeDescriptorUnique(PointDataTreeT& tree) +{ + auto leafIter = tree.beginLeaf(); + if (!leafIter) return nullptr; + + const AttributeSet::Descriptor& descriptor = leafIter->attributeSet().descriptor(); + auto newDescriptor = std::make_shared(descriptor); + for (; leafIter; ++leafIter) { + leafIter->resetDescriptor(newDescriptor); + } + + return newDescriptor; +} + + +template +inline void +setStreamingMode(PointDataTreeT& tree, bool on) +{ + auto leafIter = tree.beginLeaf(); + for (; leafIter; ++leafIter) { + for (size_t i = 0; i < leafIter->attributeSet().size(); i++) { + leafIter->attributeArray(i).setStreaming(on); + } + } +} + + +template +inline void +prefetch(PointDataTreeT& tree, bool position, bool otherAttributes) +{ + // NOTE: the following is intentionally not multi-threaded, as the I/O + // is faster if done in the order in which it is stored in the file + + auto leaf = tree.cbeginLeaf(); + if (!leaf) return; + + const auto& attributeSet = leaf->attributeSet(); + + // pre-fetch leaf data + + for ( ; leaf; ++leaf) { + leaf->buffer().data(); + } + + // pre-fetch position attribute data (position will typically have index 0) + + size_t positionIndex = attributeSet.find("P"); + + if (position && positionIndex != AttributeSet::INVALID_POS) { + for (leaf = tree.cbeginLeaf(); leaf; ++leaf) { + assert(leaf->hasAttribute(positionIndex)); + leaf->constAttributeArray(positionIndex).loadData(); + } + } + + // pre-fetch other attribute data + + if (otherAttributes) { + const size_t attributes = attributeSet.size(); + for (size_t attributeIndex = 0; attributeIndex < attributes; attributeIndex++) { + if (attributeIndex == positionIndex) continue; + for (leaf = tree.cbeginLeaf(); leaf; ++leaf) { + assert(leaf->hasAttribute(attributeIndex)); + leaf->constAttributeArray(attributeIndex).loadData(); + } + } + } +} + + +namespace internal { + +/// @brief Global registration of point data-related types +/// @note This is called from @c openvdb::initialize, so there is +/// no need to call it directly. +void initialize(); + +/// @brief Global deregistration of point data-related types +/// @note This is called from @c openvdb::uninitialize, so there is +/// no need to call it directly. +void uninitialize(); + + +/// @brief Recursive node chain which generates a boost::mpl::vector listing +/// value converted types of nodes to PointDataGrid nodes of the same configuration, +/// rooted at RootNodeType in reverse order, from LeafNode to RootNode. +/// See also TreeConverter<>. +template +struct PointDataNodeChain +{ + using SubtreeT = typename PointDataNodeChain::Type; + using RootNodeT = tree::RootNode::type>; + using Type = typename boost::mpl::push_back::type; +}; + +// Specialization for internal nodes which require their embedded child type to +// be switched +template +struct PointDataNodeChain, HeadLevel> +{ + using SubtreeT = typename PointDataNodeChain::Type; + using InternalNodeT = tree::InternalNode::type, Log2Dim>; + using Type = typename boost::mpl::push_back::type; +}; + +// Specialization for the last internal node of a node chain, expected +// to be templated on a leaf node +template +struct PointDataNodeChain, /*HeadLevel=*/1> +{ + using LeafNodeT = PointDataLeafNode; + using InternalNodeT = tree::InternalNode; + using Type = typename boost::mpl::vector::type; +}; + +} // namespace internal + + +/// @brief Similiar to ValueConverter, but allows for tree configuration conversion +/// to a PointDataTree. ValueConverter cannot be used as a +/// PointDataLeafNode is not a specialization of LeafNode +template +struct TreeConverter { + using RootNodeT = typename TreeType::RootNodeType; + using NodeChainT = typename internal::PointDataNodeChain::Type; + using Type = tree::Tree::type>; +}; + + +} // namespace points + + +//////////////////////////////////////// + + +namespace tree +{ + +/// Helper metafunction used to implement LeafNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameLeafConfig> { static const bool value = true; }; + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_POINT_DATA_GRID_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointDelete.h b/openvdb/points/PointDelete.h new file mode 100644 index 00000000..eb460f90 --- /dev/null +++ b/openvdb/points/PointDelete.h @@ -0,0 +1,278 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Nick Avramoussis, Francisco Gochez, Dan Bailey +/// +/// @file PointDelete.h +/// +/// @brief Methods for deleting points based on group membership + +#ifndef OPENVDB_POINTS_POINT_DELETE_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_DELETE_HAS_BEEN_INCLUDED + +#include "PointDataGrid.h" +#include "PointGroup.h" +#include "IndexIterator.h" +#include "IndexFilter.h" + +#include +#include + +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +/// @brief Delete points that are members of specific groups +/// +/// @details This method will delete points which are members of any of the supplied groups and +/// will optionally drop the groups from the tree. An invert flag can be used to +/// delete points that belong to none of the groups. +/// +/// @param pointTree the point tree +/// @param groups the groups from which to delete points +/// @param invert if enabled, points not belonging to any of the groups will be deleted +/// @param drop if enabled and invert is disabled, the groups will be dropped from the tree +/// +/// @note If the invert flag is true, none of the groups will be dropped after deleting points +/// regardless of the value of the drop parameter. + +template +inline void deleteFromGroups(PointDataTreeT& pointTree, + const std::vector& groups, + bool invert = false, + bool drop = true); + +/// @brief Delete points that are members of a group +/// +/// @details This method will delete points which are members of the supplied group and will +/// optionally drop the group from the tree. An invert flag can be used to +/// delete points that belong to none of the groups. +/// +/// @param pointTree the point tree with the group to delete +/// @param group the name of the group to delete +/// @param invert if enabled, points not belonging to any of the groups will be deleted +/// @param drop if enabled and invert is disabled, the group will be dropped from the tree +/// +/// @note If the invert flag is true, the group will not be dropped after deleting points +/// regardless of the value of the drop parameter. + +template +inline void deleteFromGroup(PointDataTreeT& pointTree, + const std::string& group, + bool invert = false, + bool drop = true); + + +//////////////////////////////////////// + + +namespace point_delete_internal { + + +struct VectorWrapper +{ + using T = std::vector>; + + VectorWrapper(const T& _data) : data(_data) { } + operator bool() const { return index < data.size(); } + VectorWrapper& operator++() { index++; return *this; } + Index sourceIndex() const { assert(*this); return data[index].first; } + Index targetIndex() const { assert(*this); return data[index].second; } + +private: + const T& data; + T::size_type index = 0; +}; // struct VectorWrapper + + +template +struct DeleteByFilterOp +{ + using LeafManagerT = tree::LeafManager; + using LeafRangeT = typename LeafManagerT::LeafRange; + using LeafNodeT = typename PointDataTreeT::LeafNodeType; + using ValueType = typename LeafNodeT::ValueType; + + DeleteByFilterOp(const FilterT& filter, + const AttributeArray::ScopedRegistryLock* lock) + : mFilter(filter) + , mLock(lock) { } + + void operator()(const LeafRangeT& range) const + { + for (auto leaf = range.begin(); leaf != range.end(); ++leaf) { + + const size_t newSize = + iterCount(leaf->template beginIndexAll(mFilter)); + + // if all points are being deleted, clear the leaf attributes + if (newSize == 0) { + leaf->clearAttributes(/*updateValueMask=*/true, mLock); + continue; + } + + // early exit if no points are being deleted + + const size_t currentSize = leaf->getLastValue(); + if (newSize == currentSize) continue; + + const AttributeSet& existingAttributeSet = leaf->attributeSet(); + AttributeSet* newAttributeSet = new AttributeSet( + existingAttributeSet, static_cast(newSize), mLock); + const size_t attributeSetSize = existingAttributeSet.size(); + + // cache the attribute arrays for efficiency + + std::vector newAttributeArrays; + std::vector existingAttributeArrays; + + for (size_t i = 0; i < attributeSetSize; i++) { + AttributeArray* newArray = newAttributeSet->get(i); + const AttributeArray* existingArray = existingAttributeSet.getConst(i); + + if (!newArray->hasConstantStride() || !existingArray->hasConstantStride()) { + OPENVDB_THROW(openvdb::NotImplementedError, + "Transfer of attribute values for dynamic arrays not currently supported."); + } + + if (newArray->stride() != existingArray->stride()) { + OPENVDB_THROW(openvdb::LookupError, + "Cannot transfer attribute values with mis-matching strides."); + } + + newAttributeArrays.push_back(newArray); + existingAttributeArrays.push_back(existingArray); + } + + Index attributeIndex = 0; + std::vector endOffsets; + + endOffsets.reserve(LeafNodeT::NUM_VALUES); + + // now construct new attribute arrays which exclude data from deleted points + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + std::vector> indexMapping; + indexMapping.reserve(newSize); + + for (auto voxel = leaf->cbeginValueAll(); voxel; ++voxel) { + for (auto iter = leaf->beginIndexVoxel(voxel.getCoord(), mFilter); + iter; ++iter) { + indexMapping.emplace_back(*iter, attributeIndex++); + } + endOffsets.push_back(static_cast(attributeIndex)); + } + + for (size_t i = 0; i < attributeSetSize; i++) { + VectorWrapper indexMappingWrapper(indexMapping); + newAttributeArrays[i]->copyValues(*(existingAttributeArrays[i]), indexMappingWrapper); + } +#else + for (auto voxel = leaf->cbeginValueAll(); voxel; ++voxel) { + for (auto iter = leaf->beginIndexVoxel(voxel.getCoord(), mFilter); + iter; ++iter) { + for (size_t i = 0; i < attributeSetSize; i++) { + newAttributeArrays[i]->set(attributeIndex, *(existingAttributeArrays[i]), + *iter); + } + ++attributeIndex; + } + endOffsets.push_back(static_cast(attributeIndex)); + } +#endif + + leaf->replaceAttributeSet(newAttributeSet); + leaf->setOffsets(endOffsets); + } + } + +private: + const FilterT& mFilter; + const AttributeArray::ScopedRegistryLock* mLock; +}; // struct DeleteByFilterOp + +} // namespace point_delete_internal + + +//////////////////////////////////////// + + +template +inline void deleteFromGroups(PointDataTreeT& pointTree, + const std::vector& groups, + bool invert, + bool drop) +{ + const typename PointDataTreeT::LeafCIter leafIter = pointTree.cbeginLeaf(); + + if (!leafIter) return; + + const openvdb::points::AttributeSet& attributeSet = leafIter->attributeSet(); + const AttributeSet::Descriptor& descriptor = attributeSet.descriptor(); + std::vector availableGroups; + + // determine which of the requested groups exist, and early exit + // if none are present in the tree + + for (const auto& groupName : groups) { + if (descriptor.hasGroup(groupName)) { + availableGroups.push_back(groupName); + } + } + + if (availableGroups.empty()) return; + + std::vector empty; + std::unique_ptr filter; + if (invert) { + filter.reset(new MultiGroupFilter(groups, empty, leafIter->attributeSet())); + } + else { + filter.reset(new MultiGroupFilter(empty, groups, leafIter->attributeSet())); + } + + { // acquire registry lock to avoid locking when appending attributes in parallel + + AttributeArray::ScopedRegistryLock lock; + + tree::LeafManager leafManager(pointTree); + point_delete_internal::DeleteByFilterOp deleteOp( + *filter, &lock); + tbb::parallel_for(leafManager.leafRange(), deleteOp); + } + + // remove empty leaf nodes + + tools::pruneInactive(pointTree); + + // drop the now-empty groups if requested (unless invert = true) + + if (drop && !invert) { + dropGroups(pointTree, availableGroups); + } +} + +template +inline void deleteFromGroup(PointDataTreeT& pointTree, + const std::string& group, + bool invert, + bool drop) +{ + std::vector groups(1, group); + + deleteFromGroups(pointTree, groups, invert, drop); +} + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_POINT_DELETE_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointGroup.h b/openvdb/points/PointGroup.h new file mode 100644 index 00000000..96708c86 --- /dev/null +++ b/openvdb/points/PointGroup.h @@ -0,0 +1,845 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Dan Bailey +/// +/// @file points/PointGroup.h +/// +/// @brief Point group manipulation in a VDB Point Grid. + +#ifndef OPENVDB_POINTS_POINT_GROUP_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_GROUP_HAS_BEEN_INCLUDED + +#include + +#include "IndexIterator.h" // FilterTraits +#include "IndexFilter.h" // FilterTraits +#include "AttributeSet.h" +#include "PointDataGrid.h" +#include "PointAttribute.h" +#include "PointCount.h" + +#include + +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + +/// @brief Delete any group that is not present in the Descriptor. +/// +/// @param groups the vector of group names. +/// @param descriptor the descriptor that holds the group map. +inline void deleteMissingPointGroups( std::vector& groups, + const AttributeSet::Descriptor& descriptor); + +/// @brief Appends a new empty group to the VDB tree. +/// +/// @param tree the PointDataTree to be appended to. +/// @param group name of the new group. +template +inline void appendGroup(PointDataTree& tree, + const Name& group); + +/// @brief Appends new empty groups to the VDB tree. +/// +/// @param tree the PointDataTree to be appended to. +/// @param groups names of the new groups. +template +inline void appendGroups(PointDataTree& tree, + const std::vector& groups); + +/// @brief Drops an existing group from the VDB tree. +/// +/// @param tree the PointDataTree to be dropped from. +/// @param group name of the group. +/// @param compact compact attributes if possible to reduce memory - if dropping +/// more than one group, compacting once at the end will be faster +template +inline void dropGroup( PointDataTree& tree, + const Name& group, + const bool compact = true); + +/// @brief Drops existing groups from the VDB tree, the tree is compacted after dropping. +/// +/// @param tree the PointDataTree to be dropped from. +/// @param groups names of the groups. +template +inline void dropGroups( PointDataTree& tree, + const std::vector& groups); + +/// @brief Drops all existing groups from the VDB tree, the tree is compacted after dropping. +/// +/// @param tree the PointDataTree to be dropped from. +template +inline void dropGroups( PointDataTree& tree); + +/// @brief Compacts existing groups of a VDB Tree to use less memory if possible. +/// +/// @param tree the PointDataTree to be compacted. +template +inline void compactGroups(PointDataTree& tree); + +/// @brief Sets group membership from a PointIndexTree-ordered vector. +/// +/// @param tree the PointDataTree. +/// @param indexTree the PointIndexTree. +/// @param membership @c 1 if the point is in the group, 0 otherwise. +/// @param group the name of the group. +/// @param remove if @c true also perform removal of points from the group. +/// +/// @note vector is not thread-safe on concurrent write, so use vector instead +template +inline void setGroup( PointDataTree& tree, + const PointIndexTree& indexTree, + const std::vector& membership, + const Name& group, + const bool remove = false); + +/// @brief Sets membership for the specified group for all points (on/off). +/// +/// @param tree the PointDataTree. +/// @param group the name of the group. +/// @param member true / false for membership of the group. +template +inline void setGroup( PointDataTree& tree, + const Name& group, + const bool member = true); + +/// @brief Sets group membership based on a provided filter. +/// +/// @param tree the PointDataTree. +/// @param group the name of the group. +/// @param filter filter data that is used to create a per-leaf filter +template +inline void setGroupByFilter( PointDataTree& tree, + const Name& group, + const FilterT& filter); + + +//////////////////////////////////////// + + +namespace point_group_internal { + + +/// Copy a group attribute value from one group offset to another +template +struct CopyGroupOp { + + using LeafManagerT = typename tree::LeafManager; + using LeafRangeT = typename LeafManagerT::LeafRange; + using GroupIndex = AttributeSet::Descriptor::GroupIndex; + + CopyGroupOp(const GroupIndex& targetIndex, + const GroupIndex& sourceIndex) + : mTargetIndex(targetIndex) + , mSourceIndex(sourceIndex) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const { + + for (auto leaf = range.begin(); leaf; ++leaf) { + + GroupHandle sourceGroup = leaf->groupHandle(mSourceIndex); + GroupWriteHandle targetGroup = leaf->groupWriteHandle(mTargetIndex); + + for (auto iter = leaf->beginIndexAll(); iter; ++iter) { + const bool groupOn = sourceGroup.get(*iter); + targetGroup.set(*iter, groupOn); + } + } + } + + ////////// + + const GroupIndex mTargetIndex; + const GroupIndex mSourceIndex; +}; + + +/// Set membership on or off for the specified group +template +struct SetGroupOp +{ + using LeafManagerT = typename tree::LeafManager; + using GroupIndex = AttributeSet::Descriptor::GroupIndex; + + SetGroupOp(const AttributeSet::Descriptor::GroupIndex& index) + : mIndex(index) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const + { + for (auto leaf = range.begin(); leaf; ++leaf) { + + // obtain the group attribute array + + GroupWriteHandle group(leaf->groupWriteHandle(mIndex)); + + // set the group value + + group.collapse(Member); + } + } + + ////////// + + const GroupIndex& mIndex; +}; // struct SetGroupOp + + +template +struct SetGroupFromIndexOp +{ + using LeafManagerT = typename tree::LeafManager; + using LeafRangeT = typename LeafManagerT::LeafRange; + using PointIndexLeafNode = typename PointIndexTree::LeafNodeType; + using IndexArray = typename PointIndexLeafNode::IndexArray; + using GroupIndex = AttributeSet::Descriptor::GroupIndex; + using MembershipArray = std::vector; + + SetGroupFromIndexOp(const PointIndexTree& indexTree, + const MembershipArray& membership, + const GroupIndex& index) + : mIndexTree(indexTree) + , mMembership(membership) + , mIndex(index) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const + { + for (auto leaf = range.begin(); leaf; ++leaf) { + + // obtain the PointIndexLeafNode (using the origin of the current leaf) + + const PointIndexLeafNode* pointIndexLeaf = mIndexTree.probeConstLeaf(leaf->origin()); + + if (!pointIndexLeaf) continue; + + // obtain the group attribute array + + GroupWriteHandle group(leaf->groupWriteHandle(mIndex)); + + // initialise the attribute storage + + Index64 index = 0; + + const IndexArray& indices = pointIndexLeaf->indices(); + + for (const Index64 i: indices) { + if (Remove) { + group.set(static_cast(index), mMembership[i]); + } else if (mMembership[i] == short(1)) { + group.set(static_cast(index), short(1)); + } + index++; + } + + // attempt to compact the array + + group.compact(); + } + } + + ////////// + + const PointIndexTree& mIndexTree; + const MembershipArray& mMembership; + const GroupIndex& mIndex; +}; // struct SetGroupFromIndexOp + + +template +struct SetGroupByFilterOp +{ + using LeafManagerT = typename tree::LeafManager; + using LeafRangeT = typename LeafManagerT::LeafRange; + using LeafNodeT = typename PointDataTree::LeafNodeType; + using GroupIndex = AttributeSet::Descriptor::GroupIndex; + + SetGroupByFilterOp( const GroupIndex& index, const FilterT& filter) + : mIndex(index) + , mFilter(filter) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const + { + for (auto leaf = range.begin(); leaf; ++leaf) { + + // obtain the group attribute array + + GroupWriteHandle group(leaf->groupWriteHandle(mIndex)); + + auto iter = leaf->template beginIndex(mFilter); + + for (; iter; ++iter) { + group.set(*iter, true); + } + + // attempt to compact the array + + group.compact(); + } + } + + ////////// + + const GroupIndex& mIndex; + const FilterT& mFilter; // beginIndex takes a copy of mFilter +}; // struct SetGroupByFilterOp + + +//////////////////////////////////////// + + +/// Convenience class with methods for analyzing group data +class GroupInfo +{ +public: + using Descriptor = AttributeSet::Descriptor; + + GroupInfo(const AttributeSet& attributeSet) + : mAttributeSet(attributeSet) { } + + /// Return the number of bits in a group (typically 8) + static size_t groupBits() { return sizeof(GroupType) * CHAR_BIT; } + + /// Return the number of empty group slots which correlates to the number of groups + /// that can be stored without increasing the number of group attribute arrays + size_t unusedGroups() const + { + const Descriptor& descriptor = mAttributeSet.descriptor(); + + // compute total slots (one slot per bit of the group attributes) + + const size_t groupAttributes = descriptor.count(GroupAttributeArray::attributeType()); + + if (groupAttributes == 0) return 0; + + const size_t totalSlots = groupAttributes * this->groupBits(); + + // compute slots in use + + const AttributeSet::Descriptor::NameToPosMap& groupMap = mAttributeSet.descriptor().groupMap(); + const size_t usedSlots = groupMap.size(); + + return totalSlots - usedSlots; + } + + /// Return @c true if there are sufficient empty slots to allow compacting + bool canCompactGroups() const + { + // can compact if more unused groups than in one group attribute array + + return this->unusedGroups() >= this->groupBits(); + } + + /// Return the next empty group slot + size_t nextUnusedOffset() const + { + const Descriptor::NameToPosMap& groupMap = mAttributeSet.descriptor().groupMap(); + + // build a list of group indices + + std::vector indices; + indices.reserve(groupMap.size()); + for (const auto& namePos : groupMap) { + indices.push_back(namePos.second); + } + + std::sort(indices.begin(), indices.end()); + + // return first index not present + + size_t offset = 0; + for (const size_t& index : indices) { + if (index != offset) break; + offset++; + } + + return offset; + } + + /// Return vector of indices correlating to the group attribute arrays + std::vector populateGroupIndices() const + { + std::vector indices; + + const Descriptor::NameToPosMap& map = mAttributeSet.descriptor().map(); + + for (const auto& namePos : map) { + const AttributeArray* array = mAttributeSet.getConst(namePos.first); + if (isGroup(*array)) { + indices.push_back(namePos.second); + } + } + + return indices; + } + + /// Determine if a move is required to efficiently compact the data and store the + /// source name, offset and the target offset in the input parameters + bool requiresMove(Name& sourceName, size_t& sourceOffset, size_t& targetOffset) const { + + targetOffset = this->nextUnusedOffset(); + + const Descriptor::NameToPosMap& groupMap = mAttributeSet.descriptor().groupMap(); + + for (const auto& namePos : groupMap) { + + // move only required if source comes after the target + + if (namePos.second >= targetOffset) { + sourceName = namePos.first; + sourceOffset = namePos.second; + return true; + } + } + + return false; + } + +private: + const AttributeSet& mAttributeSet; +}; // class GroupInfo + + +} // namespace point_group_internal + + +//////////////////////////////////////// + + +inline void deleteMissingPointGroups( std::vector& groups, + const AttributeSet::Descriptor& descriptor) +{ + for (auto it = groups.begin(); it != groups.end();) { + if (!descriptor.hasGroup(*it)) it = groups.erase(it); + else ++it; + } +} + + +//////////////////////////////////////// + + +template +inline void appendGroup(PointDataTreeT& tree, const Name& group) +{ + using point_group_internal::GroupInfo; + + if (group.empty()) { + OPENVDB_THROW(KeyError, "Cannot use an empty group name as a key."); + } + + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + auto descriptor = attributeSet.descriptorPtr(); + GroupInfo groupInfo(attributeSet); + + // don't add if group already exists + + if (descriptor->hasGroup(group)) return; + + const bool hasUnusedGroup = groupInfo.unusedGroups() > 0; + + // add a new group attribute if there are no unused groups + + if (!hasUnusedGroup) { + + // find a new internal group name + + const Name groupName = descriptor->uniqueName("__group"); + + descriptor = descriptor->duplicateAppend(groupName, GroupAttributeArray::attributeType()); + const size_t pos = descriptor->find(groupName); + + // insert new group attribute + + tree::LeafManager leafManager(tree); + leafManager.foreach( + [&](typename PointDataTreeT::LeafNodeType& leaf, size_t /*idx*/) { + auto expected = leaf.attributeSet().descriptorPtr(); + leaf.appendAttribute(*expected, descriptor, pos); + }, /*threaded=*/true + ); + } + else { + // make the descriptor unique before we modify the group map + + makeDescriptorUnique(tree); + descriptor = attributeSet.descriptorPtr(); + } + + // ensure that there are now available groups + + assert(groupInfo.unusedGroups() > 0); + + // find next unused offset + + const size_t offset = groupInfo.nextUnusedOffset(); + + // add the group mapping to the descriptor + + descriptor->setGroup(group, offset); + + // if there was an unused group then we did not need to append a new attribute, so + // we must manually clear membership in the new group as its bits may have been + // previously set + + if (hasUnusedGroup) setGroup(tree, group, false); +} + + +//////////////////////////////////////// + + +template +inline void appendGroups(PointDataTree& tree, + const std::vector& groups) +{ + // TODO: could be more efficient by appending multiple groups at once + // instead of one-by-one, however this is likely not that common a use case + + for (const Name& name : groups) { + appendGroup(tree, name); + } +} + + +//////////////////////////////////////// + + +template +inline void dropGroup(PointDataTree& tree, const Name& group, const bool compact) +{ + using Descriptor = AttributeSet::Descriptor; + + if (group.empty()) { + OPENVDB_THROW(KeyError, "Cannot use an empty group name as a key."); + } + + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + + // make the descriptor unique before we modify the group map + + makeDescriptorUnique(tree); + Descriptor::Ptr descriptor = attributeSet.descriptorPtr(); + + // now drop the group + + descriptor->dropGroup(group); + + if (compact) { + compactGroups(tree); + } +} + + +//////////////////////////////////////// + + +template +inline void dropGroups( PointDataTree& tree, + const std::vector& groups) +{ + for (const Name& name : groups) { + dropGroup(tree, name, /*compact=*/false); + } + + // compaction done once for efficiency + + compactGroups(tree); +} + + +//////////////////////////////////////// + + +template +inline void dropGroups( PointDataTree& tree) +{ + using Descriptor = AttributeSet::Descriptor; + + using point_group_internal::GroupInfo; + + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + GroupInfo groupInfo(attributeSet); + + // make the descriptor unique before we modify the group map + + makeDescriptorUnique(tree); + Descriptor::Ptr descriptor = attributeSet.descriptorPtr(); + + descriptor->clearGroups(); + + // find all indices for group attribute arrays + + std::vector indices = groupInfo.populateGroupIndices(); + + // drop these attributes arrays + + dropAttributes(tree, indices); +} + + +//////////////////////////////////////// + + +template +inline void compactGroups(PointDataTree& tree) +{ + using Descriptor = AttributeSet::Descriptor; + using GroupIndex = Descriptor::GroupIndex; + using LeafManagerT = typename tree::template LeafManager; + + using point_group_internal::CopyGroupOp; + using point_group_internal::GroupInfo; + + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + GroupInfo groupInfo(attributeSet); + + // early exit if not possible to compact + + if (!groupInfo.canCompactGroups()) return; + + // make the descriptor unique before we modify the group map + + makeDescriptorUnique(tree); + Descriptor::Ptr descriptor = attributeSet.descriptorPtr(); + + // generate a list of group offsets and move them (one-by-one) + // TODO: improve this algorithm to move multiple groups per array at once + // though this is likely not that common a use case + + Name sourceName; + size_t sourceOffset, targetOffset; + + while (groupInfo.requiresMove(sourceName, sourceOffset, targetOffset)) { + + const GroupIndex sourceIndex = attributeSet.groupIndex(sourceOffset); + const GroupIndex targetIndex = attributeSet.groupIndex(targetOffset); + + CopyGroupOp copy(targetIndex, sourceIndex); + LeafManagerT leafManager(tree); + tbb::parallel_for(leafManager.leafRange(), copy); + + descriptor->setGroup(sourceName, targetOffset); + } + + // drop unused attribute arrays + + std::vector indices = groupInfo.populateGroupIndices(); + + const size_t totalAttributesToDrop = groupInfo.unusedGroups() / groupInfo.groupBits(); + + assert(totalAttributesToDrop <= indices.size()); + + std::vector indicesToDrop(indices.end() - totalAttributesToDrop, indices.end()); + + dropAttributes(tree, indicesToDrop); +} + + +//////////////////////////////////////// + + +template +inline void setGroup( PointDataTree& tree, + const PointIndexTree& indexTree, + const std::vector& membership, + const Name& group, + const bool remove) +{ + using Descriptor = AttributeSet::Descriptor; + using LeafManagerT = typename tree::template LeafManager; + using point_group_internal::SetGroupFromIndexOp; + + auto iter = tree.cbeginLeaf(); + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + const Descriptor& descriptor = attributeSet.descriptor(); + + if (!descriptor.hasGroup(group)) { + OPENVDB_THROW(LookupError, "Group must exist on Tree before defining membership."); + } + + { + // Check that that the largest index in the PointIndexTree is smaller than the size + // of the membership vector. The index tree will be used to lookup membership + // values. If the index tree was constructed with nan positions, this index will + // differ from the PointDataTree count + + using IndexTreeManager = tree::LeafManager; + IndexTreeManager leafManager(indexTree); + + const int64_t max = tbb::parallel_reduce(leafManager.leafRange(), -1, + [](const typename IndexTreeManager::LeafRange& range, int64_t value) -> int64_t { + for (auto leaf = range.begin(); leaf; ++leaf) { + auto it = std::max_element(leaf->indices().begin(), leaf->indices().end()); + value = std::max(value, static_cast(*it)); + } + return value; + }, + [](const int64_t a, const int64_t b) { + return std::max(a, b); + } + ); + + if (max != -1 && membership.size() <= static_cast(max)) { + OPENVDB_THROW(IndexError, "Group membership vector size must be larger than " + " the maximum index within the provided index tree."); + } + } + + const Descriptor::GroupIndex index = attributeSet.groupIndex(group); + LeafManagerT leafManager(tree); + + // set membership + + if (remove) { + SetGroupFromIndexOp + set(indexTree, membership, index); + tbb::parallel_for(leafManager.leafRange(), set); + } + else { + SetGroupFromIndexOp + set(indexTree, membership, index); + tbb::parallel_for(leafManager.leafRange(), set); + } +} + + +//////////////////////////////////////// + + +template +inline void setGroup( PointDataTree& tree, + const Name& group, + const bool member) +{ + using Descriptor = AttributeSet::Descriptor; + using LeafManagerT = typename tree::template LeafManager; + + using point_group_internal::SetGroupOp; + + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + const Descriptor& descriptor = attributeSet.descriptor(); + + if (!descriptor.hasGroup(group)) { + OPENVDB_THROW(LookupError, "Group must exist on Tree before defining membership."); + } + + const Descriptor::GroupIndex index = attributeSet.groupIndex(group); + LeafManagerT leafManager(tree); + + // set membership based on member variable + + if (member) tbb::parallel_for(leafManager.leafRange(), SetGroupOp(index)); + else tbb::parallel_for(leafManager.leafRange(), SetGroupOp(index)); +} + + +//////////////////////////////////////// + + +template +inline void setGroupByFilter( PointDataTree& tree, + const Name& group, + const FilterT& filter) +{ + using Descriptor = AttributeSet::Descriptor; + using LeafManagerT = typename tree::template LeafManager; + + using point_group_internal::SetGroupByFilterOp; + + auto iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + const Descriptor& descriptor = attributeSet.descriptor(); + + if (!descriptor.hasGroup(group)) { + OPENVDB_THROW(LookupError, "Group must exist on Tree before defining membership."); + } + + const Descriptor::GroupIndex index = attributeSet.groupIndex(group); + + // set membership using filter + + SetGroupByFilterOp set(index, filter); + LeafManagerT leafManager(tree); + + tbb::parallel_for(leafManager.leafRange(), set); +} + + +//////////////////////////////////////// + + +template +inline void setGroupByRandomTarget( PointDataTree& tree, + const Name& group, + const Index64 targetPoints, + const unsigned int seed = 0) +{ + using RandomFilter = RandomLeafFilter; + + RandomFilter filter(tree, targetPoints, seed); + + setGroupByFilter(tree, group, filter); +} + + +//////////////////////////////////////// + + +template +inline void setGroupByRandomPercentage( PointDataTree& tree, + const Name& group, + const float percentage = 10.0f, + const unsigned int seed = 0) +{ + using RandomFilter = RandomLeafFilter; + + const int currentPoints = static_cast(pointCount(tree)); + const int targetPoints = int(math::Round((percentage * float(currentPoints))/100.0f)); + + RandomFilter filter(tree, targetPoints, seed); + + setGroupByFilter(tree, group, filter); +} + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_POINTS_POINT_GROUP_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointMask.h b/openvdb/points/PointMask.h new file mode 100644 index 00000000..ea0f21e0 --- /dev/null +++ b/openvdb/points/PointMask.h @@ -0,0 +1,435 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/PointMask.h +/// +/// @author Dan Bailey +/// +/// @brief Methods for extracting masks from VDB Point grids. + +#ifndef OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED + +#include +#include // valxform::SumOp + +#include "PointDataGrid.h" +#include "IndexFilter.h" + +#include + +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +/// @brief Extract a Mask Grid from a Point Data Grid +/// @param grid the PointDataGrid to extract the mask from. +/// @param filter an optional index filter +/// @param threaded enable or disable threading (threading is enabled by default) +/// @note this method is only available for Bool Grids and Mask Grids +template ::Type, + typename FilterT = NullFilter> +inline typename std::enable_if::value, + typename MaskT::Ptr>::type +convertPointsToMask(const PointDataGridT& grid, + const FilterT& filter = NullFilter(), + bool threaded = true); + + +/// @brief Extract a Mask Grid from a Point Data Grid using a new transform +/// @param grid the PointDataGrid to extract the mask from. +/// @param transform target transform for the mask. +/// @param filter an optional index filter +/// @param threaded enable or disable threading (threading is enabled by default) +/// @note this method is only available for Bool Grids and Mask Grids +template ::Type, + typename FilterT = NullFilter> +inline typename std::enable_if::value, + typename MaskT::Ptr>::type +convertPointsToMask(const PointDataGridT& grid, + const openvdb::math::Transform& transform, + const FilterT& filter = NullFilter(), + bool threaded = true); + + +/// @brief No-op deformer (adheres to the deformer interface documented in PointMove.h) +struct NullDeformer +{ + template + void reset(LeafT&, size_t /*idx*/ = 0) { } + + template + void apply(Vec3d&, IterT&) const { } +}; + +/// @brief Deformer Traits for optionally configuring deformers to be applied +/// in index-space. The default is world-space. +template +struct DeformerTraits +{ + static const bool IndexSpace = false; +}; + + +//////////////////////////////////////// + + +namespace point_mask_internal { + +template +void voxelSum(LeafT& leaf, const Index offset, const typename LeafT::ValueType& value) +{ + leaf.modifyValue(offset, tools::valxform::SumOp(value)); +} + +// overload PointDataLeaf access to use setOffsetOn(), as modifyValue() +// is intentionally disabled to avoid accidental usage + +template +void voxelSum(PointDataLeafNode& leaf, const Index offset, + const typename PointDataLeafNode::ValueType& value) +{ + leaf.setOffsetOn(offset, leaf.getValue(offset) + value); +} + + +/// @brief Combines multiple grids into one by stealing leaf nodes and summing voxel values +/// This class is designed to work with thread local storage containers such as tbb::combinable +template +struct GridCombinerOp +{ + using CombinableT = typename tbb::combinable; + + using TreeT = typename GridT::TreeType; + using LeafT = typename TreeT::LeafNodeType; + using ValueType = typename TreeT::ValueType; + using SumOp = tools::valxform::SumOp; + + GridCombinerOp(GridT& grid) + : mTree(grid.tree()) {} + + void operator()(const GridT& grid) + { + for (auto leaf = grid.tree().beginLeaf(); leaf; ++leaf) { + auto* newLeaf = mTree.probeLeaf(leaf->origin()); + if (!newLeaf) { + // if the leaf doesn't yet exist in the new tree, steal it + auto& tree = const_cast(grid).tree(); + mTree.addLeaf(tree.template stealNode(leaf->origin(), + zeroVal(), false)); + } + else { + // otherwise increment existing values + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + voxelSum(*newLeaf, iter.offset(), ValueType(*iter)); + } + } + } + } + +private: + TreeT& mTree; +}; // struct GridCombinerOp + + +/// @brief Compute scalar grid from PointDataGrid while evaluating the point filter +template +struct PointsToScalarOp +{ + using LeafT = typename GridT::TreeType::LeafNodeType; + using ValueT = typename LeafT::ValueType; + + PointsToScalarOp( const PointDataGridT& grid, + const FilterT& filter) + : mPointDataAccessor(grid.getConstAccessor()) + , mFilter(filter) { } + + void operator()(LeafT& leaf, size_t /*idx*/) const { + + const auto* const pointLeaf = + mPointDataAccessor.probeConstLeaf(leaf.origin()); + + // assumes matching topology + assert(pointLeaf); + + for (auto value = leaf.beginValueOn(); value; ++value) { + const Index64 count = points::iterCount( + pointLeaf->beginIndexVoxel(value.getCoord(), mFilter)); + if (count > Index64(0)) { + value.setValue(ValueT(count)); + } else { + // disable any empty voxels + value.setValueOn(false); + } + } + } + +private: + const typename PointDataGridT::ConstAccessor mPointDataAccessor; + const FilterT& mFilter; +}; // struct PointsToScalarOp + + +/// @brief Compute scalar grid from PointDataGrid using a different transform +/// and while evaluating the point filter +template +struct PointsToTransformedScalarOp +{ + using PointDataLeafT = typename PointDataGridT::TreeType::LeafNodeType; + using ValueT = typename GridT::TreeType::ValueType; + using HandleT = AttributeHandle; + using CombinableT = typename GridCombinerOp::CombinableT; + + PointsToTransformedScalarOp(const math::Transform& targetTransform, + const math::Transform& sourceTransform, + const FilterT& filter, + const DeformerT& deformer, + CombinableT& combinable) + : mTargetTransform(targetTransform) + , mSourceTransform(sourceTransform) + , mFilter(filter) + , mDeformer(deformer) + , mCombinable(combinable) { } + + void operator()(const PointDataLeafT& leaf, size_t idx) const + { + DeformerT deformer(mDeformer); + + auto& grid = mCombinable.local(); + auto& countTree = grid.tree(); + tree::ValueAccessor accessor(countTree); + + deformer.reset(leaf, idx); + + auto handle = HandleT::create(leaf.constAttributeArray("P")); + + for (auto iter = leaf.beginIndexOn(mFilter); iter; iter++) { + + // extract index-space position + + Vec3d position = handle->get(*iter) + iter.getCoord().asVec3d(); + + // if deformer is designed to be used in index-space, perform deformation prior + // to transforming position to world-space, otherwise perform deformation afterwards + + if (DeformerTraits::IndexSpace) { + deformer.template apply(position, iter); + position = mSourceTransform.indexToWorld(position); + } + else { + position = mSourceTransform.indexToWorld(position); + deformer.template apply(position, iter); + } + + // determine coord of target grid + + const Coord ijk = mTargetTransform.worldToIndexCellCentered(position); + + // increment count in target voxel + + auto* newLeaf = accessor.touchLeaf(ijk); + assert(newLeaf); + voxelSum(*newLeaf, newLeaf->coordToOffset(ijk), ValueT(1)); + } + } + +private: + const openvdb::math::Transform& mTargetTransform; + const openvdb::math::Transform& mSourceTransform; + const FilterT& mFilter; + const DeformerT& mDeformer; + CombinableT& mCombinable; +}; // struct PointsToTransformedScalarOp + + +template +inline typename GridT::Ptr convertPointsToScalar( + const PointDataGridT& points, + const FilterT& filter, + bool threaded = true) +{ + using point_mask_internal::PointsToScalarOp; + + using GridTreeT = typename GridT::TreeType; + using ValueT = typename GridTreeT::ValueType; + + // copy the topology from the points grid + + typename GridTreeT::Ptr tree(new GridTreeT(points.constTree(), + false, openvdb::TopologyCopy())); + typename GridT::Ptr grid = GridT::create(tree); + grid->setTransform(points.transform().copy()); + + // early exit if no leaves + + if (points.constTree().leafCount() == 0) return grid; + + // early exit if mask and no group logic + + if (std::is_same::value && filter.state() == index::ALL) return grid; + + // evaluate point group filters to produce a subset of the generated mask + + tree::LeafManager leafManager(*tree); + + if (filter.state() == index::ALL) { + NullFilter nullFilter; + PointsToScalarOp pointsToScalarOp( + points, nullFilter); + leafManager.foreach(pointsToScalarOp, threaded); + } else { + // build mask from points in parallel only where filter evaluates to true + PointsToScalarOp pointsToScalarOp( + points, filter); + leafManager.foreach(pointsToScalarOp, threaded); + } + + return grid; +} + + +template +inline typename GridT::Ptr convertPointsToScalar( + PointDataGridT& points, + const openvdb::math::Transform& transform, + const FilterT& filter, + const DeformerT& deformer, + bool threaded = true) +{ + using point_mask_internal::PointsToTransformedScalarOp; + using point_mask_internal::GridCombinerOp; + + using CombinerOpT = GridCombinerOp; + using CombinableT = typename GridCombinerOp::CombinableT; + + // use the simpler method if the requested transform matches the existing one + + const openvdb::math::Transform& pointsTransform = points.constTransform(); + + if (transform == pointsTransform && std::is_same()) { + return convertPointsToScalar(points, filter, threaded); + } + + typename GridT::Ptr grid = GridT::create(); + grid->setTransform(transform.copy()); + + // early exit if no leaves + + if (points.constTree().leafCount() == 0) return grid; + + // compute mask grids in parallel using new transform + + CombinableT combiner; + + tree::LeafManager leafManager(points.tree()); + + if (filter.state() == index::ALL) { + NullFilter nullFilter; + PointsToTransformedScalarOp pointsToScalarOp( + transform, pointsTransform, nullFilter, deformer, combiner); + leafManager.foreach(pointsToScalarOp, threaded); + } else { + PointsToTransformedScalarOp pointsToScalarOp( + transform, pointsTransform, filter, deformer, combiner); + leafManager.foreach(pointsToScalarOp, threaded); + } + + // combine the mask grids into one + + CombinerOpT combineOp(*grid); + combiner.combine_each(combineOp); + + return grid; +} + + +} // namespace point_mask_internal + + +//////////////////////////////////////// + + +template +inline typename std::enable_if::value, + typename MaskT::Ptr>::type +convertPointsToMask( + const PointDataGridT& points, + const FilterT& filter, + bool threaded) +{ + return point_mask_internal::convertPointsToScalar( + points, filter, threaded); +} + + +template +inline typename std::enable_if::value, + typename MaskT::Ptr>::type +convertPointsToMask( + const PointDataGridT& points, + const openvdb::math::Transform& transform, + const FilterT& filter, + bool threaded) +{ + // This is safe because the PointDataGrid can only be modified by the deformer + using AdapterT = TreeAdapter; + auto& nonConstPoints = const_cast(points); + + NullDeformer deformer; + return point_mask_internal::convertPointsToScalar( + nonConstPoints, transform, filter, deformer, threaded); +} + + +//////////////////////////////////////// + + +// deprecated functions + + +template ::Type> +OPENVDB_DEPRECATED +inline typename std::enable_if::value, + typename MaskT::Ptr>::type +convertPointsToMask(const PointDataGridT& grid, + const std::vector& includeGroups, + const std::vector& excludeGroups) +{ + auto leaf = grid.tree().cbeginLeaf(); + if (!leaf) return MaskT::create(); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + return convertPointsToMask(grid, filter); +} + + +template ::Type> +OPENVDB_DEPRECATED +inline typename std::enable_if::value, + typename MaskT::Ptr>::type +convertPointsToMask(const PointDataGridT& grid, + const openvdb::math::Transform& transform, + const std::vector& includeGroups, + const std::vector& excludeGroups) +{ + auto leaf = grid.tree().cbeginLeaf(); + if (!leaf) return MaskT::create(); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + return convertPointsToMask(grid, transform, filter); +} + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointMove.h b/openvdb/points/PointMove.h new file mode 100644 index 00000000..5c33283b --- /dev/null +++ b/openvdb/points/PointMove.h @@ -0,0 +1,1237 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Dan Bailey +/// +/// @file PointMove.h +/// +/// @brief Ability to move VDB Points using a custom deformer. +/// +/// Deformers used when moving points are in world space by default and must adhere +/// to the interface described in the example below: +/// @code +/// struct MyDeformer +/// { +/// // A reset is performed on each leaf in turn before the points in that leaf are +/// // deformed. A leaf and leaf index (standard leaf traversal order) are supplied as +/// // the arguments, which matches the functor interface for LeafManager::foreach(). +/// template +/// void reset(LeafNoteType& leaf, size_t idx); +/// +/// // Evaluate the deformer and modify the given position to generate the deformed +/// // position. An index iterator is supplied as the argument to allow querying the +/// // point offset or containing voxel coordinate. +/// template +/// void apply(Vec3d& position, const IndexIterT& iter) const; +/// }; +/// @endcode +/// +/// @note The DeformerTraits struct (defined in PointMask.h) can be used to configure +/// a deformer to evaluate in index space. + +#ifndef OPENVDB_POINTS_POINT_MOVE_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_MOVE_HAS_BEEN_INCLUDED + +#include + +#include +#include + +#include + +#include +#include // for std::begin(), std::end() +#include +#include // for std::iota() +#include +#include +#include + +class TestPointMove; + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + +// dummy object for future use +namespace future { struct Advect { }; } + + +/// @brief Move points in a PointDataGrid using a custom deformer +/// @param points the PointDataGrid containing the points to be moved. +/// @param deformer a custom deformer that defines how to move the points. +/// @param filter an optional index filter +/// @param objectNotInUse for future use, this object is currently ignored +/// @param threaded enable or disable threading (threading is enabled by default) +template +inline void movePoints(PointDataGridT& points, + DeformerT& deformer, + const FilterT& filter = NullFilter(), + future::Advect* objectNotInUse = nullptr, + bool threaded = true); + + +/// @brief Move points in a PointDataGrid using a custom deformer and a new transform +/// @param points the PointDataGrid containing the points to be moved. +/// @param transform target transform to use for the resulting points. +/// @param deformer a custom deformer that defines how to move the points. +/// @param filter an optional index filter +/// @param objectNotInUse for future use, this object is currently ignored +/// @param threaded enable or disable threading (threading is enabled by default) +template +inline void movePoints(PointDataGridT& points, + const math::Transform& transform, + DeformerT& deformer, + const FilterT& filter = NullFilter(), + future::Advect* objectNotInUse = nullptr, + bool threaded = true); + + +// define leaf index in use as 32-bit +namespace point_move_internal { using LeafIndex = Index32; } + + +/// @brief A Deformer that caches the resulting positions from evaluating another Deformer +template +class CachedDeformer +{ +public: + using LeafIndex = point_move_internal::LeafIndex; + using Vec3T = typename math::Vec3; + using LeafVecT = std::vector; + using LeafMapT = std::unordered_map; + + // Internal data cache to allow the deformer to offer light-weight copying + struct Cache + { + struct Leaf + { + /// @brief clear data buffers and reset counter + void clear() { + vecData.clear(); + mapData.clear(); + totalSize = 0; + } + + LeafVecT vecData; + LeafMapT mapData; + Index totalSize = 0; + }; // struct Leaf + + std::vector leafs; + }; // struct Cache + + /// Cache is expected to be persistent for the lifetime of the CachedDeformer + explicit CachedDeformer(Cache& cache); + + /// Caches the result of evaluating the supplied point grid using the deformer and filter + /// @param grid the points to be moved + /// @param deformer the deformer to apply to the points + /// @param filter the point filter to use when evaluating the points + /// @param threaded enable or disable threading (threading is enabled by default) + template + void evaluate(PointDataGridT& grid, DeformerT& deformer, const FilterT& filter, + bool threaded = true); + + /// Stores pointers to the vector or map and optionally expands the map into a vector + /// @throw IndexError if idx is out-of-range of the leafs in the cache + template + void reset(const LeafT& leaf, size_t idx); + + /// Retrieve the new position from the cache + template + void apply(Vec3d& position, const IndexIterT& iter) const; + +private: + friend class ::TestPointMove; + + Cache& mCache; + const LeafVecT* mLeafVec = nullptr; + const LeafMapT* mLeafMap = nullptr; +}; // class CachedDeformer + + +//////////////////////////////////////// + + +namespace point_move_internal { + +using IndexArray = std::vector; + +using IndexTriple = std::tuple; +using IndexTripleArray = tbb::concurrent_vector; +using GlobalPointIndexMap = std::vector; +using GlobalPointIndexIndices = std::vector; + +using IndexPair = std::pair; +using IndexPairArray = std::vector; +using LocalPointIndexMap = std::vector; + +using LeafIndexArray = std::vector; +using LeafOffsetArray = std::vector; +using LeafMap = std::unordered_map; + + +template +struct BuildMoveMapsOp +{ + using LeafT = typename TreeT::LeafNodeType; + using LeafArrayT = std::vector; + using LeafManagerT = typename tree::LeafManager; + + BuildMoveMapsOp(const DeformerT& deformer, + GlobalPointIndexMap& globalMoveLeafMap, + LocalPointIndexMap& localMoveLeafMap, + const LeafMap& targetLeafMap, + const math::Transform& targetTransform, + const math::Transform& sourceTransform, + const FilterT& filter) + : mDeformer(deformer) + , mGlobalMoveLeafMap(globalMoveLeafMap) + , mLocalMoveLeafMap(localMoveLeafMap) + , mTargetLeafMap(targetLeafMap) + , mTargetTransform(targetTransform) + , mSourceTransform(sourceTransform) + , mFilter(filter) { } + + void operator()(LeafT& leaf, size_t idx) const + { + DeformerT deformer(mDeformer); + deformer.reset(leaf, idx); + + // determine source leaf node origin and offset in the source leaf vector + + Coord sourceLeafOrigin = leaf.origin(); + + auto sourceHandle = AttributeWriteHandle::create(leaf.attributeArray("P")); + + for (auto iter = leaf.beginIndexOn(mFilter); iter; iter++) { + + const bool useIndexSpace = DeformerTraits::IndexSpace; + + // extract index-space position and apply index-space deformation (if applicable) + + Vec3d positionIS = sourceHandle->get(*iter) + iter.getCoord().asVec3d(); + if (useIndexSpace) { + deformer.apply(positionIS, iter); + } + + // transform to world-space position and apply world-space deformation (if applicable) + + Vec3d positionWS = mSourceTransform.indexToWorld(positionIS); + if (!useIndexSpace) { + deformer.apply(positionWS, iter); + } + + // transform to index-space position of target grid + + positionIS = mTargetTransform.worldToIndex(positionWS); + + // determine target voxel and offset + + Coord targetVoxel = Coord::round(positionIS); + Index targetOffset = LeafT::coordToOffset(targetVoxel); + + // set new local position in source transform space (if point has been deformed) + + Vec3d voxelPosition(positionIS - targetVoxel.asVec3d()); + sourceHandle->set(*iter, voxelPosition); + + // determine target leaf node origin and offset in the target leaf vector + + Coord targetLeafOrigin = targetVoxel & ~(LeafT::DIM - 1); + assert(mTargetLeafMap.find(targetLeafOrigin) != mTargetLeafMap.end()); + const LeafIndex targetLeafOffset(mTargetLeafMap.at(targetLeafOrigin)); + + // insert into move map based on whether point ends up in a new leaf node or not + + if (targetLeafOrigin == sourceLeafOrigin) { + mLocalMoveLeafMap[targetLeafOffset].emplace_back(targetOffset, *iter); + } + else { + mGlobalMoveLeafMap[targetLeafOffset].push_back(IndexTriple( + LeafIndex(static_cast(idx)), targetOffset, *iter)); + } + } + } + +private: + const DeformerT& mDeformer; + GlobalPointIndexMap& mGlobalMoveLeafMap; + LocalPointIndexMap& mLocalMoveLeafMap; + const LeafMap& mTargetLeafMap; + const math::Transform& mTargetTransform; + const math::Transform& mSourceTransform; + const FilterT& mFilter; +}; // struct BuildMoveMapsOp + +template +inline Index +indexOffsetFromVoxel(const Index voxelOffset, const LeafT& leaf, IndexArray& offsets) +{ + // compute the target point index by summing the point index of the previous + // voxel with the current number of points added to this voxel, tracked by the + // offsets array + + Index targetOffset = offsets[voxelOffset]++; + if (voxelOffset > 0) { + targetOffset += static_cast(leaf.getValue(voxelOffset - 1)); + } + return targetOffset; +} + + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + + +template +struct GlobalMovePointsOp +{ + using LeafT = typename TreeT::LeafNodeType; + using LeafArrayT = std::vector; + using LeafManagerT = typename tree::LeafManager; + using AttributeArrays = std::vector; + + GlobalMovePointsOp(LeafOffsetArray& offsetMap, + LeafManagerT& sourceLeafManager, + const Index attributeIndex, + const GlobalPointIndexMap& moveLeafMap, + const GlobalPointIndexIndices& moveLeafIndices) + : mOffsetMap(offsetMap) + , mSourceLeafManager(sourceLeafManager) + , mAttributeIndex(attributeIndex) + , mMoveLeafMap(moveLeafMap) + , mMoveLeafIndices(moveLeafIndices) { } + + // A CopyIterator is designed to use the indices in a GlobalPointIndexMap for this leaf + // and match the interface required for AttributeArray::copyValues() + struct CopyIterator + { + CopyIterator(const LeafT& leaf, const IndexArray& sortedIndices, + const IndexTripleArray& moveIndices, IndexArray& offsets) + : mLeaf(leaf) + , mSortedIndices(sortedIndices) + , mMoveIndices(moveIndices) + , mOffsets(offsets) { } + + operator bool() const { return bool(mIt); } + + void reset(Index startIndex, Index endIndex) + { + mIndex = startIndex; + mEndIndex = endIndex; + this->advance(); + } + + CopyIterator& operator++() + { + this->advance(); + return *this; + } + + Index leafIndex(Index i) const + { + if (i < mSortedIndices.size()) { + return std::get<0>(this->leafIndexTriple(i)); + } + return std::numeric_limits::max(); + } + + Index sourceIndex() const + { + assert(mIt); + return std::get<2>(*mIt); + } + + Index targetIndex() const + { + assert(mIt); + return indexOffsetFromVoxel(std::get<1>(*mIt), mLeaf, mOffsets); + } + + private: + void advance() + { + if (mIndex >= mEndIndex || mIndex >= mSortedIndices.size()) { + mIt = nullptr; + } + else { + mIt = &this->leafIndexTriple(mIndex); + } + ++mIndex; + } + + const IndexTriple& leafIndexTriple(Index i) const + { + return mMoveIndices[mSortedIndices[i]]; + } + + private: + const LeafT& mLeaf; + Index mIndex; + Index mEndIndex; + const IndexArray& mSortedIndices; + const IndexTripleArray& mMoveIndices; + IndexArray& mOffsets; + const IndexTriple* mIt = nullptr; + }; // struct CopyIterator + + void operator()(LeafT& leaf, size_t idx) const + { + const IndexTripleArray& moveIndices = mMoveLeafMap[idx]; + if (moveIndices.empty()) return; + const IndexArray& sortedIndices = mMoveLeafIndices[idx]; + + // extract per-voxel offsets for this leaf + + LeafIndexArray& offsets = mOffsetMap[idx]; + + // extract target array and ensure data is out-of-core and non-uniform + + auto& targetArray = leaf.attributeArray(mAttributeIndex); + targetArray.loadData(); + targetArray.expand(); + + // perform the copy + + CopyIterator copyIterator(leaf, sortedIndices, moveIndices, offsets); + + // use the sorted indices to track the index of the source leaf + + Index sourceLeafIndex = copyIterator.leafIndex(0); + Index startIndex = 0; + + for (size_t i = 1; i <= sortedIndices.size(); i++) { + Index endIndex = static_cast(i); + Index newSourceLeafIndex = copyIterator.leafIndex(endIndex); + + // when it changes, do a batch-copy of all the indices that lie within this range + // TODO: this step could use nested parallelization for cases where there are a + // large number of points being moved per attribute + + if (newSourceLeafIndex > sourceLeafIndex) { + copyIterator.reset(startIndex, endIndex); + + const LeafT& sourceLeaf = mSourceLeafManager.leaf(sourceLeafIndex); + const auto& sourceArray = sourceLeaf.constAttributeArray(mAttributeIndex); + sourceArray.loadData(); + + targetArray.copyValuesUnsafe(sourceArray, copyIterator); + + sourceLeafIndex = newSourceLeafIndex; + startIndex = endIndex; + } + } + } + +private: + LeafOffsetArray& mOffsetMap; + LeafManagerT& mSourceLeafManager; + const Index mAttributeIndex; + const GlobalPointIndexMap& mMoveLeafMap; + const GlobalPointIndexIndices& mMoveLeafIndices; +}; // struct GlobalMovePointsOp + + +template +struct LocalMovePointsOp +{ + using LeafT = typename TreeT::LeafNodeType; + using LeafArrayT = std::vector; + using LeafManagerT = typename tree::LeafManager; + using AttributeArrays = std::vector; + + LocalMovePointsOp( LeafOffsetArray& offsetMap, + const LeafIndexArray& sourceIndices, + LeafManagerT& sourceLeafManager, + const Index attributeIndex, + const LocalPointIndexMap& moveLeafMap) + : mOffsetMap(offsetMap) + , mSourceIndices(sourceIndices) + , mSourceLeafManager(sourceLeafManager) + , mAttributeIndex(attributeIndex) + , mMoveLeafMap(moveLeafMap) { } + + // A CopyIterator is designed to use the indices in a LocalPointIndexMap for this leaf + // and match the interface required for AttributeArray::copyValues() + struct CopyIterator + { + CopyIterator(const LeafT& leaf, const IndexPairArray& indices, IndexArray& offsets) + : mLeaf(leaf) + , mIndices(indices) + , mOffsets(offsets) { } + + operator bool() const { return mIndex < static_cast(mIndices.size()); } + + CopyIterator& operator++() { ++mIndex; return *this; } + + Index sourceIndex() const + { + return mIndices[mIndex].second; + } + + Index targetIndex() const + { + return indexOffsetFromVoxel(mIndices[mIndex].first, mLeaf, mOffsets); + } + + private: + const LeafT& mLeaf; + const IndexPairArray& mIndices; + IndexArray& mOffsets; + int mIndex = 0; + }; // struct CopyIterator + + void operator()(LeafT& leaf, size_t idx) const + { + const IndexPairArray& moveIndices = mMoveLeafMap[idx]; + if (moveIndices.empty()) return; + + // extract per-voxel offsets for this leaf + + LeafIndexArray& offsets = mOffsetMap[idx]; + + // extract source array that has the same origin as the target leaf + + assert(idx < mSourceIndices.size()); + const Index sourceLeafOffset(mSourceIndices[idx]); + LeafT& sourceLeaf = mSourceLeafManager.leaf(sourceLeafOffset); + const auto& sourceArray = sourceLeaf.constAttributeArray(mAttributeIndex); + sourceArray.loadData(); + + // extract target array and ensure data is out-of-core and non-uniform + + auto& targetArray = leaf.attributeArray(mAttributeIndex); + targetArray.loadData(); + targetArray.expand(); + + // perform the copy + + CopyIterator copyIterator(leaf, moveIndices, offsets); + targetArray.copyValuesUnsafe(sourceArray, copyIterator); + } + +private: + LeafOffsetArray& mOffsetMap; + const LeafIndexArray& mSourceIndices; + LeafManagerT& mSourceLeafManager; + const Index mAttributeIndex; + const LocalPointIndexMap& mMoveLeafMap; +}; // struct LocalMovePointsOp + + +#else + + +// The following infrastructure - ArrayProcessor, PerformTypedMoveOp, processTypedArray() +// is required to improve AttributeArray copying performance beyond using the virtual function +// AttributeArray::set(Index, AttributeArray&, Index). An ABI=6 addition to AttributeArray +// improves this by introducing an AttributeArray::copyValues() method to significantly +// simplify this logic without incurring the same virtual function cost. + + +/// Helper class used internally by processTypedArray() +template +struct ArrayProcessor { + static inline void call(OpType& op, const AttributeArray& array) { +#ifdef _MSC_VER + op.operator()(array); +#else + op.template operator()(array); +#endif + } +}; + + +/// @brief Utility function that, given a generic attribute array, +/// calls a functor with the fully-resolved value type of the array +template +bool +processTypedArray(const ArrayType& array, OpType& op) +{ + using namespace openvdb; + using namespace openvdb::math; + if (array.template hasValueType()) ArrayProcessor::call(op, array); + else if (array.template hasValueType()) ArrayProcessor::call(op, array); + else if (array.template hasValueType()) ArrayProcessor::call(op, array); + else if (array.template hasValueType()) ArrayProcessor::call(op, array); + else if (array.template hasValueType()) ArrayProcessor::call(op, array); + else if (array.template hasValueType()) ArrayProcessor::call(op, array); + else if (array.template hasValueType>()) ArrayProcessor, OpType>::call(op, array); + else if (array.template hasValueType>()) ArrayProcessor, OpType>::call(op, array); + else if (array.template hasValueType>()) ArrayProcessor, OpType>::call(op, array); + else if (array.template hasValueType()) ArrayProcessor::call(op, array); + else if (array.template hasValueType()) ArrayProcessor::call(op, array); + else if (array.template hasValueType>()) ArrayProcessor, OpType>::call(op, array); + else if (array.template hasValueType>()) ArrayProcessor, OpType>::call(op, array); + else if (array.template hasValueType>()) ArrayProcessor, OpType>::call(op, array); + else if (array.template hasValueType>()) ArrayProcessor, OpType>::call(op, array); + else if (array.template hasValueType>()) ArrayProcessor, OpType>::call(op, array); + else if (array.template hasValueType>()) ArrayProcessor, OpType>::call(op, array); + else return false; + return true; +} + + +/// Cache read and write attribute handles to amortize construction cost +struct AttributeHandles +{ + using HandleArray = std::vector::Ptr>; + + AttributeHandles(const size_t size) + : mHandles() { mHandles.reserve(size); } + + AttributeArray& getArray(const Index leafOffset) + { + auto* handle = reinterpret_cast*>(mHandles[leafOffset].get()); + assert(handle); + return handle->array(); + } + + const AttributeArray& getConstArray(const Index leafOffset) const + { + const auto* handle = mHandles[leafOffset].get(); + assert(handle); + return handle->array(); + } + + template + AttributeHandle& getHandle(const Index leafOffset) + { + auto* handle = reinterpret_cast*>(mHandles[leafOffset].get()); + assert(handle); + return *handle; + } + + template + AttributeWriteHandle& getWriteHandle(const Index leafOffset) + { + auto* handle = reinterpret_cast*>(mHandles[leafOffset].get()); + assert(handle); + return *handle; + } + + /// Create a handle and reinterpret cast as an int handle to store + struct CacheHandleOp + { + CacheHandleOp(HandleArray& handles) + : mHandles(handles) { } + + template + void operator()(const AttributeArray& array) const + { + auto* handleAsInt = reinterpret_cast*>( + new AttributeHandle(array)); + mHandles.emplace_back(handleAsInt); + } + + private: + HandleArray& mHandles; + }; // struct CacheHandleOp + + template + void cache(const LeafRangeT& range, const Index attributeIndex) + { + using namespace openvdb::math; + + mHandles.clear(); + CacheHandleOp op(mHandles); + + for (auto leaf = range.begin(); leaf; ++leaf) { + const auto& array = leaf->constAttributeArray(attributeIndex); + processTypedArray(array, op); + } + } + +private: + HandleArray mHandles; +}; // struct AttributeHandles + + +template +struct GlobalMovePointsOp +{ + using LeafT = typename TreeT::LeafNodeType; + using LeafArrayT = std::vector; + using LeafManagerT = typename tree::LeafManager; + + GlobalMovePointsOp(LeafOffsetArray& offsetMap, + AttributeHandles& targetHandles, + AttributeHandles& sourceHandles, + const Index attributeIndex, + const GlobalPointIndexMap& moveLeafMap, + const GlobalPointIndexIndices& moveLeafIndices) + : mOffsetMap(offsetMap) + , mTargetHandles(targetHandles) + , mSourceHandles(sourceHandles) + , mAttributeIndex(attributeIndex) + , mMoveLeafMap(moveLeafMap) + , mMoveLeafIndices(moveLeafIndices) { } + + struct PerformTypedMoveOp + { + PerformTypedMoveOp(AttributeHandles& targetHandles, AttributeHandles& sourceHandles, + Index targetOffset, const LeafT& targetLeaf, + IndexArray& offsets, const IndexTripleArray& indices, + const IndexArray& sortedIndices) + : mTargetHandles(targetHandles) + , mSourceHandles(sourceHandles) + , mTargetOffset(targetOffset) + , mTargetLeaf(targetLeaf) + , mOffsets(offsets) + , mIndices(indices) + , mSortedIndices(sortedIndices) { } + + template + void operator()(const AttributeArray&) const + { + auto& targetHandle = mTargetHandles.getWriteHandle(mTargetOffset); + targetHandle.expand(); + + for (const auto& index : mSortedIndices) { + const auto& it = mIndices[index]; + const auto& sourceHandle = mSourceHandles.getHandle(std::get<0>(it)); + const Index targetIndex = indexOffsetFromVoxel(std::get<1>(it), mTargetLeaf, mOffsets); + for (Index i = 0; i < sourceHandle.stride(); i++) { + ValueT sourceValue = sourceHandle.get(std::get<2>(it), i); + targetHandle.set(targetIndex, i, sourceValue); + } + } + } + + private: + AttributeHandles& mTargetHandles; + AttributeHandles& mSourceHandles; + Index mTargetOffset; + const LeafT& mTargetLeaf; + IndexArray& mOffsets; + const IndexTripleArray& mIndices; + const IndexArray& mSortedIndices; + }; // struct PerformTypedMoveOp + + void performMove(Index targetOffset, const LeafT& targetLeaf, + IndexArray& offsets, const IndexTripleArray& indices, + const IndexArray& sortedIndices) const + { + auto& targetArray = mTargetHandles.getArray(targetOffset); + targetArray.loadData(); + targetArray.expand(); + + for (const auto& index : sortedIndices) { + const auto& it = indices[index]; + + const auto& sourceArray = mSourceHandles.getConstArray(std::get<0>(it)); + + const Index sourceOffset = std::get<2>(it); + const Index targetOffset = indexOffsetFromVoxel(std::get<1>(it), targetLeaf, offsets); + + targetArray.set(targetOffset, sourceArray, sourceOffset); + } + } + + void operator()(LeafT& leaf, size_t aIdx) const + { + const Index idx(static_cast(aIdx)); + const auto& moveIndices = mMoveLeafMap[aIdx]; + if (moveIndices.empty()) return; + const auto& sortedIndices = mMoveLeafIndices[aIdx]; + + // extract per-voxel offsets for this leaf + + auto& offsets = mOffsetMap[aIdx]; + + const auto& array = leaf.constAttributeArray(mAttributeIndex); + + PerformTypedMoveOp op(mTargetHandles, mSourceHandles, idx, leaf, offsets, + moveIndices, sortedIndices); + if (!processTypedArray(array, op)) { + this->performMove(idx, leaf, offsets, moveIndices, sortedIndices); + } + } + +private: + LeafOffsetArray& mOffsetMap; + AttributeHandles& mTargetHandles; + AttributeHandles& mSourceHandles; + const Index mAttributeIndex; + const GlobalPointIndexMap& mMoveLeafMap; + const GlobalPointIndexIndices& mMoveLeafIndices; +}; // struct GlobalMovePointsOp + + +template +struct LocalMovePointsOp +{ + using LeafT = typename TreeT::LeafNodeType; + using LeafArrayT = std::vector; + using LeafManagerT = typename tree::LeafManager; + + LocalMovePointsOp( LeafOffsetArray& offsetMap, + AttributeHandles& targetHandles, + const LeafIndexArray& sourceIndices, + AttributeHandles& sourceHandles, + const Index attributeIndex, + const LocalPointIndexMap& moveLeafMap) + : mOffsetMap(offsetMap) + , mTargetHandles(targetHandles) + , mSourceIndices(sourceIndices) + , mSourceHandles(sourceHandles) + , mAttributeIndex(attributeIndex) + , mMoveLeafMap(moveLeafMap) { } + + struct PerformTypedMoveOp + { + PerformTypedMoveOp(AttributeHandles& targetHandles, AttributeHandles& sourceHandles, + Index targetOffset, Index sourceOffset, const LeafT& targetLeaf, + IndexArray& offsets, const IndexPairArray& indices) + : mTargetHandles(targetHandles) + , mSourceHandles(sourceHandles) + , mTargetOffset(targetOffset) + , mSourceOffset(sourceOffset) + , mTargetLeaf(targetLeaf) + , mOffsets(offsets) + , mIndices(indices) { } + + template + void operator()(const AttributeArray&) const + { + auto& targetHandle = mTargetHandles.getWriteHandle(mTargetOffset); + const auto& sourceHandle = mSourceHandles.getHandle(mSourceOffset); + + targetHandle.expand(); + + for (const auto& it : mIndices) { + const Index targetIndex = indexOffsetFromVoxel(it.first, mTargetLeaf, mOffsets); + for (Index i = 0; i < sourceHandle.stride(); i++) { + ValueT sourceValue = sourceHandle.get(it.second, i); + targetHandle.set(targetIndex, i, sourceValue); + } + } + } + + private: + AttributeHandles& mTargetHandles; + AttributeHandles& mSourceHandles; + Index mTargetOffset; + Index mSourceOffset; + const LeafT& mTargetLeaf; + IndexArray& mOffsets; + const IndexPairArray& mIndices; + }; // struct PerformTypedMoveOp + + template + void performTypedMove(Index sourceOffset, Index targetOffset, const LeafT& targetLeaf, + IndexArray& offsets, const IndexPairArray& indices) const + { + auto& targetHandle = mTargetHandles.getWriteHandle(targetOffset); + const auto& sourceHandle = mSourceHandles.getHandle(sourceOffset); + + targetHandle.expand(); + + for (const auto& it : indices) { + const Index tgtOffset = indexOffsetFromVoxel(it.first, targetLeaf, offsets); + for (Index i = 0; i < sourceHandle.stride(); i++) { + ValueT sourceValue = sourceHandle.get(it.second, i); + targetHandle.set(tgtOffset, i, sourceValue); + } + } + } + + void performMove(Index targetOffset, Index sourceOffset, const LeafT& targetLeaf, + IndexArray& offsets, const IndexPairArray& indices) const + { + auto& targetArray = mTargetHandles.getArray(targetOffset); + const auto& sourceArray = mSourceHandles.getConstArray(sourceOffset); + + for (const auto& it : indices) { + const Index sourceOffset = it.second; + const Index targetOffset = indexOffsetFromVoxel(it.first, targetLeaf, offsets); + + targetArray.set(targetOffset, sourceArray, sourceOffset); + } + } + + void operator()(const LeafT& leaf, size_t aIdx) const + { + const Index idx(static_cast(aIdx)); + const auto& moveIndices = mMoveLeafMap.at(aIdx); + if (moveIndices.empty()) return; + + // extract target leaf and per-voxel offsets for this leaf + + auto& offsets = mOffsetMap[aIdx]; + + // extract source leaf that has the same origin as the target leaf (if any) + + assert(aIdx < mSourceIndices.size()); + const Index sourceOffset(mSourceIndices[aIdx]); + + const auto& array = leaf.constAttributeArray(mAttributeIndex); + + PerformTypedMoveOp op(mTargetHandles, mSourceHandles, + idx, sourceOffset, leaf, offsets, moveIndices); + if (!processTypedArray(array, op)) { + this->performMove(idx, sourceOffset, leaf, offsets, moveIndices); + } + } + +private: + LeafOffsetArray& mOffsetMap; + AttributeHandles& mTargetHandles; + const LeafIndexArray& mSourceIndices; + AttributeHandles& mSourceHandles; + const Index mAttributeIndex; + const LocalPointIndexMap& mMoveLeafMap; +}; // struct LocalMovePointsOp + + +#endif // OPENVDB_ABI_VERSION_NUMBER >= 6 + + +} // namespace point_move_internal + + +//////////////////////////////////////// + + +template +inline void movePoints( PointDataGridT& points, + const math::Transform& transform, + DeformerT& deformer, + const FilterT& filter, + future::Advect* objectNotInUse, + bool threaded) +{ + using LeafIndex = point_move_internal::LeafIndex; + using PointDataTreeT = typename PointDataGridT::TreeType; + using LeafT = typename PointDataTreeT::LeafNodeType; + using LeafManagerT = typename tree::LeafManager; + + using namespace point_move_internal; + + // this object is for future use only + assert(!objectNotInUse); + (void)objectNotInUse; + + PointDataTreeT& tree = points.tree(); + + // early exit if no LeafNodes + + PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + // build voxel topology taking into account any point group deletion + + auto newPoints = point_mask_internal::convertPointsToScalar( + points, transform, filter, deformer, threaded); + auto& newTree = newPoints->tree(); + + // create leaf managers for both trees + + LeafManagerT sourceLeafManager(tree); + LeafManagerT targetLeafManager(newTree); + + // extract the existing attribute set + const auto& existingAttributeSet = points.tree().cbeginLeaf()->attributeSet(); + + // build a coord -> index map for looking up target leafs by origin and a faster + // unordered map for finding the source index from a target index + + LeafMap targetLeafMap; + LeafIndexArray sourceIndices(targetLeafManager.leafCount(), + std::numeric_limits::max()); + + LeafOffsetArray offsetMap(targetLeafManager.leafCount()); + + { + LeafMap sourceLeafMap; + auto sourceRange = sourceLeafManager.leafRange(); + for (auto leaf = sourceRange.begin(); leaf; ++leaf) { + sourceLeafMap.insert({leaf->origin(), LeafIndex(static_cast(leaf.pos()))}); + } + auto targetRange = targetLeafManager.leafRange(); + for (auto leaf = targetRange.begin(); leaf; ++leaf) { + targetLeafMap.insert({leaf->origin(), LeafIndex(static_cast(leaf.pos()))}); + } + + // acquire registry lock to avoid locking when appending attributes in parallel + + AttributeArray::ScopedRegistryLock lock; + + // perform four independent per-leaf operations in parallel + targetLeafManager.foreach( + [&](LeafT& leaf, size_t idx) { + // map frequency => cumulative histogram + auto* buffer = leaf.buffer().data(); + for (Index i = 1; i < leaf.buffer().size(); i++) { + buffer[i] = buffer[i-1] + buffer[i]; + } + // replace attribute set with a copy of the existing one + leaf.replaceAttributeSet( + new AttributeSet(existingAttributeSet, leaf.getLastValue(), &lock), + /*allowMismatchingDescriptors=*/true); + // store the index of the source leaf in a corresponding target leaf array + const auto it = sourceLeafMap.find(leaf.origin()); + if (it != sourceLeafMap.end()) { + sourceIndices[idx] = it->second; + } + // allocate offset maps + offsetMap[idx].resize(LeafT::SIZE); + }, + threaded); + } + + // moving leaf + + GlobalPointIndexMap globalMoveLeafMap(targetLeafManager.leafCount()); + LocalPointIndexMap localMoveLeafMap(targetLeafManager.leafCount()); + + // build global and local move leaf maps and update local positions + + if (filter.state() == index::ALL) { + NullFilter nullFilter; + BuildMoveMapsOp op(deformer, + globalMoveLeafMap, localMoveLeafMap, targetLeafMap, + transform, points.transform(), nullFilter); + sourceLeafManager.foreach(op, threaded); + } else { + BuildMoveMapsOp op(deformer, + globalMoveLeafMap, localMoveLeafMap, targetLeafMap, + transform, points.transform(), filter); + sourceLeafManager.foreach(op, threaded); + } + + // build a sorted index vector for each leaf that references the global move map + // indices in order of their source leafs and voxels to ensure determinism in the + // resulting point orders + + GlobalPointIndexIndices globalMoveLeafIndices(globalMoveLeafMap.size()); + + targetLeafManager.foreach( + [&](LeafT& /*leaf*/, size_t idx) { + const IndexTripleArray& moveIndices = globalMoveLeafMap[idx]; + if (moveIndices.empty()) return; + + IndexArray& sortedIndices = globalMoveLeafIndices[idx]; + sortedIndices.resize(moveIndices.size()); + std::iota(std::begin(sortedIndices), std::end(sortedIndices), 0); + std::sort(std::begin(sortedIndices), std::end(sortedIndices), + [&](int i, int j) + { + const Index& indexI0(std::get<0>(moveIndices[i])); + const Index& indexJ0(std::get<0>(moveIndices[j])); + if (indexI0 < indexJ0) return true; + if (indexI0 > indexJ0) return false; + return std::get<2>(moveIndices[i]) < std::get<2>(moveIndices[j]); + } + ); + }, + threaded); + +#if OPENVDB_ABI_VERSION_NUMBER < 6 + // initialize attribute handles + AttributeHandles sourceHandles(sourceLeafManager.leafCount()); + AttributeHandles targetHandles(targetLeafManager.leafCount()); +#endif + + for (const auto& it : existingAttributeSet.descriptor().map()) { + + const Index attributeIndex = static_cast(it.second); + + // zero offsets + targetLeafManager.foreach( + [&offsetMap](const LeafT& /*leaf*/, size_t idx) { + std::fill(offsetMap[idx].begin(), offsetMap[idx].end(), 0); + }, + threaded); + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + + // move points between leaf nodes + + GlobalMovePointsOp globalMoveOp(offsetMap, + sourceLeafManager, attributeIndex, globalMoveLeafMap, globalMoveLeafIndices); + targetLeafManager.foreach(globalMoveOp, threaded); + + // move points within leaf nodes + + LocalMovePointsOp localMoveOp(offsetMap, + sourceIndices, sourceLeafManager, attributeIndex, localMoveLeafMap); + targetLeafManager.foreach(localMoveOp, threaded); +#else + // cache attribute handles + + sourceHandles.cache(sourceLeafManager.leafRange(), attributeIndex); + targetHandles.cache(targetLeafManager.leafRange(), attributeIndex); + + // move points between leaf nodes + + GlobalMovePointsOp globalMoveOp(offsetMap, targetHandles, + sourceHandles, attributeIndex, globalMoveLeafMap, globalMoveLeafIndices); + targetLeafManager.foreach(globalMoveOp, threaded); + + // move points within leaf nodes + + LocalMovePointsOp localMoveOp(offsetMap, targetHandles, + sourceIndices, sourceHandles, + attributeIndex, localMoveLeafMap); + targetLeafManager.foreach(localMoveOp, threaded); +#endif // OPENVDB_ABI_VERSION_NUMBER >= 6 + } + + points.setTree(newPoints->treePtr()); +} + + +template +inline void movePoints( PointDataGridT& points, + DeformerT& deformer, + const FilterT& filter, + future::Advect* objectNotInUse, + bool threaded) +{ + movePoints(points, points.transform(), deformer, filter, objectNotInUse, threaded); +} + + +//////////////////////////////////////// + + +template +CachedDeformer::CachedDeformer(Cache& cache) + : mCache(cache) { } + + +template +template +void CachedDeformer::evaluate(PointDataGridT& grid, DeformerT& deformer, const FilterT& filter, + bool threaded) +{ + using TreeT = typename PointDataGridT::TreeType; + using LeafT = typename TreeT::LeafNodeType; + using LeafManagerT = typename tree::LeafManager; + LeafManagerT leafManager(grid.tree()); + + // initialize cache + auto& leafs = mCache.leafs; + leafs.resize(leafManager.leafCount()); + + const auto& transform = grid.transform(); + + // insert deformed positions into the cache + + auto cachePositionsOp = [&](const LeafT& leaf, size_t idx) { + + const Index64 totalPointCount = leaf.pointCount(); + if (totalPointCount == 0) return; + + // deformer is copied to ensure that it is unique per-thread + + DeformerT newDeformer(deformer); + + newDeformer.reset(leaf, idx); + + auto handle = AttributeHandle::create(leaf.constAttributeArray("P")); + + auto& cache = leafs[idx]; + cache.clear(); + + // only insert into a vector directly if the filter evaluates all points + // and all points are stored in active voxels + const bool useVector = filter.state() == index::ALL && + (leaf.isDense() || (leaf.onPointCount() == leaf.pointCount())); + if (useVector) { + cache.vecData.resize(totalPointCount); + } + + for (auto iter = leaf.beginIndexOn(filter); iter; iter++) { + + // extract index-space position and apply index-space deformation (if defined) + + Vec3d position = handle->get(*iter) + iter.getCoord().asVec3d(); + + // if deformer is designed to be used in index-space, perform deformation prior + // to transforming position to world-space, otherwise perform deformation afterwards + + if (DeformerTraits::IndexSpace) { + newDeformer.apply(position, iter); + position = transform.indexToWorld(position); + } + else { + position = transform.indexToWorld(position); + newDeformer.apply(position, iter); + } + + // insert new position into the cache + + if (useVector) { + cache.vecData[*iter] = static_cast(position); + } + else { + cache.mapData.insert({*iter, static_cast(position)}); + } + } + + // store the total number of points to allow use of an expanded vector on access + + if (!cache.mapData.empty()) { + cache.totalSize = static_cast(totalPointCount); + } + }; + + leafManager.foreach(cachePositionsOp, threaded); +} + + +template +template +void CachedDeformer::reset(const LeafT& /*leaf*/, size_t idx) +{ + if (idx >= mCache.leafs.size()) { + if (mCache.leafs.empty()) { + throw IndexError("No leafs in cache, perhaps CachedDeformer has not been evaluated?"); + } else { + throw IndexError("Leaf index is out-of-range of cache leafs."); + } + } + auto& cache = mCache.leafs[idx]; + if (!cache.mapData.empty()) { + mLeafMap = &cache.mapData; + mLeafVec = nullptr; + } + else { + mLeafVec = &cache.vecData; + mLeafMap = nullptr; + } +} + + +template +template +void CachedDeformer::apply(Vec3d& position, const IndexIterT& iter) const +{ + assert(*iter >= 0); + + if (mLeafMap) { + auto it = mLeafMap->find(*iter); + if (it == mLeafMap->end()) return; + position = static_cast(it->second); + } + else { + assert(mLeafVec); + + if (mLeafVec->empty()) return; + assert(*iter < mLeafVec->size()); + position = static_cast((*mLeafVec)[*iter]); + } +} + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_POINT_MOVE_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointSample.h b/openvdb/points/PointSample.h new file mode 100644 index 00000000..3c588936 --- /dev/null +++ b/openvdb/points/PointSample.h @@ -0,0 +1,544 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Nick Avramoussis, Francisco Gochez, Dan Bailey +/// +/// @file points/PointSample.h +/// +/// @brief Sample a VDB Grid onto a VDB Points attribute + +#ifndef OPENVDB_POINTS_POINT_SAMPLE_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_SAMPLE_HAS_BEEN_INCLUDED + +#include +#include + +#include "PointDataGrid.h" +#include "PointAttribute.h" + +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + + +/// @brief Performs closest point sampling from a VDB grid onto a VDB Points attribute +/// @param points the PointDataGrid whose points will be sampled on to +/// @param sourceGrid VDB grid which will be sampled +/// @param targetAttribute a target attribute on the points which will hold samples. This +/// attribute will be created with the source grid type if it does +/// not exist, and with the source grid name if the name is empty +/// @param filter an optional index filter +/// @param interrupter an optional interrupter +/// @note The target attribute may exist provided it can be cast to the SourceGridT ValueType +template +inline void pointSample(PointDataGridT& points, + const SourceGridT& sourceGrid, + const Name& targetAttribute = "", + const FilterT& filter = NullFilter(), + InterrupterT* const interrupter = nullptr); + + +/// @brief Performs tri-linear sampling from a VDB grid onto a VDB Points attribute +/// @param points the PointDataGrid whose points will be sampled on to +/// @param sourceGrid VDB grid which will be sampled +/// @param targetAttribute a target attribute on the points which will hold samples. This +/// attribute will be created with the source grid type if it does +/// not exist, and with the source grid name if the name is empty +/// @param filter an optional index filter +/// @param interrupter an optional interrupter +/// @note The target attribute may exist provided it can be cast to the SourceGridT ValueType +template +inline void boxSample( PointDataGridT& points, + const SourceGridT& sourceGrid, + const Name& targetAttribute = "", + const FilterT& filter = NullFilter(), + InterrupterT* const interrupter = nullptr); + + +/// @brief Performs tri-quadratic sampling from a VDB grid onto a VDB Points attribute +/// @param points the PointDataGrid whose points will be sampled on to +/// @param sourceGrid VDB grid which will be sampled +/// @param targetAttribute a target attribute on the points which will hold samples. This +/// attribute will be created with the source grid type if it does +/// not exist, and with the source grid name if the name is empty +/// @param filter an optional index filter +/// @param interrupter an optional interrupter +/// @note The target attribute may exist provided it can be cast to the SourceGridT ValueType +template +inline void quadraticSample(PointDataGridT& points, + const SourceGridT& sourceGrid, + const Name& targetAttribute = "", + const FilterT& filter = NullFilter(), + InterrupterT* const interrupter = nullptr); + + +// This struct samples the source grid accessor using the world-space position supplied, +// with SamplerT providing the sampling scheme. In the case where ValueT does not match +// the value type of the source grid, the sample() method will also convert the sampled +// value into a ValueT value, using round-to-nearest for float-to-integer conversion. +struct SampleWithRounding +{ + template + inline ValueT sample(const AccessorT& accessor, const Vec3d& position) const; +}; + + +// A dummy struct that is used to mean that the sampled attribute should either match the type +// of the existing attribute or the type of the source grid (if the attribute doesn't exist yet) +struct DummySampleType { }; + + +/// @brief Performs sampling and conversion from a VDB grid onto a VDB Points attribute +/// @param order the sampling order - 0 = closest-point, 1 = trilinear, 2 = triquadratic +/// @param points the PointDataGrid whose points will be sampled on to +/// @param sourceGrid VDB grid which will be sampled +/// @param targetAttribute a target attribute on the points which will hold samples. This +/// attribute will be created with the source grid type if it does +/// not exist, and with the source grid name if the name is empty +/// @param filter an optional index filter +/// @param sampler handles sampling and conversion into the target attribute type, +/// which by default this uses the SampleWithRounding struct. +/// @param interrupter an optional interrupter +/// @param threaded enable or disable threading (threading is enabled by default) +/// @note The target attribute may exist provided it can be cast to the SourceGridT ValueType +template +inline void sampleGrid( size_t order, + PointDataGridT& points, + const SourceGridT& sourceGrid, + const Name& targetAttribute, + const FilterT& filter = NullFilter(), + const SamplerT& sampler = SampleWithRounding(), + InterrupterT* const interrupter = nullptr, + const bool threaded = true); + + +/////////////////////////////////////////////////// + + +namespace point_sample_internal { + + +template +struct CompatibleTypes { enum { value = std::is_constructible::value }; }; + +// Specializations for types that can be converted from source grid to target attribute +template struct CompatibleTypes< + T, T> { enum { value = true }; }; +template struct CompatibleTypes< + T, math::Vec2> { enum { value = true }; }; +template struct CompatibleTypes< + T, math::Vec3> { enum { value = true }; }; +template struct CompatibleTypes< + T, math::Vec4> { enum { value = true }; }; +template struct CompatibleTypes< + math::Vec2, math::Vec2> { enum { value = true }; }; +template struct CompatibleTypes< + math::Vec3, math::Vec3> { enum { value = true }; }; +template struct CompatibleTypes< + math::Vec4, math::Vec4> { enum { value = true }; }; +template struct CompatibleTypes< + math::Vec2, math::Vec2> { enum { value = CompatibleTypes::value }; }; +template struct CompatibleTypes< + math::Vec3, math::Vec3> { enum { value = CompatibleTypes::value }; }; +template struct CompatibleTypes< + math::Vec4, math::Vec4> { enum { value = CompatibleTypes::value }; }; +template struct CompatibleTypes< + ValueMask, T> { enum { value = CompatibleTypes::value }; }; + + +// Ability to access the Staggered template parameter from tools::Sampler +template struct SamplerTraits { + static const bool Staggered = false; +}; +template struct SamplerTraits> { + static const bool Staggered = T1; +}; + + +// default sampling is incompatible, so throw an error +template +struct SampleWithRoundingOp +{ + static inline void sample(ValueT&, const AccessorT&, const Vec3d&) + { + std::ostringstream ostr; + ostr << "Cannot sample a " << typeNameAsString() + << " grid on to a " << typeNameAsString() << " attribute"; + OPENVDB_THROW(TypeError, ostr.str()); + } +}; +// partial specialization to handle sampling and rounding of compatible conversion +template +struct SampleWithRoundingOp +{ + static inline void sample(ValueT& value, const AccessorT& accessor, const Vec3d& position) + { + value = ValueT(math::Round(SamplerT::sample(accessor, position))); + } +}; +// partial specialization to handle sampling and simple casting of compatible conversion +template +struct SampleWithRoundingOp +{ + static inline void sample(ValueT& value, const AccessorT& accessor, const Vec3d& position) + { + value = ValueT(SamplerT::sample(accessor, position)); + } +}; + + +template +class PointDataSampler +{ +public: + PointDataSampler(size_t order, + PointDataGridT& points, + const SamplerT& sampler, + const FilterT& filter, + InterrupterT* const interrupter, + const bool threaded) + : mOrder(order) + , mPoints(points) + , mSampler(sampler) + , mFilter(filter) + , mInterrupter(interrupter) + , mThreaded(threaded) { } + +private: + // No-op transformation + struct AlignedTransform + { + inline Vec3d transform(const Vec3d& position) const { return position; } + }; // struct AlignedTransform + + // Re-sample world-space position from source to target transforms + struct NonAlignedTransform + { + NonAlignedTransform(const math::Transform& source, const math::Transform& target) + : mSource(source) + , mTarget(target) { } + + inline Vec3d transform(const Vec3d& position) const + { + return mSource.worldToIndex(mTarget.indexToWorld(position)); + } + + private: + const math::Transform& mSource; + const math::Transform& mTarget; + }; // struct NonAlignedTransform + + // A simple convenience wrapper that contains the source grid accessor and the sampler + template + struct SamplerWrapper + { + using ValueType = ValueT; + using SourceAccessorT = typename SourceGridT::ConstAccessor; + + SamplerWrapper(const SourceGridT& sourceGrid, const SamplerT& sampler) + : mAccessor(sourceGrid.getConstAccessor()) + , mSampler(sampler) { } + + // note that creating a new accessor from the underlying tree is faster than + // copying an existing accessor + SamplerWrapper(const SamplerWrapper& other) + : mAccessor(other.mAccessor.tree()) + , mSampler(other.mSampler) { } + + inline ValueT sample(const Vec3d& position) const { + return mSampler.template sample( + mAccessor, position); + } + + private: + SourceAccessorT mAccessor; + const SamplerT& mSampler; + }; // struct SamplerWrapper + + template + inline void doSample(const SamplerWrapperT& sampleWrapper, const Index targetIndex, + const TransformerT& transformer) + { + using PointDataTreeT = typename PointDataGridT::TreeType; + using LeafT = typename PointDataTreeT::LeafNodeType; + using LeafManagerT = typename tree::LeafManager; + + const auto& filter(mFilter); + const auto& interrupter(mInterrupter); + + auto sampleLambda = [targetIndex, &sampleWrapper, &transformer, &filter, &interrupter]( + LeafT& leaf, size_t /*idx*/) + { + using TargetHandleT = AttributeWriteHandle; + + if (util::wasInterrupted(interrupter)) { + tbb::task::self().cancel_group_execution(); + return; + } + + SamplerWrapperT newSampleWrapper(sampleWrapper); + auto positionHandle = AttributeHandle::create(leaf.constAttributeArray("P")); + auto targetHandle = TargetHandleT::create(leaf.attributeArray(targetIndex)); + for (auto iter = leaf.beginIndexOn(filter); iter; ++iter) { + const Vec3d position = transformer.transform( + positionHandle->get(*iter) + iter.getCoord().asVec3d()); + targetHandle->set(*iter, newSampleWrapper.sample(position)); + } + }; + + LeafManagerT leafManager(mPoints.tree()); + + if (mInterrupter) mInterrupter->start(); + + leafManager.foreach(sampleLambda, mThreaded); + + if (mInterrupter) mInterrupter->end(); + } + + template + inline void resolveTransform(const SourceGridT& sourceGrid, const SamplerWrapperT& sampleWrapper, + const Index targetIndex) + { + const auto& sourceTransform = sourceGrid.constTransform(); + const auto& pointsTransform = mPoints.constTransform(); + + if (sourceTransform == pointsTransform) { + AlignedTransform transformer; + doSample(sampleWrapper, targetIndex, transformer); + } else { + NonAlignedTransform transformer(sourceTransform, pointsTransform); + doSample(sampleWrapper, targetIndex, transformer); + } + } + + template + inline void resolveStaggered(const SourceGridT& sourceGrid, const Index targetIndex) + { + using SamplerWrapperT = SamplerWrapper>; + using StaggeredSamplerWrapperT = SamplerWrapper>; + + using SourceValueType = typename SourceGridT::ValueType; + if (VecTraits::Size == 3 && sourceGrid.getGridClass() == GRID_STAGGERED) { + StaggeredSamplerWrapperT sampleWrapper(sourceGrid, mSampler); + resolveTransform(sourceGrid, sampleWrapper, targetIndex); + } else { + SamplerWrapperT sampleWrapper(sourceGrid, mSampler); + resolveTransform(sourceGrid, sampleWrapper, targetIndex); + } + } + +public: + template + inline void sample(const SourceGridT& sourceGrid, Index targetIndex) + { + if (mOrder == 0) { + resolveStaggered(sourceGrid, targetIndex); + } else if (mOrder == 1) { + resolveStaggered(sourceGrid, targetIndex); + } else if (mOrder == 2) { + resolveStaggered(sourceGrid, targetIndex); + } + } + +private: + size_t mOrder; + PointDataGridT& mPoints; + const SamplerT& mSampler; + const FilterT& mFilter; + InterrupterT* const mInterrupter; + const bool mThreaded; +}; // class PointDataSampler + + +template +struct AppendAttributeOp +{ + static void append(PointDataGridT& points, const Name& attribute) + { + appendAttribute(points.tree(), attribute); + } +}; +// partial specialization to disable attempts to append attribute type of DummySampleType +template +struct AppendAttributeOp +{ + static void append(PointDataGridT&, const Name&) { } +}; + +} // namespace point_sample_internal + + +//////////////////////////////////////// + + +template +ValueT SampleWithRounding::sample(const AccessorT& accessor, const Vec3d& position) const +{ + using namespace point_sample_internal; + using SourceValueT = typename AccessorT::ValueType; + static const bool staggered = SamplerTraits::Staggered; + static const bool compatible = CompatibleTypes::value && + (!staggered || (staggered && VecTraits::Size == 3)); + static const bool round = std::is_floating_point::value && + std::is_integral::value; + ValueT value; + SampleWithRoundingOp::sample( + value, accessor, position); + return value; +} + + +//////////////////////////////////////// + + +template +inline void sampleGrid( size_t order, + PointDataGridT& points, + const SourceGridT& sourceGrid, + const Name& targetAttribute, + const FilterT& filter, + const SamplerT& sampler, + InterrupterT* const interrupter, + const bool threaded) +{ + using point_sample_internal::AppendAttributeOp; + using point_sample_internal::PointDataSampler; + + // use the name of the grid if no target attribute name supplied + Name attribute(targetAttribute); + if (targetAttribute.empty()) { + attribute = sourceGrid.getName(); + } + + // we do not allow sampling onto the "P" attribute + if (attribute == "P") { + OPENVDB_THROW(RuntimeError, "Cannot sample onto the \"P\" attribute"); + } + + auto leaf = points.tree().cbeginLeaf(); + if (!leaf) return; + + PointDataSampler pointDataSampler( + order, points, sampler, filter, interrupter, threaded); + + const auto& descriptor = leaf->attributeSet().descriptor(); + size_t targetIndex = descriptor.find(attribute); + const bool attributeExists = targetIndex != AttributeSet::INVALID_POS; + + if (std::is_same::value) { + if (!attributeExists) { + // append attribute of source grid value type + appendAttribute(points.tree(), attribute); + targetIndex = leaf->attributeSet().descriptor().find(attribute); + assert(targetIndex != AttributeSet::INVALID_POS); + + // sample using same type as source grid + pointDataSampler.template sample(sourceGrid, Index(targetIndex)); + } else { + auto targetIdx = static_cast(targetIndex); + // attempt to explicitly sample using type of existing attribute + const Name& targetType = descriptor.valueType(targetIndex); + if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else if (targetType == typeNameAsString()) { + pointDataSampler.template sample(sourceGrid, targetIdx); + } else { + std::ostringstream ostr; + ostr << "Cannot sample attribute of type - " << targetType; + OPENVDB_THROW(TypeError, ostr.str()); + } + } + } else { + if (!attributeExists) { + // append attribute of target value type + // (point_sample_internal wrapper disables the ability to use DummySampleType) + AppendAttributeOp::append(points, attribute); + targetIndex = leaf->attributeSet().descriptor().find(attribute); + assert(targetIndex != AttributeSet::INVALID_POS); + } + else { + const Name targetType = typeNameAsString(); + const Name attributeType = descriptor.valueType(targetIndex); + if (targetType != attributeType) { + std::ostringstream ostr; + ostr << "Requested attribute type " << targetType << " for sampling " + << " does not match existing attribute type " << attributeType; + OPENVDB_THROW(TypeError, ostr.str()); + } + } + + // sample using target value type + pointDataSampler.template sample( + sourceGrid, static_cast(targetIndex)); + } +} + +template +inline void pointSample(PointDataGridT& points, + const SourceGridT& sourceGrid, + const Name& targetAttribute, + const FilterT& filter, + InterrupterT* const interrupter) +{ + SampleWithRounding sampler; + sampleGrid(/*order=*/0, points, sourceGrid, targetAttribute, filter, sampler, interrupter); +} + +template +inline void boxSample( PointDataGridT& points, + const SourceGridT& sourceGrid, + const Name& targetAttribute, + const FilterT& filter, + InterrupterT* const interrupter) +{ + SampleWithRounding sampler; + sampleGrid(/*order=*/1, points, sourceGrid, targetAttribute, filter, sampler, interrupter); +} + +template +inline void quadraticSample(PointDataGridT& points, + const SourceGridT& sourceGrid, + const Name& targetAttribute, + const FilterT& filter, + InterrupterT* const interrupter) +{ + SampleWithRounding sampler; + sampleGrid(/*order=*/2, points, sourceGrid, targetAttribute, filter, sampler, interrupter); +} + + +//////////////////////////////////////// + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_POINT_SAMPLE_HAS_BEEN_INCLUDED diff --git a/openvdb/points/PointScatter.h b/openvdb/points/PointScatter.h new file mode 100644 index 00000000..339c9c2d --- /dev/null +++ b/openvdb/points/PointScatter.h @@ -0,0 +1,554 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Nick Avramoussis +/// +/// @file points/PointScatter.h +/// +/// @brief Various point scattering methods for generating VDB Points. +/// +/// All random number calls are made to the same generator to produce +/// temporarily consistent results in relation to the provided seed. This +/// comes with some multi-threaded performance trade-offs. + +#ifndef OPENVDB_POINTS_POINT_SCATTER_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_POINT_SCATTER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "AttributeArray.h" +#include "PointCount.h" +#include "PointDataGrid.h" + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + +/// @brief The free functions depend on the following class: +/// +/// The @c InterrupterT template argument below refers to any class +/// with the following interface: +/// @code +/// class Interrupter { +/// ... +/// public: +/// void start(const char* name = nullptr) // called when computations begin +/// void end() // called when computations end +/// bool wasInterrupted(int percent=-1) // return true to break computation +///}; +/// @endcode +/// +/// @note If no template argument is provided for this InterrupterT +/// the util::NullInterrupter is used which implies that all +/// interrupter calls are no-ops (i.e. incurs no computational overhead). + + +/// @brief Uniformly scatter a total amount of points in active regions +/// +/// @param grid A source grid. The resulting PointDataGrid will copy this grids +/// transform and scatter in its active voxelized topology. +/// @param count The total number of points to scatter +/// @param seed A seed for the RandGenT +/// @param spread The spread of points as a scale from each voxels center. A value of +/// 1.0f indicates points can be placed anywhere within the voxel, where +/// as a value of 0.0f will force all points to be created exactly at the +/// centers of each voxel. +/// @param interrupter An optional interrupter +/// @note returns the scattered PointDataGrid +template< + typename GridT, + typename RandGenT = std::mt19937, + typename PositionArrayT = TypedAttributeArray, + typename PointDataGridT = Grid< + typename points::TreeConverter::Type>, + typename InterrupterT = util::NullInterrupter> +inline typename PointDataGridT::Ptr +uniformPointScatter(const GridT& grid, + const Index64 count, + const unsigned int seed = 0, + const float spread = 1.0f, + InterrupterT* interrupter = nullptr); + +/// @brief Uniformly scatter a fixed number of points per active voxel. If the pointsPerVoxel +/// value provided is a fractional value, each voxel calculates a delta value of +/// how likely it is to contain an extra point. +/// +/// @param grid A source grid. The resulting PointDataGrid will copy this grids +/// transform and scatter in its active voxelized topology. +/// @param pointsPerVoxel The number of points to scatter per voxel +/// @param seed A seed for the RandGenT +/// @param spread The spread of points as a scale from each voxels center. A value of +/// 1.0f indicates points can be placed anywhere within the voxel, where +/// as a value of 0.0f will force all points to be created exactly at the +/// centers of each voxel. +/// @param interrupter An optional interrupter +/// @note returns the scattered PointDataGrid + +template< + typename GridT, + typename RandGenT = std::mt19937, + typename PositionArrayT = TypedAttributeArray, + typename PointDataGridT = Grid< + typename points::TreeConverter::Type>, + typename InterrupterT = util::NullInterrupter> +inline typename PointDataGridT::Ptr +denseUniformPointScatter(const GridT& grid, + const float pointsPerVoxel, + const unsigned int seed = 0, + const float spread = 1.0f, + InterrupterT* interrupter = nullptr); + +/// @brief Non uniformly scatter points per active voxel. The pointsPerVoxel value is used +/// to weight each grids cell value to compute a fixed number of points for every +/// active voxel. If the computed result is a fractional value, each voxel calculates +/// a delta value of how likely it is to contain an extra point. +/// +/// @param grid A source grid. The resulting PointDataGrid will copy this grids +/// transform, voxelized topology and use its values to compute a +/// target points per voxel. The grids ValueType must be convertible +/// to a scalar value. Only active and larger than zero values will +/// contain points. +/// @param pointsPerVoxel The number of points to scatter per voxel +/// @param seed A seed for the RandGenT +/// @param spread The spread of points as a scale from each voxels center. A value of +/// 1.0f indicates points can be placed anywhere within the voxel, where +/// as a value of 0.0f will force all points to be created exactly at the +/// centers of each voxel. +/// @param interrupter An optional interrupter +/// @note returns the scattered PointDataGrid +template< + typename GridT, + typename RandGenT = std::mt19937, + typename PositionArrayT = TypedAttributeArray, + typename PointDataGridT = Grid< + typename points::TreeConverter::Type>, + typename InterrupterT = util::NullInterrupter> +inline typename PointDataGridT::Ptr +nonUniformPointScatter(const GridT& grid, + const float pointsPerVoxel, + const unsigned int seed = 0, + const float spread = 1.0f, + InterrupterT* interrupter = nullptr); + + +//////////////////////////////////////// + + +namespace point_scatter_internal +{ + +/// @brief initialise the topology of a PointDataGrid and ensure +/// everything is voxelized +/// @param grid The source grid from which to base the topology generation +template +inline typename PointDataGridT::Ptr +initialisePointTopology(const GridT& grid) +{ + typename PointDataGridT::Ptr points(new PointDataGridT); + points->setTransform(grid.transform().copy()); + points->topologyUnion(grid); + if (points->tree().hasActiveTiles()) { + points->tree().voxelizeActiveTiles(); + } + + return points; +} + +/// @brief Generate random point positions for a leaf node +/// @param leaf The leaf node to initialize +/// @param descriptor The descriptor containing the position type +/// @param count The number of points to generate +/// @param spread The spread of points from the voxel center +/// @param rand01 The random number generator, expected to produce floating point +/// values between 0 and 1. +template +inline void +generatePositions(LeafNodeT& leaf, + const AttributeSet::Descriptor::Ptr& descriptor, + const Index64& count, + const float spread, + RandGenT& rand01) +{ + using PositionTraits = VecTraits; + using ValueType = typename PositionTraits::ElementType; + using PositionWriteHandle = AttributeWriteHandle; + + leaf.initializeAttributes(descriptor, static_cast(count)); + + // directly expand to avoid needlessly setting uniform values in the + // write handle + auto& array = leaf.attributeArray(0); + array.expand(/*fill*/false); + + PositionWriteHandle pHandle(array, /*expand*/false); + PositionType P; + for (Index64 index = 0; index < count; ++index) { + P[0] = (spread * (rand01() - ValueType(0.5))); + P[1] = (spread * (rand01() - ValueType(0.5))); + P[2] = (spread * (rand01() - ValueType(0.5))); + pHandle.set(static_cast(index), P); + } +} + +} // namespace point_scatter_internal + + +//////////////////////////////////////// + + +template< + typename GridT, + typename RandGenT, + typename PositionArrayT, + typename PointDataGridT, + typename InterrupterT> +inline typename PointDataGridT::Ptr +uniformPointScatter(const GridT& grid, + const Index64 count, + const unsigned int seed, + const float spread, + InterrupterT* interrupter) +{ + using PositionType = typename PositionArrayT::ValueType; + using PositionTraits = VecTraits; + using ValueType = typename PositionTraits::ElementType; + using CodecType = typename PositionArrayT::Codec; + + using RandomGenerator = math::Rand01; + + using TreeType = typename PointDataGridT::TreeType; + using LeafNodeType = typename TreeType::LeafNodeType; + using LeafManagerT = tree::LeafManager; + + struct Local + { + /// @brief Get the prefixed voxel counts for each leaf node with an + /// additional value to represent the end voxel count. + /// See also LeafManager::getPrefixSum() + static void getPrefixSum(LeafManagerT& leafManager, + std::vector& offsets) + { + Index64 offset = 0; + offsets.reserve(leafManager.leafCount() + 1); + offsets.push_back(0); + const auto leafRange = leafManager.leafRange(); + for (auto leaf = leafRange.begin(); leaf; ++leaf) { + offset += leaf->onVoxelCount(); + offsets.push_back(offset); + } + } + }; + + static_assert(PositionTraits::IsVec && PositionTraits::Size == 3, + "Invalid Position Array type."); + + if (spread < 0.0f || spread > 1.0f) { + OPENVDB_THROW(ValueError, "Spread must be between 0 and 1."); + } + + if (interrupter) interrupter->start("Uniform scattering with fixed point count"); + + typename PointDataGridT::Ptr points = + point_scatter_internal::initialisePointTopology(grid); + TreeType& tree = points->tree(); + if (!tree.cbeginLeaf()) return points; + + LeafManagerT leafManager(tree); + const Index64 voxelCount = leafManager.activeLeafVoxelCount(); + assert(voxelCount != 0); + + const double pointsPerVolume = double(count) / double(voxelCount); + const Index32 pointsPerVoxel = static_cast(math::RoundDown(pointsPerVolume)); + const Index64 remainder = count - (pointsPerVoxel * voxelCount); + + if (remainder == 0) { + return denseUniformPointScatter< + GridT, RandGenT, PositionArrayT, PointDataGridT, InterrupterT>( + grid, float(pointsPerVoxel), seed, spread, interrupter); + } + + std::vector voxelOffsets, values; + std::thread worker(&Local::getPrefixSum, std::ref(leafManager), std::ref(voxelOffsets)); + + { + math::RandInt gen(seed, 0, voxelCount-1); + values.reserve(remainder); + for (Index64 i = 0; i < remainder; ++i) values.emplace_back(gen()); + } + + worker.join(); + + if (util::wasInterrupted(interrupter)) { + tree.clear(); + return points; + } + + tbb::parallel_sort(values.begin(), values.end()); + const bool fractionalOnly(pointsPerVoxel == 0); + + leafManager.foreach([&voxelOffsets, &values, fractionalOnly] + (LeafNodeType& leaf, const size_t idx) + { + const Index64 lowerOffset = voxelOffsets[idx]; // inclusive + const Index64 upperOffset = voxelOffsets[idx + 1]; // exclusive + assert(upperOffset > lowerOffset); + + const auto valuesEnd = values.end(); + auto lower = std::lower_bound(values.begin(), valuesEnd, lowerOffset); + + auto* const data = leaf.buffer().data(); + auto iter = leaf.beginValueOn(); + + Index32 currentOffset(0); + bool addedPoints(!fractionalOnly); + while (lower != valuesEnd) { + const Index64 vId = *lower; + if (vId >= upperOffset) break; + + const Index32 nextOffset = Index32(vId - lowerOffset); + iter.increment(nextOffset - currentOffset); + currentOffset = nextOffset; + assert(iter); + + auto& value = data[iter.pos()]; + value = value + 1; // no += operator support + addedPoints = true; + ++lower; + } + + // deactivate this leaf if no points were added. This will speed up + // the unthreaded rng + if (!addedPoints) leaf.setValuesOff(); + }); + + voxelOffsets.clear(); + values.clear(); + + if (fractionalOnly) { + tools::pruneInactive(tree); + leafManager.rebuild(); + } + + const AttributeSet::Descriptor::Ptr descriptor = + AttributeSet::Descriptor::create(PositionArrayT::attributeType()); + RandomGenerator rand01(seed); + + const auto leafRange = leafManager.leafRange(); + auto leaf = leafRange.begin(); + for (; leaf; ++leaf) { + if (util::wasInterrupted(interrupter)) break; + Index32 offset(0); + for (auto iter = leaf->beginValueAll(); iter; ++iter) { + if (iter.isValueOn()) { + const Index32 value = Index32(pointsPerVolume + Index32(*iter)); + if (value == 0) leaf->setValueOff(iter.pos()); + else offset += value; + } + // @note can't use iter.setValue(offset) on point grids + leaf->setOffsetOnly(iter.pos(), offset); + } + + // offset should always be non zero + assert(offset != 0); + point_scatter_internal::generatePositions + (*leaf, descriptor, offset, spread, rand01); + } + + // if interrupted, remove remaining leaf nodes + if (leaf) { + for (; leaf; ++leaf) leaf->setValuesOff(); + tools::pruneInactive(tree); + } + + if (interrupter) interrupter->end(); + return points; +} + + +//////////////////////////////////////// + + +template< + typename GridT, + typename RandGenT, + typename PositionArrayT, + typename PointDataGridT, + typename InterrupterT> +inline typename PointDataGridT::Ptr +denseUniformPointScatter(const GridT& grid, + const float pointsPerVoxel, + const unsigned int seed, + const float spread, + InterrupterT* interrupter) +{ + using PositionType = typename PositionArrayT::ValueType; + using PositionTraits = VecTraits; + using ValueType = typename PositionTraits::ElementType; + using CodecType = typename PositionArrayT::Codec; + + using RandomGenerator = math::Rand01; + + using TreeType = typename PointDataGridT::TreeType; + + static_assert(PositionTraits::IsVec && PositionTraits::Size == 3, + "Invalid Position Array type."); + + if (pointsPerVoxel < 0.0f) { + OPENVDB_THROW(ValueError, "Points per voxel must not be less than zero."); + } + + if (spread < 0.0f || spread > 1.0f) { + OPENVDB_THROW(ValueError, "Spread must be between 0 and 1."); + } + + if (interrupter) interrupter->start("Dense uniform scattering with fixed point count"); + + typename PointDataGridT::Ptr points = + point_scatter_internal::initialisePointTopology(grid); + TreeType& tree = points->tree(); + auto leafIter = tree.beginLeaf(); + if (!leafIter) return points; + + const Index32 pointsPerVoxelInt = math::Floor(pointsPerVoxel); + const double delta = pointsPerVoxel - float(pointsPerVoxelInt); + const bool fractional = !math::isApproxZero(delta, 1.0e-6); + const bool fractionalOnly = pointsPerVoxelInt == 0; + + const AttributeSet::Descriptor::Ptr descriptor = + AttributeSet::Descriptor::create(PositionArrayT::attributeType()); + RandomGenerator rand01(seed); + + for (; leafIter; ++leafIter) { + if (util::wasInterrupted(interrupter)) break; + Index32 offset(0); + for (auto iter = leafIter->beginValueAll(); iter; ++iter) { + if (iter.isValueOn()) { + offset += pointsPerVoxelInt; + if (fractional && rand01() < delta) ++offset; + else if (fractionalOnly) leafIter->setValueOff(iter.pos()); + } + // @note can't use iter.setValue(offset) on point grids + leafIter->setOffsetOnly(iter.pos(), offset); + } + + if (offset != 0) { + point_scatter_internal::generatePositions + (*leafIter, descriptor, offset, spread, rand01); + } + } + + // if interrupted, remove remaining leaf nodes + const bool prune(leafIter || fractionalOnly); + for (; leafIter; ++leafIter) leafIter->setValuesOff(); + + if (prune) tools::pruneInactive(tree); + if (interrupter) interrupter->end(); + return points; +} + + +//////////////////////////////////////// + + +template< + typename GridT, + typename RandGenT, + typename PositionArrayT, + typename PointDataGridT, + typename InterrupterT> +inline typename PointDataGridT::Ptr +nonUniformPointScatter(const GridT& grid, + const float pointsPerVoxel, + const unsigned int seed, + const float spread, + InterrupterT* interrupter) +{ + using PositionType = typename PositionArrayT::ValueType; + using PositionTraits = VecTraits; + using ValueType = typename PositionTraits::ElementType; + using CodecType = typename PositionArrayT::Codec; + + using RandomGenerator = math::Rand01; + + using TreeType = typename PointDataGridT::TreeType; + + static_assert(PositionTraits::IsVec && PositionTraits::Size == 3, + "Invalid Position Array type."); + static_assert(std::is_arithmetic::value, + "Scalar grid type required for weighted voxel scattering."); + + if (pointsPerVoxel < 0.0f) { + OPENVDB_THROW(ValueError, "Points per voxel must not be less than zero."); + } + + if (spread < 0.0f || spread > 1.0f) { + OPENVDB_THROW(ValueError, "Spread must be between 0 and 1."); + } + + if (interrupter) interrupter->start("Non-uniform scattering with local point density"); + + typename PointDataGridT::Ptr points = + point_scatter_internal::initialisePointTopology(grid); + TreeType& tree = points->tree(); + auto leafIter = tree.beginLeaf(); + if (!leafIter) return points; + + const AttributeSet::Descriptor::Ptr descriptor = + AttributeSet::Descriptor::create(PositionArrayT::attributeType()); + RandomGenerator rand01(seed); + const auto accessor = grid.getConstAccessor(); + + for (; leafIter; ++leafIter) { + if (util::wasInterrupted(interrupter)) break; + Index32 offset(0); + for (auto iter = leafIter->beginValueAll(); iter; ++iter) { + if (iter.isValueOn()) { + double fractional = + double(accessor.getValue(iter.getCoord())) * pointsPerVoxel; + fractional = std::max(0.0, fractional); + int count = int(fractional); + if (rand01() < (fractional - double(count))) ++count; + else if (count == 0) leafIter->setValueOff(iter.pos()); + offset += count; + } + // @note can't use iter.setValue(offset) on point grids + leafIter->setOffsetOnly(iter.pos(), offset); + } + + if (offset != 0) { + point_scatter_internal::generatePositions + (*leafIter, descriptor, offset, spread, rand01); + } + } + + // if interrupted, remove remaining leaf nodes + for (; leafIter; ++leafIter) leafIter->setValuesOff(); + + tools::pruneInactive(points->tree()); + if (interrupter) interrupter->end(); + return points; +} + + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_POINTS_POINT_SCATTER_HAS_BEEN_INCLUDED diff --git a/openvdb/points/StreamCompression.cc b/openvdb/points/StreamCompression.cc new file mode 100644 index 00000000..a49bfbd3 --- /dev/null +++ b/openvdb/points/StreamCompression.cc @@ -0,0 +1,633 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/StreamCompression.cc + +#include "StreamCompression.h" +#include +#include +#ifdef OPENVDB_USE_BLOSC +#include +#endif + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace compression { + + +#ifdef OPENVDB_USE_BLOSC + + +bool +bloscCanCompress() +{ + return true; +} + + +size_t +bloscUncompressedSize(const char* buffer) +{ + size_t bytes, _1, _2; + blosc_cbuffer_sizes(buffer, &bytes, &_1, &_2); + return bytes; +} + + +void +bloscCompress(char* compressedBuffer, size_t& compressedBytes, const size_t bufferBytes, + const char* uncompressedBuffer, const size_t uncompressedBytes) +{ + if (bufferBytes > BLOSC_MAX_BUFFERSIZE) { + OPENVDB_LOG_DEBUG("Blosc compress failed due to exceeding maximum buffer size."); + compressedBytes = 0; + compressedBuffer = nullptr; + return; + } + if (bufferBytes < uncompressedBytes + BLOSC_MAX_OVERHEAD) { + OPENVDB_LOG_DEBUG("Blosc compress failed due to insufficient space in compressed buffer."); + compressedBytes = 0; + compressedBuffer = nullptr; + return; + } + + if (uncompressedBytes <= BLOSC_MINIMUM_BYTES) { + // no Blosc compression performed below this limit + compressedBytes = 0; + compressedBuffer = nullptr; + return; + } + + if (uncompressedBytes < BLOSC_PAD_BYTES && bufferBytes < BLOSC_PAD_BYTES + BLOSC_MAX_OVERHEAD) { + OPENVDB_LOG_DEBUG( + "Blosc compress failed due to insufficient space in compressed buffer for padding."); + compressedBytes = 0; + compressedBuffer = nullptr; + return; + } + + size_t inputBytes = uncompressedBytes; + + const char* buffer = uncompressedBuffer; + + std::unique_ptr paddedBuffer; + if (uncompressedBytes < BLOSC_PAD_BYTES) { + // input array padded with zeros below this limit to improve compression + paddedBuffer.reset(new char[BLOSC_PAD_BYTES]); + std::memcpy(paddedBuffer.get(), buffer, uncompressedBytes); + for (int i = static_cast(uncompressedBytes); i < BLOSC_PAD_BYTES; i++) { + paddedBuffer.get()[i] = 0; + } + buffer = paddedBuffer.get(); + inputBytes = BLOSC_PAD_BYTES; + } + + int _compressedBytes = blosc_compress_ctx( + /*clevel=*/9, // 0 (no compression) to 9 (maximum compression) + /*doshuffle=*/true, + /*typesize=*/sizeof(float), // hard-coded to 4-bytes for better compression + /*srcsize=*/inputBytes, + /*src=*/buffer, + /*dest=*/compressedBuffer, + /*destsize=*/bufferBytes, + BLOSC_LZ4_COMPNAME, + /*blocksize=*/inputBytes, + /*numthreads=*/1); + + if (_compressedBytes <= 0) { + std::ostringstream ostr; + ostr << "Blosc failed to compress " << uncompressedBytes << " byte" + << (uncompressedBytes == 1 ? "" : "s"); + if (_compressedBytes < 0) ostr << " (internal error " << _compressedBytes << ")"; + OPENVDB_LOG_DEBUG(ostr.str()); + compressedBytes = 0; + return; + } + + compressedBytes = _compressedBytes; + + // fail if compression does not result in a smaller buffer + if (compressedBytes >= uncompressedBytes) { + compressedBytes = 0; + } +} + + +std::unique_ptr +bloscCompress(const char* buffer, const size_t uncompressedBytes, size_t& compressedBytes, + const bool resize) +{ + size_t tempBytes = uncompressedBytes; + // increase temporary buffer for padding if necessary + if (tempBytes >= BLOSC_MINIMUM_BYTES && tempBytes < BLOSC_PAD_BYTES) { + tempBytes += BLOSC_PAD_BYTES; + } + // increase by Blosc max overhead + tempBytes += BLOSC_MAX_OVERHEAD; + const bool outOfRange = tempBytes > BLOSC_MAX_BUFFERSIZE; + std::unique_ptr outBuffer(outOfRange ? new char[1] : new char[tempBytes]); + + bloscCompress(outBuffer.get(), compressedBytes, tempBytes, buffer, uncompressedBytes); + + if (compressedBytes == 0) { + return nullptr; + } + + // buffer size is larger due to Blosc overhead so resize + // (resize can be skipped if the buffer is only temporary) + + if (resize) { + std::unique_ptr newBuffer(new char[compressedBytes]); + std::memcpy(newBuffer.get(), outBuffer.get(), compressedBytes); + outBuffer.reset(newBuffer.release()); + } + + return outBuffer; +} + + +size_t +bloscCompressedSize( const char* buffer, const size_t uncompressedBytes) +{ + size_t compressedBytes; + bloscCompress(buffer, uncompressedBytes, compressedBytes, /*resize=*/false); + return compressedBytes; +} + + +void +bloscDecompress(char* uncompressedBuffer, const size_t expectedBytes, + const size_t bufferBytes, const char* compressedBuffer) +{ + size_t uncompressedBytes = bloscUncompressedSize(compressedBuffer); + + if (bufferBytes > BLOSC_MAX_BUFFERSIZE) { + OPENVDB_THROW(RuntimeError, + "Blosc decompress failed due to exceeding maximum buffer size."); + } + if (bufferBytes < uncompressedBytes + BLOSC_MAX_OVERHEAD) { + OPENVDB_THROW(RuntimeError, + "Blosc decompress failed due to insufficient space in uncompressed buffer."); + } + + uncompressedBytes = blosc_decompress_ctx( /*src=*/compressedBuffer, + /*dest=*/uncompressedBuffer, + bufferBytes, + /*numthreads=*/1); + + if (uncompressedBytes < 1) { + OPENVDB_THROW(RuntimeError, "Blosc decompress returned error code " << uncompressedBytes); + } + + if (uncompressedBytes == BLOSC_PAD_BYTES && expectedBytes <= BLOSC_PAD_BYTES) { + // padded array to improve compression + } + else if (uncompressedBytes != expectedBytes) { + OPENVDB_THROW(RuntimeError, "Expected to decompress " << expectedBytes + << " byte" << (expectedBytes == 1 ? "" : "s") << ", got " + << uncompressedBytes << " byte" << (uncompressedBytes == 1 ? "" : "s")); + } +} + + +std::unique_ptr +bloscDecompress(const char* buffer, const size_t expectedBytes, const bool resize) +{ + size_t uncompressedBytes = bloscUncompressedSize(buffer); + size_t tempBytes = uncompressedBytes + BLOSC_MAX_OVERHEAD; + const bool outOfRange = tempBytes > BLOSC_MAX_BUFFERSIZE; + if (outOfRange) tempBytes = 1; + std::unique_ptr outBuffer(new char[tempBytes]); + + bloscDecompress(outBuffer.get(), expectedBytes, tempBytes, buffer); + + // buffer size is larger due to Blosc overhead so resize + // (resize can be skipped if the buffer is only temporary) + + if (resize) { + std::unique_ptr newBuffer(new char[expectedBytes]); + std::memcpy(newBuffer.get(), outBuffer.get(), expectedBytes); + outBuffer.reset(newBuffer.release()); + } + + return outBuffer; +} + + +#else + + +bool +bloscCanCompress() +{ + OPENVDB_LOG_DEBUG("Can't compress array data without the blosc library."); + return false; +} + + +size_t +bloscUncompressedSize(const char*) +{ + OPENVDB_THROW(RuntimeError, "Can't extract compressed data without the blosc library."); +} + + +void +bloscCompress(char*, size_t& compressedBytes, const size_t, const char*, const size_t) +{ + OPENVDB_LOG_DEBUG("Can't compress array data without the blosc library."); + compressedBytes = 0; +} + + +std::unique_ptr +bloscCompress(const char*, const size_t, size_t& compressedBytes, const bool) +{ + OPENVDB_LOG_DEBUG("Can't compress array data without the blosc library."); + compressedBytes = 0; + return nullptr; +} + + +size_t +bloscCompressedSize(const char*, const size_t) +{ + OPENVDB_LOG_DEBUG("Can't compress array data without the blosc library."); + return 0; +} + + +void +bloscDecompress(char*, const size_t, const size_t, const char*) +{ + OPENVDB_THROW(RuntimeError, "Can't extract compressed data without the blosc library."); +} + + +std::unique_ptr +bloscDecompress(const char*, const size_t, const bool) +{ + OPENVDB_THROW(RuntimeError, "Can't extract compressed data without the blosc library."); +} + + +#endif // OPENVDB_USE_BLOSC + + +//////////////////////////////////////// + + +void +Page::load() const +{ + this->doLoad(); +} + + +long +Page::uncompressedBytes() const +{ + assert(mInfo); + return mInfo->uncompressedBytes; +} + + +const char* +Page::buffer(const int index) const +{ + if (this->isOutOfCore()) this->load(); + + return mData.get() + index; +} + + +void +Page::readHeader(std::istream& is) +{ + assert(mInfo); + + // read the (compressed) size of the page + int compressedSize; + is.read(reinterpret_cast(&compressedSize), sizeof(int)); + + int uncompressedSize; + // if uncompressed, read the (compressed) size of the page + if (compressedSize > 0) is.read(reinterpret_cast(&uncompressedSize), sizeof(int)); + else uncompressedSize = -compressedSize; + + assert(compressedSize != 0); + assert(uncompressedSize != 0); + + mInfo->compressedBytes = compressedSize; + mInfo->uncompressedBytes = uncompressedSize; +} + + +void +Page::readBuffers(std::istream&is, bool delayed) +{ + assert(mInfo); + + bool isCompressed = mInfo->compressedBytes > 0; + + io::MappedFile::Ptr mappedFile = io::getMappedFilePtr(is); + + if (delayed && mappedFile) { + SharedPtr meta = io::getStreamMetadataPtr(is); + assert(meta); + + std::streamoff filepos = is.tellg(); + + // seek over the page + is.seekg((isCompressed ? mInfo->compressedBytes : -mInfo->compressedBytes), + std::ios_base::cur); + + mInfo->mappedFile = mappedFile; + mInfo->meta = meta; + mInfo->filepos = filepos; + + assert(mInfo->mappedFile); + } + else { + std::unique_ptr buffer(new char[ + (isCompressed ? mInfo->compressedBytes : -mInfo->compressedBytes)]); + is.read(buffer.get(), (isCompressed ? mInfo->compressedBytes : -mInfo->compressedBytes)); + + if (mInfo->compressedBytes > 0) { + this->decompress(buffer); + } else { + this->copy(buffer, -static_cast(mInfo->compressedBytes)); + } + mInfo.reset(); + } +} + + +bool +Page::isOutOfCore() const +{ + return bool(mInfo); +} + + +void +Page::copy(const std::unique_ptr& temp, int pageSize) +{ + mData.reset(new char[pageSize]); + std::memcpy(mData.get(), temp.get(), pageSize); +} + + +void +Page::decompress(const std::unique_ptr& temp) +{ + size_t uncompressedBytes = bloscUncompressedSize(temp.get()); + size_t tempBytes = uncompressedBytes; +#ifdef OPENVDB_USE_BLOSC + tempBytes += uncompressedBytes; +#endif + mData.reset(new char[tempBytes]); + + bloscDecompress(mData.get(), uncompressedBytes, tempBytes, temp.get()); +} + + +void +Page::doLoad() const +{ + if (!this->isOutOfCore()) return; + + Page* self = const_cast(this); + + // This lock will be contended at most once, after which this buffer + // will no longer be out-of-core. + tbb::spin_mutex::scoped_lock lock(self->mMutex); + if (!this->isOutOfCore()) return; + + assert(self->mInfo); + + int compressedBytes = static_cast(self->mInfo->compressedBytes); + bool compressed = compressedBytes > 0; + if (!compressed) compressedBytes = -compressedBytes; + + assert(compressedBytes); + + std::unique_ptr temp(new char[compressedBytes]); + + assert(self->mInfo->mappedFile); + SharedPtr buf = self->mInfo->mappedFile->createBuffer(); + assert(buf); + + std::istream is(buf.get()); + io::setStreamMetadataPtr(is, self->mInfo->meta, /*transfer=*/true); + is.seekg(self->mInfo->filepos); + + is.read(temp.get(), compressedBytes); + + if (compressed) self->decompress(temp); + else self->copy(temp, compressedBytes); + + self->mInfo.reset(); +} + + +//////////////////////////////////////// + + +PageHandle::PageHandle( const Page::Ptr& page, const int index, const int size) + : mPage(page) + , mIndex(index) + , mSize(size) +{ +} + + +Page& +PageHandle::page() +{ + assert(mPage); + return *mPage; +} + + +std::unique_ptr +PageHandle::read() +{ + assert(mIndex >= 0); + assert(mSize > 0); + std::unique_ptr buffer(new char[mSize]); + std::memcpy(buffer.get(), mPage->buffer(mIndex), mSize); + return buffer; +} + + +//////////////////////////////////////// + + +PagedInputStream::PagedInputStream(std::istream& is) + : mIs(&is) +{ +} + + +PageHandle::Ptr +PagedInputStream::createHandle(std::streamsize n) +{ + assert(mByteIndex <= mUncompressedBytes); + + if (mByteIndex == mUncompressedBytes) { + + mPage = std::make_shared(); + mPage->readHeader(*mIs); + mUncompressedBytes = static_cast(mPage->uncompressedBytes()); + mByteIndex = 0; + } + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + // TODO: C++14 introduces std::make_unique + PageHandle::Ptr pageHandle(new PageHandle(mPage, mByteIndex, int(n))); +#else + PageHandle::Ptr pageHandle = std::make_shared(mPage, mByteIndex, int(n)); +#endif + + mByteIndex += int(n); + + return pageHandle; +} + + +void +PagedInputStream::read(PageHandle::Ptr& pageHandle, std::streamsize n, bool delayed) +{ + assert(mByteIndex <= mUncompressedBytes); + + Page& page = pageHandle->page(); + + if (mByteIndex == mUncompressedBytes) { + mUncompressedBytes = static_cast(page.uncompressedBytes()); + page.readBuffers(*mIs, delayed); + mByteIndex = 0; + } + + mByteIndex += int(n); +} + + +//////////////////////////////////////// + + +PagedOutputStream::PagedOutputStream() +{ +#ifdef OPENVDB_USE_BLOSC + mCompressedData.reset(new char[PageSize + BLOSC_MAX_OVERHEAD]); +#endif +} + + +PagedOutputStream::PagedOutputStream(std::ostream& os) + : mOs(&os) +{ +#ifdef OPENVDB_USE_BLOSC + mCompressedData.reset(new char[PageSize + BLOSC_MAX_OVERHEAD]); +#endif +} + + +PagedOutputStream& +PagedOutputStream::write(const char* str, std::streamsize n) +{ + if (n > PageSize) { + this->flush(); + // write out the block as if a whole page + this->compressAndWrite(str, size_t(n)); + } + else { + // if the size of this block will overflow the page, flush to disk + if ((int(n) + mBytes) > PageSize) { + this->flush(); + } + + // store and increment the data in the current page + std::memcpy(mData.get() + mBytes, str, n); + mBytes += int(n); + } + + return *this; +} + + +void +PagedOutputStream::flush() +{ + this->compressAndWrite(mData.get(), mBytes); + mBytes = 0; +} + + +void +PagedOutputStream::compressAndWrite(const char* buffer, size_t size) +{ + if (size == 0) return; + + assert(size < std::numeric_limits::max()); + + this->resize(size); + + size_t compressedBytes(0); + if (mSizeOnly) { +#ifdef OPENVDB_USE_BLOSC + compressedBytes = bloscCompressedSize(buffer, size); +#endif + } + else { +#ifdef OPENVDB_USE_BLOSC + bloscCompress(mCompressedData.get(), compressedBytes, mCapacity + BLOSC_MAX_OVERHEAD, buffer, size); +#endif + } + + if (compressedBytes == 0) { + int uncompressedBytes = -static_cast(size); + if (mSizeOnly) { + mOs->write(reinterpret_cast(&uncompressedBytes), sizeof(int)); + } + else { + mOs->write(buffer, size); + } + } + else { + if (mSizeOnly) { + mOs->write(reinterpret_cast(&compressedBytes), sizeof(int)); + mOs->write(reinterpret_cast(&size), sizeof(int)); + } + else { +#ifdef OPENVDB_USE_BLOSC + mOs->write(mCompressedData.get(), compressedBytes); +#else + OPENVDB_THROW(RuntimeError, "Cannot write out compressed data without Blosc."); +#endif + } + } +} + + +void +PagedOutputStream::resize(size_t size) +{ + // grow the capacity if not sufficient space + size_t requiredSize = size; + if (size < BLOSC_PAD_BYTES && size >= BLOSC_MINIMUM_BYTES) { + requiredSize = BLOSC_PAD_BYTES; + } + if (requiredSize > mCapacity) { + mCapacity = requiredSize; + mData.reset(new char[mCapacity]); +#ifdef OPENVDB_USE_BLOSC + mCompressedData.reset(new char[mCapacity + BLOSC_MAX_OVERHEAD]); +#endif + } +} + +} // namespace compression +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/points/StreamCompression.h b/openvdb/points/StreamCompression.h new file mode 100644 index 00000000..2dc5b46d --- /dev/null +++ b/openvdb/points/StreamCompression.h @@ -0,0 +1,289 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/StreamCompression.h +/// +/// @author Dan Bailey +/// +/// @brief Convenience wrappers to using Blosc and reading and writing of Paged data. +/// +/// Blosc is most effective with large (> ~256KB) blocks of data. Writing the entire +/// data block contiguously would provide the most optimal compression, however would +/// limit the ability to use delayed-loading as the whole block would be required to +/// be loaded from disk at once. To balance these two competing factors, Paging is used +/// to write out blocks of data that are a reasonable size for Blosc. These Pages are +/// loaded lazily, tracking the input stream pointers and creating Handles that reference +/// portions of the buffer. When the Page buffer is accessed, the data will be read from +/// the stream. + +#ifndef OPENVDB_TOOLS_STREAM_COMPRESSION_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_STREAM_COMPRESSION_HAS_BEEN_INCLUDED + +#include +#include +#include +#include + + +class TestStreamCompression; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace compression { + + +// This is the minimum number of bytes below which Blosc compression is not used to +// avoid unecessary computation, as Blosc offers minimal compression until this limit +static const int BLOSC_MINIMUM_BYTES = 48; + +// This is the minimum number of bytes below which the array is padded with zeros up +// to this number of bytes to allow Blosc to perform compression with small arrays +static const int BLOSC_PAD_BYTES = 128; + + +/// @brief Returns true if compression is available +OPENVDB_API bool bloscCanCompress(); + +/// @brief Retrieves the uncompressed size of buffer when uncompressed +/// +/// @param buffer the compressed buffer +OPENVDB_API size_t bloscUncompressedSize(const char* buffer); + +/// @brief Compress into the supplied buffer. +/// +/// @param compressedBuffer the buffer to compress +/// @param compressedBytes number of compressed bytes +/// @param bufferBytes the number of bytes in compressedBuffer available to be filled +/// @param uncompressedBuffer the uncompressed buffer to compress +/// @param uncompressedBytes number of uncompressed bytes +OPENVDB_API void bloscCompress(char* compressedBuffer, size_t& compressedBytes, + const size_t bufferBytes, const char* uncompressedBuffer, const size_t uncompressedBytes); + +/// @brief Compress and return the heap-allocated compressed buffer. +/// +/// @param buffer the buffer to compress +/// @param uncompressedBytes number of uncompressed bytes +/// @param compressedBytes number of compressed bytes (written to this variable) +/// @param resize the compressed buffer will be exactly resized to remove the +/// portion used for Blosc overhead, for efficiency this can be +/// skipped if it is known that the resulting buffer is temporary +OPENVDB_API std::unique_ptr bloscCompress(const char* buffer, + const size_t uncompressedBytes, size_t& compressedBytes, const bool resize = true); + +/// @brief Convenience wrapper to retrieve the compressed size of buffer when compressed +/// +/// @param buffer the uncompressed buffer +/// @param uncompressedBytes number of uncompressed bytes +OPENVDB_API size_t bloscCompressedSize(const char* buffer, const size_t uncompressedBytes); + +/// @brief Decompress into the supplied buffer. Will throw if decompression fails or +/// uncompressed buffer has insufficient space in which to decompress. +/// +/// @param uncompressedBuffer the uncompressed buffer to decompress into +/// @param expectedBytes the number of bytes expected once the buffer is decompressed +/// @param bufferBytes the number of bytes in uncompressedBuffer available to be filled +/// @param compressedBuffer the compressed buffer to decompress +OPENVDB_API void bloscDecompress(char* uncompressedBuffer, const size_t expectedBytes, + const size_t bufferBytes, const char* compressedBuffer); + +/// @brief Decompress and return the the heap-allocated uncompressed buffer. +/// +/// @param buffer the buffer to decompress +/// @param expectedBytes the number of bytes expected once the buffer is decompressed +/// @param resize the compressed buffer will be exactly resized to remove the +/// portion used for Blosc overhead, for efficiency this can be +/// skipped if it is known that the resulting buffer is temporary +OPENVDB_API std::unique_ptr bloscDecompress(const char* buffer, + const size_t expectedBytes, const bool resize = true); + + +//////////////////////////////////////// + + +// 1MB = 1048576 Bytes +static const int PageSize = 1024 * 1024; + + +/// @brief Stores a variable-size, compressed, delayed-load Page of data +/// that is loaded into memory when accessed. Access to the Page is +/// thread-safe as loading and decompressing the data is protected by a mutex. +class OPENVDB_API Page +{ +private: + struct Info + { + io::MappedFile::Ptr mappedFile; + SharedPtr meta; + std::streamoff filepos; + long compressedBytes; + long uncompressedBytes; + }; // Info + +public: + using Ptr = std::shared_ptr; + + Page() = default; + + /// @brief load the Page into memory + void load() const; + + /// @brief Uncompressed bytes of the Paged data, available + /// when the header has been read. + long uncompressedBytes() const; + + /// @brief Retrieves a data pointer at the specific @param index + /// @note Will force a Page load when called. + const char* buffer(const int index) const; + + /// @brief Read the Page header + void readHeader(std::istream&); + + /// @brief Read the Page buffers. If @a delayed is true, stream + /// pointers will be stored to load the data lazily. + void readBuffers(std::istream&, bool delayed); + + /// @brief Test if the data is out-of-core + bool isOutOfCore() const; + +private: + /// @brief Convenience method to store a copy of the supplied buffer + void copy(const std::unique_ptr& temp, int pageSize); + + /// @brief Decompress and store the supplied data + void decompress(const std::unique_ptr& temp); + + /// @brief Thread-safe loading of the data + void doLoad() const; + + std::unique_ptr mInfo = std::unique_ptr(new Info); + std::unique_ptr mData; + tbb::spin_mutex mMutex; +}; // class Page + + +/// @brief A PageHandle holds a unique ptr to a Page and a specific stream +/// pointer to a point within the decompressed Page buffer +class OPENVDB_API PageHandle +{ +public: +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + using Ptr = std::unique_ptr; +#else + using Ptr = std::shared_ptr; +#endif + + /// @brief Create the page handle + /// @param page a shared ptr to the page that stores the buffer + /// @param index start position of the buffer to be read + /// @param size total size of the buffer to be read in bytes + PageHandle(const Page::Ptr& page, const int index, const int size); + + /// @brief Retrieve a reference to the stored page + Page& page(); + + /// @brief Return the size of the buffer + int size() const { return mSize; } + + /// @brief Read and return the buffer, loading and decompressing + /// the Page if necessary. + std::unique_ptr read(); + + /// @brief Return a copy of this PageHandle + Ptr copy() { return Ptr(new PageHandle(mPage, mIndex, mSize)); } + +protected: + friend class ::TestStreamCompression; + +private: + Page::Ptr mPage; + int mIndex = -1; + int mSize = 0; +}; // class PageHandle + + +/// @brief A Paging wrapper to std::istream that is responsible for reading +/// from a given input stream and creating Page objects and PageHandles that +/// reference those pages for delayed reading. +class OPENVDB_API PagedInputStream +{ +public: + using Ptr = std::shared_ptr; + + PagedInputStream() = default; + + explicit PagedInputStream(std::istream& is); + + /// @brief Size-only mode tags the stream as only reading size data. + void setSizeOnly(bool sizeOnly) { mSizeOnly = sizeOnly; } + bool sizeOnly() const { return mSizeOnly; } + + // @brief Set and get the input stream + std::istream& getInputStream() { assert(mIs); return *mIs; } + void setInputStream(std::istream& is) { mIs = &is; } + + /// @brief Creates a PageHandle to access the next @param n bytes of the Page. + PageHandle::Ptr createHandle(std::streamsize n); + + /// @brief Takes a @a pageHandle and updates the referenced page with the + /// current stream pointer position and if @a delayed is false performs + /// an immediate read of the data. + void read(PageHandle::Ptr& pageHandle, std::streamsize n, bool delayed = true); + +private: + int mByteIndex = 0; + int mUncompressedBytes = 0; + std::istream* mIs = nullptr; + Page::Ptr mPage; + bool mSizeOnly = false; +}; // class PagedInputStream + + +/// @brief A Paging wrapper to std::ostream that is responsible for writing +/// from a given output stream at intervals set by the PageSize. As Pages are +/// variable in size, they are flushed to disk as soon as sufficiently large. +class OPENVDB_API PagedOutputStream +{ +public: + using Ptr = std::shared_ptr; + + PagedOutputStream(); + + explicit PagedOutputStream(std::ostream& os); + + /// @brief Size-only mode tags the stream as only writing size data. + void setSizeOnly(bool sizeOnly) { mSizeOnly = sizeOnly; } + bool sizeOnly() const { return mSizeOnly; } + + /// @brief Set and get the output stream + std::ostream& getOutputStream() { assert(mOs); return *mOs; } + void setOutputStream(std::ostream& os) { mOs = &os; } + + /// @brief Writes the given @param str buffer of size @param n + PagedOutputStream& write(const char* str, std::streamsize n); + + /// @brief Manually flushes the current page to disk if non-zero + void flush(); + +private: + /// @brief Compress the @param buffer of @param size bytes and write + /// out to the stream. + void compressAndWrite(const char* buffer, size_t size); + + /// @brief Resize the internal page buffer to @param size bytes + void resize(size_t size); + + std::unique_ptr mData = std::unique_ptr(new char[PageSize]); + std::unique_ptr mCompressedData = nullptr; + size_t mCapacity = PageSize; + int mBytes = 0; + std::ostream* mOs = nullptr; + bool mSizeOnly = false; +}; // class PagedOutputStream + + +} // namespace compression +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_STREAM_COMPRESSION_HAS_BEEN_INCLUDED diff --git a/openvdb/points/points.cc b/openvdb/points/points.cc new file mode 100644 index 00000000..31af8156 --- /dev/null +++ b/openvdb/points/points.cc @@ -0,0 +1,70 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file points/points.cc + +#include "PointDataGrid.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + +void +internal::initialize() +{ + // Register attribute arrays with no compression + TypedAttributeArray::registerType(); + TypedAttributeArray::registerType(); + TypedAttributeArray::registerType(); + TypedAttributeArray::registerType(); + TypedAttributeArray::registerType(); + TypedAttributeArray::registerType(); + TypedAttributeArray::registerType(); + TypedAttributeArray>::registerType(); + TypedAttributeArray>::registerType(); + TypedAttributeArray>::registerType(); + + // Register attribute arrays with group and string attribute + GroupAttributeArray::registerType(); + StringAttributeArray::registerType(); + + // Register attribute arrays with matrix and quaternion attributes + TypedAttributeArray>::registerType(); + TypedAttributeArray>::registerType(); + TypedAttributeArray>::registerType(); + TypedAttributeArray>::registerType(); + TypedAttributeArray>::registerType(); + TypedAttributeArray>::registerType(); + + // Register attribute arrays with truncate compression + TypedAttributeArray::registerType(); + TypedAttributeArray, TruncateCodec>::registerType(); + + // Register attribute arrays with fixed point compression + TypedAttributeArray, FixedPointCodec>::registerType(); + TypedAttributeArray, FixedPointCodec>::registerType(); + TypedAttributeArray, FixedPointCodec>::registerType(); + TypedAttributeArray, FixedPointCodec>::registerType(); + TypedAttributeArray, FixedPointCodec>::registerType(); + TypedAttributeArray, FixedPointCodec>::registerType(); + + // Register attribute arrays with unit vector compression + TypedAttributeArray, UnitVecCodec>::registerType(); + + // Register types associated with point data grids. + Metadata::registerType(typeNameAsString(), Int32Metadata::createMetadata); + Metadata::registerType(typeNameAsString(), Int64Metadata::createMetadata); + PointDataGrid::registerGrid(); +} + + +void +internal::uninitialize() +{ + AttributeArray::clearRegistry(); +} + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/python/CMakeLists.txt b/openvdb/python/CMakeLists.txt new file mode 100644 index 00000000..f2604320 --- /dev/null +++ b/openvdb/python/CMakeLists.txt @@ -0,0 +1,312 @@ +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: MPL-2.0 +# +#[=======================================================================[ + + CMake Configuration for OpenVDB Python bindings + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.3) +project(OpenVDBPython) + +# Monitoring _ROOT variables +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif() +include(GNUInstallDirs) + +###### OpenVDB Python Options + +option(USE_NUMPY [=[ +Build the python library with numpy support. Currently requires CMake 3.14.]=] OFF) +option(OPENVDB_PYTHON_WRAP_ALL_GRID_TYPES [=[ +Expose (almost) all of the grid types in the python module. Otherwise, only FloatGrid, BoolGrid and +Vec3SGrid will be exposed (see, e.g., exportIntGrid() in python/pyIntGrid.cc). Compiling the Python +module with this ON can be very memory-intensive.]=] OFF) + +######################################################################### + +message(STATUS "----------------------------------------------------") +message(STATUS "------------ Configuring OpenVDBPython -------------") +message(STATUS "----------------------------------------------------") + +########################################################################## + +# Collect and configure lib dependencies + +if(NOT OPENVDB_BUILD_CORE) + set(OPENVDB_LIB OpenVDB::openvdb) +else() + set(OPENVDB_LIB openvdb) +endif() + +set(OPENVDB_PYTHON_DEPS) +set(OPENVDB_PYTHON_INCLUDES) + +# Small function which mimics basic output (bar components) of +# FindPackageHandleStandardArgs. This is required as we want to ensure +# the minimum python version is MINIMUM_PYTHON_VERSION - however this cannot +# be provided to find_package(Python) with differing major versions. e.g. +# calls to find_package(Python 2.7) fails if python3 is found on the system. +function(OPENVDB_CHECK_PYTHON_VERSION _PY_VERSION _PY_PATH) + if(_PY_VERSION VERSION_LESS MINIMUM_PYTHON_VERSION) + message(FATAL_ERROR "Could NOT find Python: Found unsuitable version \"${_PY_VERSION}\"" + "but required is at least \"${MINIMUM_PYTHON_VERSION}\" (found ${_PY_PATH})" + ) + else() + message(STATUS "Found Python: ${_PY_PATH}) (found suitable version \"${_PY_VERSION}\", " + "minimum required is \"${MINIMUM_PYTHON_VERSION}\")" + ) + endif() +endfunction() + +# Configure Python and Numpy. Note that: +# - find_package(Python Interpreter Development) requires CMake >= 3.12 +# - find_package(Python NumPy) requires CMake >= 3.14 +# To ensure consistent versions between components Interpreter, Compiler, +# Development and NumPy, specify all components at the same time when using +# FindPython +# Note that the Interpreter component is only required for the python test +if(${CMAKE_VERSION} VERSION_LESS 3.12) + # CMake < 3.12 + find_package(PythonInterp REQUIRED) + find_package(PythonLibs REQUIRED) + + # Alias variables to newer names + set(Python_VERSION ${PYTHON_VERSION_STRING}) + set(Python_VERSION_MAJOR ${PYTHON_VERSION_MAJOR}) + set(Python_VERSION_MINOR ${PYTHON_VERSION_MINOR}) + set(Python_EXECUTABLE ${PYTHON_EXECUTABLE}) + set(OPENVDB_PYTHON_DEPS ${PYTHON_LIBRARIES}) + get_filename_component(Python_LIBRARY_DIRS ${OPENVDB_PYTHON_DEPS} DIRECTORY) + + list(APPEND OPENVDB_PYTHON_INCLUDES ${PYTHON_INCLUDE_DIR}) + + OPENVDB_CHECK_PYTHON_VERSION(${Python_VERSION} ${Python_EXECUTABLE}) + + if(USE_NUMPY) + # Note: This uses our custom backport in cmake/backports + find_package(NumPy ${MINIMUM_NUMPY_VERSION} REQUIRED) + list(APPEND OPENVDB_PYTHON_DEPS Python::NumPy) + endif() +elseif(${CMAKE_VERSION} VERSION_LESS 3.14) + # CMake < 3.14 + find_package(Python QUIET COMPONENTS Interpreter Development) + OPENVDB_CHECK_PYTHON_VERSION(${Python_VERSION} ${Python_EXECUTABLE}) + list(APPEND OPENVDB_PYTHON_DEPS Python::Python) + + if(USE_NUMPY) + # Note: This uses our custom backport in cmake/backports + find_package(NumPy ${MINIMUM_NUMPY_VERSION} REQUIRED) + list(APPEND OPENVDB_PYTHON_DEPS Python::NumPy) + endif() +else() + # CMake >= 3.14 + if(USE_NUMPY) + find_package(Python QUIET REQUIRED COMPONENTS Interpreter Development NumPy) + else() + find_package(Python QUIET REQUIRED COMPONENTS Interpreter Development) + endif() + + OPENVDB_CHECK_PYTHON_VERSION(${Python_VERSION} ${Python_EXECUTABLE}) + list(APPEND OPENVDB_PYTHON_DEPS Python::Python) + + # Check NumPy version + if(USE_NUMPY AND (Python_NumPy_VERSION VERSION_LESS MINIMUM_NUMPY_VERSION)) + message(FATAL_ERROR "Could NOT find NumPy (Required is at least version " + "\"${MINIMUM_NUMPY_VERSION}\")" + ) + elseif(USE_NUMPY) + message(STATUS "Found NumPy: ${Python_NumPy_INCLUDE_DIRS} (found suitable " + "version \"${Python_NumPy_VERSION}\", minimum required is " + "\"${MINIMUM_NUMPY_VERSION}\")" + ) + list(APPEND OPENVDB_PYTHON_DEPS Python::NumPy) + endif() +endif() + +if(USE_NUMPY) + if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_NUMPY_VERSION) + if(Python_NumPy_VERSION VERSION_LESS FUTURE_MINIMUM_NUMPY_VERSION) + message(DEPRECATION "Support for NumPy versions < ${FUTURE_MINIMUM_NUMPY_VERSION} " + "is deprecated and will be removed.") + endif() + endif() +endif() + +if(TARGET openvdb_shared) + # @note Both of these must be set for Boost 1.70 (VFX2020) to link against + # boost shared libraries (more specifically libraries built with -fPIC). + # http://boost.2283326.n4.nabble.com/CMake-config-scripts-broken-in-1-70-td4708957.html + # https://github.com/boostorg/boost_install/commit/160c7cb2b2c720e74463865ef0454d4c4cd9ae7c + set(BUILD_SHARED_LIBS ON) + set(Boost_USE_STATIC_LIBS OFF) +endif() + +# Boost python handling. Try to find the following named boost python libraries: +# - boost_python{Python_VERSION_MAJOR}${Python_VERSION_MINOR} +# - boost_python{Python_VERSION_MAJOR} +# - boost_python +# Prioritize the version suffixed library, failing if none exist. + +find_package(Boost ${MINIMUM_BOOST_VERSION} + QUIET COMPONENTS python${Python_VERSION_MAJOR}${Python_VERSION_MINOR} +) +find_package(Boost ${MINIMUM_BOOST_VERSION} + QUIET COMPONENTS python${Python_VERSION_MAJOR} +) +find_package(Boost ${MINIMUM_BOOST_VERSION} + QUIET COMPONENTS python +) + +if(TARGET Boost::python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}) + message(STATUS "Found boost_python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}") + list(APPEND OPENVDB_PYTHON_DEPS Boost::python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}) +elseif(TARGET Boost::python${Python_VERSION_MAJOR}) + message(STATUS "Found boost_python${Python_VERSION_MAJOR}") + list(APPEND OPENVDB_PYTHON_DEPS Boost::python${Python_VERSION_MAJOR}) +elseif(TARGET Boost::python) + message(STATUS "Found non-suffixed boost_python, assuming to be python version " + "\"${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}\" compatible" + ) + list(APPEND OPENVDB_PYTHON_DEPS Boost::python) +else() + message(FATAL_ERROR "Unable to find boost_python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}, " + "boost_python${Python_VERSION_MAJOR} or boost_python." + ) +endif() + +# If boost >= 1.65, find the required numpy boost component and link that in. +# Use the same system as above, first trying without the suffix, then with. + +if(USE_NUMPY AND ( + (${Boost_VERSION} VERSION_EQUAL 1.65) OR + (${Boost_VERSION} VERSION_GREATER 1.65))) + + find_package(Boost ${MINIMUM_BOOST_VERSION} + QUIET COMPONENTS numpy${Python_VERSION_MAJOR}${Python_VERSION_MINOR} + ) + find_package(Boost ${MINIMUM_BOOST_VERSION} + QUIET COMPONENTS numpy${Python_VERSION_MAJOR} + ) + find_package(Boost ${MINIMUM_BOOST_VERSION} + QUIET COMPONENTS numpy + ) + + if(TARGET Boost::numpy${Python_VERSION_MAJOR}${Python_VERSION_MINOR}) + message(STATUS "Found boost_numpy${Python_VERSION_MAJOR}${Python_VERSION_MINOR}") + list(APPEND OPENVDB_PYTHON_DEPS Boost::numpy${Python_VERSION_MAJOR}${Python_VERSION_MINOR}) + elseif(TARGET Boost::numpy${Python_VERSION_MAJOR}) + message(STATUS "Found boost_numpy${Python_VERSION_MAJOR}") + list(APPEND OPENVDB_PYTHON_DEPS Boost::numpy${Python_VERSION_MAJOR}) + elseif(TARGET Boost::numpy) + message(STATUS "Found non-suffixed boost_numpy, assuming to be python version " + "\"${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}\" compatible" + ) + list(APPEND OPENVDB_PYTHON_DEPS Boost::numpy) + else() + message(FATAL_ERROR "Unable to find boost_numpy${Python_VERSION_MAJOR}${Python_VERSION_MINOR}, " + "boost_numpy${Python_VERSION_MAJOR} or boost_numpy." + ) + endif() +endif() + +set(OPENVDB_PYTHON_DEPENDENT_LIBS + ${OPENVDB_LIB} + ${OPENVDB_PYTHON_DEPS} +) + +########################################################################## + +set(OPENVDB_PYTHON_MODULE_SOURCE_FILES + pyFloatGrid.cc + pyIntGrid.cc + pyMetadata.cc + pyPointGrid.cc + pyOpenVDBModule.cc + pyPointGrid.cc + pyTransform.cc + pyVec3Grid.cc +) + +if(NOT DEFINED PYOPENVDB_INSTALL_DIRECTORY) + set(PYOPENVDB_INSTALL_DIRECTORY + ${CMAKE_INSTALL_LIBDIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR} + CACHE STRING "The directory to install the pyopenvdb.so module." + ) +endif() + +add_library(pyopenvdb SHARED + ${OPENVDB_PYTHON_MODULE_SOURCE_FILES} +) + +target_include_directories(pyopenvdb + SYSTEM PUBLIC ${OPENVDB_PYTHON_INCLUDES} +) + +if(OPENVDB_PYTHON_WRAP_ALL_GRID_TYPES) + target_compile_definitions(pyopenvdb PRIVATE "-DPY_OPENVDB_WRAP_ALL_GRID_TYPES") +endif() + +if(USE_NUMPY) + target_compile_definitions(pyopenvdb PUBLIC "-DPY_OPENVDB_USE_NUMPY") +endif() + +target_link_libraries(pyopenvdb + ${OPENVDB_PYTHON_DEPENDENT_LIBS} +) + +set_target_properties(pyopenvdb PROPERTIES + PREFIX "" # no 'lib' prefix +) + +if(UNIX) + # must be .so (not .dylib) + set_target_properties(pyopenvdb PROPERTIES + SUFFIX ".so" + ) +endif() + +if(OPENVDB_ENABLE_RPATH) + # @todo There is probably a better way to do this for imported targets + set(RPATHS "") + list(APPEND RPATHS + ${Boost_LIBRARY_DIRS} + ${IlmBase_LIBRARY_DIRS} + ${Log4cplus_LIBRARY_DIRS} + ${Blosc_LIBRARY_DIRS} + ${Tbb_LIBRARY_DIRS} + ${Python_LIBRARY_DIRS} + ) + if(OPENVDB_BUILD_CORE) + list(APPEND RPATHS ${CMAKE_INSTALL_PREFIX}/lib) + else() + list(APPEND RPATHS ${OpenVDB_LIBRARY_DIRS}) + endif() + + list(REMOVE_DUPLICATES RPATHS) + set_target_properties(pyopenvdb + PROPERTIES INSTALL_RPATH "${RPATHS}" + ) + unset(RPATHS) +endif() + +set(PYTHON_PUBLIC_INCLUDE_NAMES + pyopenvdb.h +) + +install(TARGETS + pyopenvdb + DESTINATION + ${PYOPENVDB_INSTALL_DIRECTORY} +) + +install(FILES ${PYTHON_PUBLIC_INCLUDE_NAMES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/python) + +# pytest + +add_test(pytest ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/TestOpenVDB.py -v) +set_tests_properties(pytest PROPERTIES ENVIRONMENT "PYTHONPATH=$ENV{PYTHONPATH}:${CMAKE_CURRENT_BINARY_DIR}") + + diff --git a/openvdb/python/pyAccessor.h b/openvdb/python/pyAccessor.h new file mode 100644 index 00000000..d5a69f42 --- /dev/null +++ b/openvdb/python/pyAccessor.h @@ -0,0 +1,312 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_PYACCESSOR_HAS_BEEN_INCLUDED +#define OPENVDB_PYACCESSOR_HAS_BEEN_INCLUDED + +#include +#include "openvdb/openvdb.h" +#include "pyutil.h" + +namespace pyAccessor { + +namespace py = boost::python; +using namespace openvdb::OPENVDB_VERSION_NAME; + + +//@{ +/// Type traits for grid accessors +template +struct AccessorTraits +{ + using GridT = _GridT; + using NonConstGridT = GridT; + using GridPtrT = typename NonConstGridT::Ptr; + using AccessorT = typename NonConstGridT::Accessor; + using ValueT = typename AccessorT::ValueType; + + static const bool IsConst = false; + + static const char* typeName() { return "Accessor"; } + + static void setActiveState(AccessorT& acc, const Coord& ijk, bool on) { + acc.setActiveState(ijk, on); + } + static void setValueOnly(AccessorT& acc, const Coord& ijk, const ValueT& val) { + acc.setValueOnly(ijk, val); + } + static void setValueOn(AccessorT& acc, const Coord& ijk) { acc.setValueOn(ijk); } + static void setValueOn(AccessorT& acc, const Coord& ijk, const ValueT& val) { + acc.setValueOn(ijk, val); + } + static void setValueOff(AccessorT& acc, const Coord& ijk) { acc.setValueOff(ijk); } + static void setValueOff(AccessorT& acc, const Coord& ijk, const ValueT& val) { + acc.setValueOff(ijk, val); + } +}; + +// Partial specialization for const accessors +template +struct AccessorTraits +{ + using GridT = const _GridT; + using NonConstGridT = _GridT; + using GridPtrT = typename NonConstGridT::ConstPtr; + using AccessorT = typename NonConstGridT::ConstAccessor; + using ValueT = typename AccessorT::ValueType; + + static const bool IsConst = true; + + static const char* typeName() { return "ConstAccessor"; } + + static void setActiveState(AccessorT&, const Coord&, bool) { notWritable(); } + static void setValueOnly(AccessorT&, const Coord&, const ValueT&) { notWritable(); } + static void setValueOn(AccessorT&, const Coord&) { notWritable(); } + static void setValueOn(AccessorT&, const Coord&, const ValueT&) { notWritable(); } + static void setValueOff(AccessorT&, const Coord&) { notWritable(); } + static void setValueOff(AccessorT&, const Coord&, const ValueT&) { notWritable(); } + + static void notWritable() + { + PyErr_SetString(PyExc_TypeError, "accessor is read-only"); + py::throw_error_already_set(); + } +}; +//@} + + +//////////////////////////////////////// + + +/// Variant of pyutil::extractArg() that extracts a Coord from a py::object +/// argument to a given ValueAccessor method +template +inline Coord +extractCoordArg(py::object obj, const char* functionName, int argIdx = 0) +{ + return pyutil::extractArg(obj, functionName, + AccessorTraits::typeName(), argIdx, "tuple(int, int, int)"); +} + + +/// Variant of pyutil::extractArg() that extracts a value of type +/// ValueAccessor::ValueType from an argument to a ValueAccessor method +template +inline typename GridT::ValueType +extractValueArg( + py::object obj, + const char* functionName, + int argIdx = 0, // args are numbered starting from 1 + const char* expectedType = nullptr) +{ + return pyutil::extractArg( + obj, functionName, AccessorTraits::typeName(), argIdx, expectedType); +} + + +//////////////////////////////////////// + + +/// @brief ValueAccessor wrapper class that also stores a grid pointer, +/// so that the grid doesn't get deleted as long as the accessor is live +/// +/// @internal This class could have just been made to inherit from ValueAccessor, +/// but the method wrappers allow for more Pythonic error messages. For example, +/// if we constructed the Python getValue() method directly from the corresponding +/// ValueAccessor method, as follows, +/// +/// .def("getValue", &Accessor::getValue, ...) +/// +/// then the conversion from a Python type to a Coord& would be done +/// automatically. But if the Python method were called with an object of +/// a type that is not convertible to a Coord, then the TypeError message +/// would say something like "TypeError: No registered converter was able to +/// produce a C++ rvalue of type openvdb::math::Coord...". +/// Handling the type conversion manually is more work, but it allows us to +/// instead generate messages like "TypeError: expected tuple(int, int, int), +/// found str as argument to FloatGridAccessor.getValue()". +template +class AccessorWrap +{ +public: + using Traits = AccessorTraits<_GridType>; + using Accessor = typename Traits::AccessorT; + using ValueType = typename Traits::ValueT; + using GridType = typename Traits::NonConstGridT; + using GridPtrType = typename Traits::GridPtrT; + + AccessorWrap(GridPtrType grid): mGrid(grid), mAccessor(grid->getAccessor()) {} + + AccessorWrap copy() const { return *this; } + + void clear() { mAccessor.clear(); } + + GridPtrType parent() const { return mGrid; } + + ValueType getValue(py::object coordObj) + { + const Coord ijk = extractCoordArg(coordObj, "getValue"); + return mAccessor.getValue(ijk); + } + + int getValueDepth(py::object coordObj) + { + const Coord ijk = extractCoordArg(coordObj, "getValueDepth"); + return mAccessor.getValueDepth(ijk); + } + + int isVoxel(py::object coordObj) + { + const Coord ijk = extractCoordArg(coordObj, "isVoxel"); + return mAccessor.isVoxel(ijk); + } + + py::tuple probeValue(py::object coordObj) + { + const Coord ijk = extractCoordArg(coordObj, "probeValue"); + ValueType value; + bool on = mAccessor.probeValue(ijk, value); + return py::make_tuple(value, on); + } + + bool isValueOn(py::object coordObj) + { + const Coord ijk = extractCoordArg(coordObj, "isValueOn"); + return mAccessor.isValueOn(ijk); + } + + void setActiveState(py::object coordObj, bool on) + { + const Coord ijk = extractCoordArg(coordObj, "setActiveState", /*argIdx=*/1); + Traits::setActiveState(mAccessor, ijk, on); + } + + void setValueOnly(py::object coordObj, py::object valObj) + { + Coord ijk = extractCoordArg(coordObj, "setValueOnly", 1); + ValueType val = extractValueArg(valObj, "setValueOnly", 2); + Traits::setValueOnly(mAccessor, ijk, val); + } + + void setValueOn(py::object coordObj, py::object valObj) + { + Coord ijk = extractCoordArg(coordObj, "setValueOn", 1); + if (valObj.is_none()) { + Traits::setValueOn(mAccessor, ijk); + } else { + ValueType val = extractValueArg(valObj, "setValueOn", 2); + Traits::setValueOn(mAccessor, ijk, val); + } + } + + void setValueOff(py::object coordObj, py::object valObj) + { + Coord ijk = extractCoordArg(coordObj, "setValueOff", 1); + if (valObj.is_none()) { + Traits::setValueOff(mAccessor, ijk); + } else { + ValueType val = extractValueArg(valObj, "setValueOff", 2); + Traits::setValueOff(mAccessor, ijk, val); + } + } + + int isCached(py::object coordObj) + { + const Coord ijk = extractCoordArg(coordObj, "isCached"); + return mAccessor.isCached(ijk); + } + + /// @brief Define a Python wrapper class for this C++ class. + static void wrap() + { + const std::string + pyGridTypeName = pyutil::GridTraits::name(), + pyValueTypeName = openvdb::typeNameAsString(), + pyAccessorTypeName = Traits::typeName(); + + py::class_ clss( + pyAccessorTypeName.c_str(), + (std::string(Traits::IsConst ? "Read-only" : "Read/write") + + " access by (i, j, k) index coordinates to the voxels\nof a " + + pyGridTypeName).c_str(), + py::no_init); + + clss.def("copy", &AccessorWrap::copy, + ("copy() -> " + pyAccessorTypeName + "\n\n" + "Return a copy of this accessor.").c_str()) + + .def("clear", &AccessorWrap::clear, + "clear()\n\n" + "Clear this accessor of all cached data.") + + .add_property("parent", &AccessorWrap::parent, + ("this accessor's parent " + pyGridTypeName).c_str()) + + // + // Voxel access + // + .def("getValue", &AccessorWrap::getValue, + py::arg("ijk"), + ("getValue(ijk) -> " + pyValueTypeName + "\n\n" + "Return the value of the voxel at coordinates (i, j, k).").c_str()) + + .def("getValueDepth", &AccessorWrap::getValueDepth, + py::arg("ijk"), + "getValueDepth(ijk) -> int\n\n" + "Return the tree depth (0 = root) at which the value of voxel\n" + "(i, j, k) resides. If (i, j, k) isn't explicitly represented in\n" + "the tree (i.e., it is implicitly a background voxel), return -1.") + + .def("isVoxel", &AccessorWrap::isVoxel, + py::arg("ijk"), + "isVoxel(ijk) -> bool\n\n" + "Return True if voxel (i, j, k) resides at the leaf level of the tree.") + + .def("probeValue", &AccessorWrap::probeValue, + py::arg("ijk"), + "probeValue(ijk) -> value, bool\n\n" + "Return the value of the voxel at coordinates (i, j, k)\n" + "together with the voxel's active state.") + + .def("isValueOn", &AccessorWrap::isValueOn, + py::arg("ijk"), + "isValueOn(ijk) -> bool\n\n" + "Return the active state of the voxel at coordinates (i, j, k).") + .def("setActiveState", &AccessorWrap::setActiveState, + (py::arg("ijk"), py::arg("on")), + "setActiveState(ijk, on)\n\n" + "Mark voxel (i, j, k) as either active or inactive (True or False),\n" + "but don't change its value.") + + .def("setValueOnly", &AccessorWrap::setValueOnly, + (py::arg("ijk"), py::arg("value")), + "setValueOnly(ijk, value)\n\n" + "Set the value of voxel (i, j, k), but don't change its active state.") + + .def("setValueOn", &AccessorWrap::setValueOn, + (py::arg("ijk"), py::arg("value") = py::object()), + "setValueOn(ijk, value=None)\n\n" + "Mark voxel (i, j, k) as active and, if the given value\n" + "is not None, set the voxel's value.\n") + .def("setValueOff", &AccessorWrap::setValueOff, + (py::arg("ijk"), py::arg("value") = py::object()), + "setValueOff(ijk, value=None)\n\n" + "Mark voxel (i, j, k) as inactive and, if the given value\n" + "is not None, set the voxel's value.") + + .def("isCached", &AccessorWrap::isCached, + py::arg("ijk"), + "isCached(ijk) -> bool\n\n" + "Return True if this accessor has cached the path to voxel (i, j, k).") + + ; // py::class_ + } + +private: + const GridPtrType mGrid; + Accessor mAccessor; +}; // class AccessorWrap + +} // namespace pyAccessor + +#endif // OPENVDB_PYACCESSOR_HAS_BEEN_INCLUDED diff --git a/openvdb/python/pyFloatGrid.cc b/openvdb/python/pyFloatGrid.cc new file mode 100644 index 00000000..11be024f --- /dev/null +++ b/openvdb/python/pyFloatGrid.cc @@ -0,0 +1,39 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file pyFloatGrid.cc +/// @author Peter Cucka +/// @brief Boost.Python wrappers for scalar, floating-point openvdb::Grid types + +#include "pyGrid.h" + + +void exportFloatGrid(); + + +/// Create a Python wrapper for each supported Grid type. +void +exportFloatGrid() +{ + // Add a module-level list that gives the types of all supported Grid classes. + py::scope().attr("GridTypes") = py::list(); + +#if defined(PY_OPENVDB_USE_NUMPY) && !defined(PY_OPENVDB_USE_BOOST_PYTHON_NUMPY) + // Specify that py::numeric::array should refer to the Python type numpy.ndarray + // (rather than the older Numeric.array). + py::numeric::array::set_module_and_type("numpy", "ndarray"); +#endif + + pyGrid::exportGrid(); +#ifdef PY_OPENVDB_WRAP_ALL_GRID_TYPES + pyGrid::exportGrid(); +#endif + + py::def("createLevelSetSphere", + &pyGrid::createLevelSetSphere, + (py::arg("radius"), py::arg("center")=openvdb::Coord(), py::arg("voxelSize")=1.0, + py::arg("halfWidth")=openvdb::LEVEL_SET_HALF_WIDTH), + "createLevelSetSphere(radius, center, voxelSize, halfWidth) -> FloatGrid\n\n" + "Return a grid containing a narrow-band level set representation\n" + "of a sphere."); +} diff --git a/openvdb/python/pyGrid.h b/openvdb/python/pyGrid.h new file mode 100644 index 00000000..7b787d3b --- /dev/null +++ b/openvdb/python/pyGrid.h @@ -0,0 +1,2566 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file pyGrid.h +/// @author Peter Cucka +/// @brief Boost.Python wrapper for openvdb::Grid + +#ifndef OPENVDB_PYGRID_HAS_BEEN_INCLUDED +#define OPENVDB_PYGRID_HAS_BEEN_INCLUDED + +#include +#ifndef DWA_BOOST_VERSION +#include +#define DWA_BOOST_VERSION (10 * BOOST_VERSION) +#endif +#ifdef PY_OPENVDB_USE_NUMPY + #if DWA_BOOST_VERSION >= 1065000 + // boost::python::numeric was replaced with boost::python::numpy in Boost 1.65. + // (boost::python::numpy requires NumPy 1.7 or later.) + #include + //#include // for PyArray_Descr (see pyGrid::arrayTypeId()) + #define PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + #else + #define PY_ARRAY_UNIQUE_SYMBOL PY_OPENVDB_ARRAY_API + #define NO_IMPORT_ARRAY // NumPy gets initialized during module initialization + #include + #ifdef NPY_1_7_API_VERSION + #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION + #endif + #include // for PyArrayObject + #endif + #include "openvdb/tools/MeshToVolume.h" + #include "openvdb/tools/VolumeToMesh.h" // for tools::volumeToMesh() +#endif +#include "openvdb/openvdb.h" +#include "openvdb/io/Stream.h" +#include "openvdb/math/Math.h" // for math::isExactlyEqual() +#include "openvdb/points/PointDataGrid.h" +#include "openvdb/tools/LevelSetSphere.h" +#include "openvdb/tools/Dense.h" +#include "openvdb/tools/ChangeBackground.h" +#include "openvdb/tools/Prune.h" +#include "openvdb/tools/SignedFloodFill.h" +#include "pyutil.h" +#include "pyAccessor.h" // for pyAccessor::AccessorWrap +#include "pyopenvdb.h" +#include // for std::max() +#include // for memcpy() +#include +#include +#include +#include +#include + +namespace py = boost::python; + +#ifdef __clang__ +// This is a private header, so it's OK to include a "using namespace" directive. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wheader-hygiene" +#endif + +using namespace openvdb::OPENVDB_VERSION_NAME; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + +namespace pyopenvdb { + +inline py::object +getPyObjectFromGrid(const GridBase::Ptr& grid) +{ + if (!grid) return py::object(); + +#define CONVERT_BASE_TO_GRID(GridType, grid) \ + if (grid->isType()) { \ + return py::object(gridPtrCast(grid)); \ + } + + CONVERT_BASE_TO_GRID(FloatGrid, grid); + CONVERT_BASE_TO_GRID(Vec3SGrid, grid); + CONVERT_BASE_TO_GRID(BoolGrid, grid); +#ifdef PY_OPENVDB_WRAP_ALL_GRID_TYPES + CONVERT_BASE_TO_GRID(DoubleGrid, grid); + CONVERT_BASE_TO_GRID(Int32Grid, grid); + CONVERT_BASE_TO_GRID(Int64Grid, grid); + CONVERT_BASE_TO_GRID(Vec3IGrid, grid); + CONVERT_BASE_TO_GRID(Vec3DGrid, grid); + CONVERT_BASE_TO_GRID(points::PointDataGrid, grid); +#endif +#undef CONVERT_BASE_TO_GRID + + OPENVDB_THROW(TypeError, grid->type() + " is not a supported OpenVDB grid type"); +} + + +inline openvdb::GridBase::Ptr +getGridFromPyObject(const boost::python::object& gridObj) +{ + if (!gridObj) return GridBase::Ptr(); + +#define CONVERT_GRID_TO_BASE(GridPtrType) \ + { \ + py::extract x(gridObj); \ + if (x.check()) return x(); \ + } + + // Extract a grid pointer of one of the supported types + // from the input object, then cast it to a base pointer. + CONVERT_GRID_TO_BASE(FloatGrid::Ptr); + CONVERT_GRID_TO_BASE(Vec3SGrid::Ptr); + CONVERT_GRID_TO_BASE(BoolGrid::Ptr); +#ifdef PY_OPENVDB_WRAP_ALL_GRID_TYPES + CONVERT_GRID_TO_BASE(DoubleGrid::Ptr); + CONVERT_GRID_TO_BASE(Int32Grid::Ptr); + CONVERT_GRID_TO_BASE(Int64Grid::Ptr); + CONVERT_GRID_TO_BASE(Vec3IGrid::Ptr); + CONVERT_GRID_TO_BASE(Vec3DGrid::Ptr); + CONVERT_GRID_TO_BASE(points::PointDataGrid::Ptr); +#endif +#undef CONVERT_GRID_TO_BASE + + OPENVDB_THROW(TypeError, + pyutil::className(gridObj) + " is not a supported OpenVDB grid type"); +} + + +inline openvdb::GridBase::Ptr +getGridFromPyObject(PyObject* gridObj) +{ + return getGridFromPyObject(pyutil::pyBorrow(gridObj)); +} + +} // namespace pyopenvdb + + +//////////////////////////////////////// + + +namespace pyGrid { + +inline py::object +getGridFromGridBase(GridBase::Ptr grid) +{ + py::object obj; + try { + obj = pyopenvdb::getPyObjectFromGrid(grid); + } catch (openvdb::TypeError& e) { + PyErr_SetString(PyExc_TypeError, e.what()); + py::throw_error_already_set(); + return py::object(); + } + return obj; +} + + +/// GridBase is not exposed in Python because it isn't really needed +/// (and because exposing it would be complicated, requiring wrapping +/// pure virtual functions like GridBase::baseTree()), but there are +/// a few cases where, internally, we need to extract a GridBase::Ptr +/// from a py::object. Hence this converter. +inline GridBase::Ptr +getGridBaseFromGrid(py::object gridObj) +{ + GridBase::Ptr grid; + try { + grid = pyopenvdb::getGridFromPyObject(gridObj); + } catch (openvdb::TypeError& e) { + PyErr_SetString(PyExc_TypeError, e.what()); + py::throw_error_already_set(); + return GridBase::Ptr(); + } + return grid; +} + + +//////////////////////////////////////// + + +/// Variant of pyutil::extractArg() that uses the class name of a given grid type +template +inline T +extractValueArg( + py::object obj, + const char* functionName, + int argIdx = 0, // args are numbered starting from 1 + const char* expectedType = nullptr) +{ + return pyutil::extractArg(obj, + functionName, pyutil::GridTraits::name(), argIdx, expectedType); +} + + +/// @brief Variant of pyutil::extractArg() that uses the class name +/// and @c ValueType of a given grid type +template +inline typename GridType::ValueType +extractValueArg( + py::object obj, + const char* functionName, + int argIdx = 0, // args are numbered starting from 1 + const char* expectedType = nullptr) +{ + return extractValueArg( + obj, functionName, argIdx, expectedType); +} + + +//////////////////////////////////////// + + +template +inline typename GridType::Ptr +copyGrid(GridType& grid) +{ + return grid.copy(); +} + + +template +inline bool +sharesWith(const GridType& grid, py::object other) +{ + py::extract x(other); + if (x.check()) { + typename GridType::ConstPtr otherGrid = x(); + return (&otherGrid->tree() == &grid.tree()); + } + return false; +} + + +//////////////////////////////////////// + + +template +inline std::string +getValueType() +{ + return pyutil::GridTraits::valueTypeName(); +} + + +template +inline typename GridType::ValueType +getZeroValue() +{ + return openvdb::zeroVal(); +} + + +template +inline typename GridType::ValueType +getOneValue() +{ + using ValueT = typename GridType::ValueType; + return ValueT(openvdb::zeroVal() + 1); +} + + +template +inline bool +notEmpty(const GridType& grid) +{ + return !grid.empty(); +} + + +template +inline typename GridType::ValueType +getGridBackground(const GridType& grid) +{ + return grid.background(); +} + + +template +inline void +setGridBackground(GridType& grid, py::object obj) +{ + tools::changeBackground(grid.tree(), extractValueArg(obj, "setBackground")); +} + + +inline void +setGridName(GridBase::Ptr grid, py::object strObj) +{ + if (grid) { + if (!strObj) { // if name is None + grid->removeMeta(GridBase::META_GRID_NAME); + } else { + const std::string name = pyutil::extractArg( + strObj, "setName", /*className=*/nullptr, /*argIdx=*/1, "str"); + grid->setName(name); + } + } +} + + +inline void +setGridCreator(GridBase::Ptr grid, py::object strObj) +{ + if (grid) { + if (!strObj) { // if name is None + grid->removeMeta(GridBase::META_GRID_CREATOR); + } else { + const std::string name = pyutil::extractArg( + strObj, "setCreator", /*className=*/nullptr, /*argIdx=*/1, "str"); + grid->setCreator(name); + } + } +} + + +inline std::string +getGridClass(GridBase::ConstPtr grid) +{ + return GridBase::gridClassToString(grid->getGridClass()); +} + + +inline void +setGridClass(GridBase::Ptr grid, py::object strObj) +{ + if (!strObj) { + grid->clearGridClass(); + } else { + const std::string name = pyutil::extractArg( + strObj, "setGridClass", /*className=*/nullptr, /*argIdx=*/1, "str"); + grid->setGridClass(GridBase::stringToGridClass(name)); + } +} + + +inline std::string +getVecType(GridBase::ConstPtr grid) +{ + return GridBase::vecTypeToString(grid->getVectorType()); +} + + +inline void +setVecType(GridBase::Ptr grid, py::object strObj) +{ + if (!strObj) { + grid->clearVectorType(); + } else { + const std::string name = pyutil::extractArg( + strObj, "setVectorType", /*className=*/nullptr, /*argIdx=*/1, "str"); + grid->setVectorType(GridBase::stringToVecType(name)); + } +} + + +inline std::string +gridInfo(GridBase::ConstPtr grid, int verbosity) +{ + std::ostringstream ostr; + grid->print(ostr, std::max(1, verbosity)); + return ostr.str(); +} + + +//////////////////////////////////////// + + +inline void +setGridTransform(GridBase::Ptr grid, py::object xformObj) +{ + if (grid) { + if (math::Transform::Ptr xform = pyutil::extractArg( + xformObj, "setTransform", /*className=*/nullptr, /*argIdx=*/1, "Transform")) + { + grid->setTransform(xform); + } else { + PyErr_SetString(PyExc_ValueError, "null transform"); + py::throw_error_already_set(); + } + } +} + + +//////////////////////////////////////// + + +// Helper class to construct a pyAccessor::AccessorWrap for a given grid, +// permitting partial specialization for const vs. non-const grids +template +struct AccessorHelper +{ + using Wrapper = typename pyAccessor::AccessorWrap; + static Wrapper wrap(typename GridType::Ptr grid) + { + if (!grid) { + PyErr_SetString(PyExc_ValueError, "null grid"); + py::throw_error_already_set(); + } + return Wrapper(grid); + } +}; + +// Specialization for const grids +template +struct AccessorHelper +{ + using Wrapper = typename pyAccessor::AccessorWrap; + static Wrapper wrap(typename GridType::ConstPtr grid) + { + if (!grid) { + PyErr_SetString(PyExc_ValueError, "null grid"); + py::throw_error_already_set(); + } + return Wrapper(grid); + } +}; + + +/// Return a non-const accessor (wrapped in a pyAccessor::AccessorWrap) for the given grid. +template +inline typename AccessorHelper::Wrapper +getAccessor(typename GridType::Ptr grid) +{ + return AccessorHelper::wrap(grid); +} + +/// @brief Return a const accessor (wrapped in a pyAccessor::AccessorWrap) for the given grid. +/// @internal Note that the grid pointer is non-const, even though the grid is +/// treated as const. This is because we don't expose a const grid type in Python. +template +inline typename AccessorHelper::Wrapper +getConstAccessor(typename GridType::Ptr grid) +{ + return AccessorHelper::wrap(grid); +} + + +//////////////////////////////////////// + + +template +inline py::tuple +evalLeafBoundingBox(const GridType& grid) +{ + CoordBBox bbox; + grid.tree().evalLeafBoundingBox(bbox); + return py::make_tuple(bbox.min(), bbox.max()); +} + + +template +inline Coord +evalLeafDim(const GridType& grid) +{ + Coord dim; + grid.tree().evalLeafDim(dim); + return dim; +} + + +template +inline py::tuple +evalActiveVoxelBoundingBox(const GridType& grid) +{ + CoordBBox bbox = grid.evalActiveVoxelBoundingBox(); + return py::make_tuple(bbox.min(), bbox.max()); +} + + +template +inline py::tuple +getNodeLog2Dims(const GridType& grid) +{ + std::vector dims; + grid.tree().getNodeLog2Dims(dims); + py::list lst; + for (size_t i = 0, N = dims.size(); i < N; ++i) { + lst.append(dims[i]); + } + return py::tuple(lst); +} + + +template +inline Index +treeDepth(const GridType& grid) +{ + return grid.tree().treeDepth(); +} + + +template +inline Index32 +leafCount(const GridType& grid) +{ + return grid.tree().leafCount(); +} + + +template +inline Index32 +nonLeafCount(const GridType& grid) +{ + return grid.tree().nonLeafCount(); +} + + +template +inline Index64 +activeLeafVoxelCount(const GridType& grid) +{ + return grid.tree().activeLeafVoxelCount(); +} + + +template +inline py::tuple +evalMinMax(const GridType& grid) +{ + typename GridType::ValueType vmin, vmax; + grid.tree().evalMinMax(vmin, vmax); + return py::make_tuple(vmin, vmax); +} + + +template +inline py::tuple +getIndexRange(const GridType& grid) +{ + CoordBBox bbox; + grid.tree().getIndexRange(bbox); + return py::make_tuple(bbox.min(), bbox.max()); +} + + +//template +//inline void +//expandIndexRange(GridType& grid, py::object coordObj) +//{ +// Coord xyz = extractValueArg( +// coordObj, "expand", 0, "tuple(int, int, int)"); +// grid.tree().expand(xyz); +//} + + +//////////////////////////////////////// + + +inline py::dict +getAllMetadata(GridBase::ConstPtr grid) +{ + if (grid) return py::dict(static_cast(*grid)); + return py::dict(); +} + + +inline void +replaceAllMetadata(GridBase::Ptr grid, const MetaMap& metadata) +{ + if (grid) { + grid->clearMetadata(); + for (MetaMap::ConstMetaIterator it = metadata.beginMeta(); + it != metadata.endMeta(); ++it) + { + if (it->second) grid->insertMeta(it->first, *it->second); + } + } +} + + +inline void +updateMetadata(GridBase::Ptr grid, const MetaMap& metadata) +{ + if (grid) { + for (MetaMap::ConstMetaIterator it = metadata.beginMeta(); + it != metadata.endMeta(); ++it) + { + if (it->second) { + grid->removeMeta(it->first); + grid->insertMeta(it->first, *it->second); + } + } + } +} + + +inline py::dict +getStatsMetadata(GridBase::ConstPtr grid) +{ + MetaMap::ConstPtr metadata; + if (grid) metadata = grid->getStatsMetadata(); + if (metadata) return py::dict(*metadata); + return py::dict(); +} + + +inline py::object +getMetadataKeys(GridBase::ConstPtr grid) +{ + if (grid) { +#if PY_MAJOR_VERSION >= 3 + // Return an iterator over the "keys" view of a dict. + return py::import("builtins").attr("iter")( + py::dict(static_cast(*grid)).keys()); +#else + return py::dict(static_cast(*grid)).iterkeys(); +#endif + } + return py::object(); +} + + +inline py::object +getMetadata(GridBase::ConstPtr grid, py::object nameObj) +{ + if (!grid) return py::object(); + + const std::string name = pyutil::extractArg( + nameObj, "__getitem__", nullptr, /*argIdx=*/1, "str"); + + Metadata::ConstPtr metadata = (*grid)[name]; + if (!metadata) { + PyErr_SetString(PyExc_KeyError, name.c_str()); + py::throw_error_already_set(); + } + + // Use the MetaMap-to-dict converter (see pyOpenVDBModule.cc) to convert + // the Metadata value to a Python object of the appropriate type. + /// @todo Would be more efficient to convert the Metadata object + /// directly to a Python object. + MetaMap metamap; + metamap.insertMeta(name, *metadata); + return py::dict(metamap)[name]; +} + + +inline void +setMetadata(GridBase::Ptr grid, py::object nameObj, py::object valueObj) +{ + if (!grid) return; + + const std::string name = pyutil::extractArg( + nameObj, "__setitem__", nullptr, /*argIdx=*/1, "str"); + + // Insert the Python object into a Python dict, then use the dict-to-MetaMap + // converter (see pyOpenVDBModule.cc) to convert the dict to a MetaMap + // containing a Metadata object of the appropriate type. + /// @todo Would be more efficient to convert the Python object + /// directly to a Metadata object. + py::dict dictObj; + dictObj[name] = valueObj; + MetaMap metamap = py::extract(dictObj); + + if (Metadata::Ptr metadata = metamap[name]) { + grid->removeMeta(name); + grid->insertMeta(name, *metadata); + } +} + + +inline void +removeMetadata(GridBase::Ptr grid, const std::string& name) +{ + if (grid) { + Metadata::Ptr metadata = (*grid)[name]; + if (!metadata) { + PyErr_SetString(PyExc_KeyError, name.c_str()); + py::throw_error_already_set(); + } + grid->removeMeta(name); + } +} + + +inline bool +hasMetadata(GridBase::ConstPtr grid, const std::string& name) +{ + if (grid) return ((*grid)[name].get() != nullptr); + return false; +} + + +//////////////////////////////////////// + + +template +inline void +prune(GridType& grid, py::object tolerance) +{ + tools::prune(grid.tree(), extractValueArg(tolerance, "prune")); +} + + +template +inline void +pruneInactive(GridType& grid, py::object valObj) +{ + if (valObj.is_none()) { + tools::pruneInactive(grid.tree()); + } else { + tools::pruneInactiveWithValue( + grid.tree(), extractValueArg(valObj, "pruneInactive")); + } +} + + +template +inline void +fill(GridType& grid, py::object minObj, py::object maxObj, + py::object valObj, bool active) +{ + const Coord + bmin = extractValueArg(minObj, "fill", 1, "tuple(int, int, int)"), + bmax = extractValueArg(maxObj, "fill", 2, "tuple(int, int, int)"); + grid.fill(CoordBBox(bmin, bmax), extractValueArg(valObj, "fill", 3), active); +} + + +template +inline void +signedFloodFill(GridType& grid) +{ + tools::signedFloodFill(grid.tree()); +} + + +//////////////////////////////////////// + + +#ifndef PY_OPENVDB_USE_NUMPY + +template +inline void +copyFromArray(GridType&, const py::object&, py::object, py::object) +{ + PyErr_SetString(PyExc_NotImplementedError, "this module was built without NumPy support"); + boost::python::throw_error_already_set(); +} + +template +inline void +copyToArray(GridType&, const py::object&, py::object) +{ + PyErr_SetString(PyExc_NotImplementedError, "this module was built without NumPy support"); + boost::python::throw_error_already_set(); +} + +#else // if defined(PY_OPENVDB_USE_NUMPY) + +using ArrayDimVec = std::vector; + +#ifdef PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + +// ID numbers for supported value types +enum class DtId { NONE, FLOAT, DOUBLE, BOOL, INT16, INT32, INT64, UINT32, UINT64/*, HALF*/ }; + +using NumPyArrayType = py::numpy::ndarray; + +#else // if !defined PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + +// NumPy type numbers for supported value types +enum class DtId { + NONE = NPY_NOTYPE, + FLOAT = NPY_FLOAT, + DOUBLE = NPY_DOUBLE, + BOOL = NPY_BOOL, + INT16 = NPY_INT16, + INT32 = NPY_INT32, + INT64 = NPY_INT64, + UINT32 = NPY_UINT32, + UINT64 = NPY_UINT64, + //HALF = NPY_HALF +}; + +using NumPyArrayType = py::numeric::array; + +#endif // PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + + +template struct NumPyToCpp {}; +template<> struct NumPyToCpp { using type = float; }; +template<> struct NumPyToCpp { using type = double; }; +template<> struct NumPyToCpp { using type = bool; }; +template<> struct NumPyToCpp { using type = Int16; }; +template<> struct NumPyToCpp { using type = Int32; }; +template<> struct NumPyToCpp { using type = Int64; }; +template<> struct NumPyToCpp { using type = Index32; }; +template<> struct NumPyToCpp { using type = Index64; }; +//template<> struct NumPyToCpp { using type = half; }; + + +#if 0 +template struct CppToNumPy { static const DtId typeId = DtId::NONE; }; +template<> struct CppToNumPy { static const DtId typeId = DtId::FLOAT; }; +template<> struct CppToNumPy { static const DtId typeId = DtId::DOUBLE; }; +template<> struct CppToNumPy { static const DtId typeId = DtId::BOOL; }; +template<> struct CppToNumPy { static const DtId typeId = DtId::INT16; }; +template<> struct CppToNumPy { static const DtId typeId = DtId::INT32; }; +template<> struct CppToNumPy { static const DtId typeId = DtId::INT64; }; +template<> struct CppToNumPy { static const DtId typeId = DtId::UINT32; }; +template<> struct CppToNumPy { static const DtId typeId = DtId::UINT64; }; +//template<> struct CppToNumPy { static const DtId typeId = DtId::HALF; }; +#endif + + +#ifdef PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + +// Return the ID number of the given NumPy array's data type. +/// @todo Revisit this if and when py::numpy::dtype ever provides a type number accessor. +inline DtId +arrayTypeId(const py::numpy::ndarray& arrayObj) +{ + namespace np = py::numpy; + const auto dtype = arrayObj.get_dtype(); +#if 0 + // More efficient than np::equivalent(), but requires NumPy headers. + if (const auto* descr = reinterpret_cast(dtype.ptr())) { + const auto typeId = static_cast(descr->type_num); + switch (typeId) { + case DtId::NONE: break; + case DtId::FLOAT: case DtId::DOUBLE: case DtId::BOOL: + case DtId::INT16: case DtId::INT32: case DtId::INT64: + case DtId::UINT32: case DtId::UINT64: + return typeId; + } + throw openvdb::TypeError{}; + } +#else + if (np::equivalent(dtype, np::dtype::get_builtin())) return DtId::FLOAT; + if (np::equivalent(dtype, np::dtype::get_builtin())) return DtId::DOUBLE; + if (np::equivalent(dtype, np::dtype::get_builtin())) return DtId::BOOL; + if (np::equivalent(dtype, np::dtype::get_builtin())) return DtId::INT16; + if (np::equivalent(dtype, np::dtype::get_builtin())) return DtId::INT32; + if (np::equivalent(dtype, np::dtype::get_builtin())) return DtId::INT64; + if (np::equivalent(dtype, np::dtype::get_builtin())) return DtId::UINT32; + if (np::equivalent(dtype, np::dtype::get_builtin())) return DtId::UINT64; + //if (np::equivalent(dtype, np::dtype::get_builtin())) return DtId::HALF; +#endif + throw openvdb::TypeError{}; +} + + +// Return a string description of the given NumPy array's data type. +inline std::string +arrayTypeName(const py::numpy::ndarray& arrayObj) +{ + return pyutil::str(arrayObj.get_dtype()); +} + + +// Return the dimensions of the given NumPy array. +inline ArrayDimVec +arrayDimensions(const py::numpy::ndarray& arrayObj) +{ + ArrayDimVec dims; + for (int i = 0, N = arrayObj.get_nd(); i < N; ++i) { + dims.push_back(static_cast(arrayObj.shape(i))); + } + return dims; +} + +#else // !defined PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + +// Return the ID number of the given NumPy array's data type. +inline DtId +arrayTypeId(const py::numeric::array& arrayObj) +{ + const PyArray_Descr* dtype = nullptr; + if (PyArrayObject* arrayObjPtr = reinterpret_cast(arrayObj.ptr())) { + dtype = PyArray_DESCR(arrayObjPtr); + } + if (dtype) return static_cast(dtype->type_num); + throw openvdb::TypeError{}; +} + + +// Return a string description of the given NumPy array's data type. +inline std::string +arrayTypeName(const py::numeric::array& arrayObj) +{ + std::string name; + if (PyObject_HasAttrString(arrayObj.ptr(), "dtype")) { + name = pyutil::str(arrayObj.attr("dtype")); + } else { + name = "'_'"; + PyArrayObject* arrayObjPtr = reinterpret_cast(arrayObj.ptr()); + name[1] = PyArray_DESCR(arrayObjPtr)->kind; + } + return name; +} + + +// Return the dimensions of the given NumPy array. +inline ArrayDimVec +arrayDimensions(const py::numeric::array& arrayObj) +{ + const py::object shape = arrayObj.attr("shape"); + ArrayDimVec dims; + for (long i = 0, N = py::len(shape); i < N; ++i) { + dims.push_back(py::extract(shape[i])); + } + return dims; +} + + +inline py::object +copyNumPyArray(PyArrayObject* arrayObj, NPY_ORDER order = NPY_CORDER) +{ +#ifdef __GNUC__ + // Silence GCC "casting between pointer-to-function and pointer-to-object" warnings. + __extension__ +#endif + auto obj = pyutil::pyBorrow(PyArray_NewCopy(arrayObj, order)); + return obj; +} + +#endif // PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + + +// Abstract base class for helper classes that copy data between +// NumPy arrays of various types and grids of various types +template +class CopyOpBase +{ +public: + using ValueT = typename GridType::ValueType; + + CopyOpBase(bool toGrid, GridType& grid, py::object arrObj, + py::object coordObj, py::object tolObj) + : mToGrid(toGrid) + , mGrid(&grid) + { + const char* const opName[2] = { "copyToArray", "copyFromArray" }; + + // Extract the coordinates (i, j, k) of the voxel at which to start populating data. + // Voxel (i, j, k) will correspond to array element (0, 0, 0). + const Coord origin = extractValueArg( + coordObj, opName[toGrid], 1, "tuple(int, int, int)"); + + // Extract a reference to (not a copy of) the NumPy array, + // or throw an exception if arrObj is not a NumPy array object. + const auto arrayObj = pyutil::extractArg( + arrObj, opName[toGrid], pyutil::GridTraits::name(), + /*argIdx=*/1, "numpy.ndarray"); + +#ifdef PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + mArray = arrayObj.get_data(); +#else + mArray = PyArray_DATA(reinterpret_cast(arrayObj.ptr())); +#endif + + mArrayTypeName = arrayTypeName(arrayObj); + mArrayTypeId = arrayTypeId(arrayObj); + mArrayDims = arrayDimensions(arrayObj); + + mTolerance = extractValueArg(tolObj, opName[toGrid], 2); + + // Compute the bounding box of the region of the grid that is to be copied from or to. + Coord bboxMax = origin; + for (size_t n = 0, N = std::min(mArrayDims.size(), 3); n < N; ++n) { + bboxMax[n] += int(mArrayDims[n]) - 1; + } + mBBox.reset(origin, bboxMax); + } + virtual ~CopyOpBase() {} + + void operator()() const + { + try { + if (mToGrid) { + copyFromArray(); // copy data from the array to the grid + } else { + copyToArray(); // copy data from the grid to the array + } + } catch (openvdb::TypeError&) { + PyErr_Format(PyExc_TypeError, + "unsupported NumPy data type %s", mArrayTypeName.c_str()); + boost::python::throw_error_already_set(); + } + } + +protected: + virtual void validate() const = 0; + virtual void copyFromArray() const = 0; + virtual void copyToArray() const = 0; + + template + void fromArray() const + { + validate(); + tools::Dense valArray(mBBox, static_cast(mArray)); + tools::copyFromDense(valArray, *mGrid, mTolerance); + } + + template + void toArray() const + { + validate(); + tools::Dense valArray(mBBox, static_cast(mArray)); + tools::copyToDense(*mGrid, valArray); + } + + + bool mToGrid; // if true, copy from the array to the grid, else vice-versa + void* mArray; + GridType* mGrid; + DtId mArrayTypeId; + ArrayDimVec mArrayDims; + std::string mArrayTypeName; + CoordBBox mBBox; + ValueT mTolerance; +}; // class CopyOpBase + + +// Helper subclass that can be specialized for various grid and NumPy array types +template class CopyOp: public CopyOpBase {}; + +// Specialization for scalar grids +template +class CopyOp: public CopyOpBase +{ +public: + CopyOp(bool toGrid, GridType& grid, py::object arrObj, py::object coordObj, + py::object tolObj = py::object(zeroVal())): + CopyOpBase(toGrid, grid, arrObj, coordObj, tolObj) + { + } + +protected: + void validate() const override + { + if (this->mArrayDims.size() != 3) { + std::ostringstream os; + os << "expected 3-dimensional array, found " + << this->mArrayDims.size() << "-dimensional array"; + PyErr_SetString(PyExc_ValueError, os.str().c_str()); + boost::python::throw_error_already_set(); + } + } + +#ifdef __clang__ + // Suppress "enum value not explicitly handled" warnings + PRAGMA(clang diagnostic push) + PRAGMA(clang diagnostic ignored "-Wswitch-enum") +#endif + + void copyFromArray() const override + { + switch (this->mArrayTypeId) { + case DtId::FLOAT: this->template fromArray::type>(); break; + case DtId::DOUBLE:this->template fromArray::type>();break; + case DtId::BOOL: this->template fromArray::type>(); break; + case DtId::INT16: this->template fromArray::type>(); break; + case DtId::INT32: this->template fromArray::type>(); break; + case DtId::INT64: this->template fromArray::type>(); break; + case DtId::UINT32:this->template fromArray::type>();break; + case DtId::UINT64:this->template fromArray::type>();break; + default: throw openvdb::TypeError(); break; + } + } + + void copyToArray() const override + { + switch (this->mArrayTypeId) { + case DtId::FLOAT: this->template toArray::type>(); break; + case DtId::DOUBLE: this->template toArray::type>(); break; + case DtId::BOOL: this->template toArray::type>(); break; + case DtId::INT16: this->template toArray::type>(); break; + case DtId::INT32: this->template toArray::type>(); break; + case DtId::INT64: this->template toArray::type>(); break; + case DtId::UINT32: this->template toArray::type>(); break; + case DtId::UINT64: this->template toArray::type>(); break; + default: throw openvdb::TypeError(); break; + } + } + +#ifdef __clang__ + PRAGMA(clang diagnostic pop) +#endif + +}; // class CopyOp + +// Specialization for Vec3 grids +template +class CopyOp: public CopyOpBase +{ +public: + CopyOp(bool toGrid, GridType& grid, py::object arrObj, py::object coordObj, + py::object tolObj = py::object(zeroVal())): + CopyOpBase(toGrid, grid, arrObj, coordObj, tolObj) + { + } + +protected: + void validate() const override + { + if (this->mArrayDims.size() != 4) { + std::ostringstream os; + os << "expected 4-dimensional array, found " + << this->mArrayDims.size() << "-dimensional array"; + PyErr_SetString(PyExc_ValueError, os.str().c_str()); + boost::python::throw_error_already_set(); + } + if (this->mArrayDims[3] != 3) { + std::ostringstream os; + os << "expected " << this->mArrayDims[0] << "x" << this->mArrayDims[1] + << "x" << this->mArrayDims[2] << "x3 array, found " << this->mArrayDims[0] + << "x" << this->mArrayDims[1] << "x" << this->mArrayDims[2] + << "x" << this->mArrayDims[3] << " array"; + PyErr_SetString(PyExc_ValueError, os.str().c_str()); + boost::python::throw_error_already_set(); + } + } + +#ifdef __clang__ + // Suppress "enum value not explicitly handled" warnings + PRAGMA(clang diagnostic push) + PRAGMA(clang diagnostic ignored "-Wswitch-enum") +#endif + + void copyFromArray() const override + { + switch (this->mArrayTypeId) { + case DtId::FLOAT: + this->template fromArray::type>>(); break; + case DtId::DOUBLE: + this->template fromArray::type>>(); break; + case DtId::BOOL: + this->template fromArray::type>>(); break; + case DtId::INT16: + this->template fromArray::type>>(); break; + case DtId::INT32: + this->template fromArray::type>>(); break; + case DtId::INT64: + this->template fromArray::type>>(); break; + case DtId::UINT32: + this->template fromArray::type>>(); break; + case DtId::UINT64: + this->template fromArray::type>>(); break; + default: throw openvdb::TypeError(); break; + } + } + + void copyToArray() const override + { + switch (this->mArrayTypeId) { + case DtId::FLOAT: + this->template toArray::type>>(); break; + case DtId::DOUBLE: + this->template toArray::type>>(); break; + case DtId::BOOL: + this->template toArray::type>>(); break; + case DtId::INT16: + this->template toArray::type>>(); break; + case DtId::INT32: + this->template toArray::type>>(); break; + case DtId::INT64: + this->template toArray::type>>(); break; + case DtId::UINT32: + this->template toArray::type>>(); break; + case DtId::UINT64: + this->template toArray::type>>(); break; + default: throw openvdb::TypeError(); break; + } + } + +#ifdef __clang__ + PRAGMA(clang diagnostic pop) +#endif + +}; // class CopyOp + + +template +inline void +copyFromArray(GridType& grid, py::object arrayObj, py::object coordObj, py::object toleranceObj) +{ + using ValueT = typename GridType::ValueType; + CopyOp::Size> + op(/*toGrid=*/true, grid, arrayObj, coordObj, toleranceObj); + op(); +} + + +template +inline void +copyToArray(GridType& grid, py::object arrayObj, py::object coordObj) +{ + using ValueT = typename GridType::ValueType; + CopyOp::Size> + op(/*toGrid=*/false, grid, arrayObj, coordObj); + op(); +} + + +template<> +inline void +copyFromArray(points::PointDataGrid& /*grid*/, py::object /*arrayObj*/, + py::object /*coordObj*/, py::object /*toleranceObj*/) +{ + PyErr_SetString(PyExc_NotImplementedError, + "copying NumPy arrays for PointDataGrids is not supported"); + boost::python::throw_error_already_set(); +} + + +template<> +inline void +copyToArray(points::PointDataGrid& /*grid*/, py::object /*arrayObj*/, py::object /*coordObj*/) +{ + PyErr_SetString(PyExc_NotImplementedError, + "copying NumPy arrays for PointDataGrids is not supported"); + boost::python::throw_error_already_set(); +} + + +#endif // defined(PY_OPENVDB_USE_NUMPY) + + +//////////////////////////////////////// + + +#ifndef PY_OPENVDB_USE_NUMPY + +template +inline typename GridType::Ptr +meshToLevelSet(py::object, py::object, py::object, py::object, py::object) +{ + PyErr_SetString(PyExc_NotImplementedError, "this module was built without NumPy support"); + boost::python::throw_error_already_set(); + return typename GridType::Ptr(); +} + +template +inline py::object +volumeToQuadMesh(const GridType&, py::object) +{ + PyErr_SetString(PyExc_NotImplementedError, "this module was built without NumPy support"); + boost::python::throw_error_already_set(); + return py::object(); +} + +template +inline py::object +volumeToMesh(const GridType&, py::object, py::object) +{ + PyErr_SetString(PyExc_NotImplementedError, "this module was built without NumPy support"); + boost::python::throw_error_already_set(); + return py::object(); +} + +#else // if defined(PY_OPENVDB_USE_NUMPY) + +// Helper class for meshToLevelSet() +template +struct CopyVecOp { + void operator()(const void* srcPtr, DstT* dst, size_t count) { + const SrcT* src = static_cast(srcPtr); + for (size_t i = count; i > 0; --i, ++src, ++dst) { + *dst = static_cast(*src); + } + } +}; +// Partial specialization for source and destination arrays of the same type +template +struct CopyVecOp { + void operator()(const void* srcPtr, T* dst, size_t count) { + const T* src = static_cast(srcPtr); + ::memcpy(dst, src, count * sizeof(T)); + } +}; + + +// Helper function for use with meshToLevelSet() to copy vectors of various types +// and sizes from NumPy arrays to STL vectors +template +inline void +copyVecArray(NumPyArrayType& arrayObj, std::vector& vec) +{ + using ValueT = typename VecT::ValueType; + + // Get the input array dimensions. + const auto dims = arrayDimensions(arrayObj); + const size_t M = dims.empty() ? 0 : dims[0]; + const size_t N = VecT().numElements(); + if (M == 0 || N == 0) return; + + // Preallocate the output vector. + vec.resize(M); + + // Copy values from the input array to the output vector (with type conversion, if necessary). +#ifdef PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + const void* src = arrayObj.get_data(); +#else + PyArrayObject* arrayObjPtr = reinterpret_cast(arrayObj.ptr()); + const void* src = PyArray_DATA(arrayObjPtr); +#endif + ValueT* dst = &vec[0][0]; + switch (arrayTypeId(arrayObj)) { + case DtId::FLOAT: CopyVecOp::type, ValueT>()(src, dst, M*N); break; + case DtId::DOUBLE: CopyVecOp::type, ValueT>()(src, dst, M*N); break; + case DtId::INT16: CopyVecOp::type, ValueT>()(src, dst, M*N); break; + case DtId::INT32: CopyVecOp::type, ValueT>()(src, dst, M*N); break; + case DtId::INT64: CopyVecOp::type, ValueT>()(src, dst, M*N); break; + case DtId::UINT32: CopyVecOp::type, ValueT>()(src, dst, M*N); break; + case DtId::UINT64: CopyVecOp::type, ValueT>()(src, dst, M*N); break; + default: break; + } +} + + +/// @brief Given NumPy arrays of points, triangle indices and quad indices, +/// call tools::meshToLevelSet() to generate a level set grid. +template +inline typename GridType::Ptr +meshToLevelSet(py::object pointsObj, py::object trianglesObj, py::object quadsObj, + py::object xformObj, py::object halfWidthObj) +{ + struct Local { + // Return the name of the Python grid method (for use in error messages). + static const char* methodName() { return "createLevelSetFromPolygons"; } + + // Raise a Python exception if the given NumPy array does not have dimensions M x N + // or does not have an integer or floating-point data type. + static void validate2DNumPyArray(NumPyArrayType arrayObj, + const size_t N, const char* desiredType) + { + const auto dims = arrayDimensions(arrayObj); + + bool wrongArrayType = false; + // Check array dimensions. + if (dims.size() != 2 || dims[1] != N) { + wrongArrayType = true; + } else { + // Check array data type. + switch (arrayTypeId(arrayObj)) { + case DtId::FLOAT: case DtId::DOUBLE: //case DtId::HALF: + case DtId::INT16: case DtId::INT32: case DtId::INT64: + case DtId::UINT32: case DtId::UINT64: break; + default: wrongArrayType = true; break; + } + } + if (wrongArrayType) { + // Generate an error message and raise a Python TypeError. + std::ostringstream os; + os << "expected N x 3 numpy.ndarray of " << desiredType << ", found "; + switch (dims.size()) { + case 0: os << "zero-dimensional"; break; + case 1: os << "one-dimensional"; break; + default: + os << dims[0]; + for (size_t i = 1; i < dims.size(); ++i) { os << " x " << dims[i]; } + break; + } + os << " " << arrayTypeName(arrayObj) << " array as argument 1 to " + << pyutil::GridTraits::name() << "." << methodName() << "()"; + PyErr_SetString(PyExc_TypeError, os.str().c_str()); + py::throw_error_already_set(); + } + } + }; + + // Extract the narrow band half width from the arguments to this method. + const float halfWidth = extractValueArg( + halfWidthObj, Local::methodName(), /*argIdx=*/5, "float"); + + // Extract the transform from the arguments to this method. + math::Transform::Ptr xform = math::Transform::createLinearTransform(); + if (!xformObj.is_none()) { + xform = extractValueArg( + xformObj, Local::methodName(), /*argIdx=*/4, "Transform"); + } + + // Extract the list of mesh vertices from the arguments to this method. + std::vector points; + if (!pointsObj.is_none()) { + // Extract a reference to (not a copy of) a NumPy array argument, + // or throw an exception if the argument is not a NumPy array object. + auto arrayObj = extractValueArg( + pointsObj, Local::methodName(), /*argIdx=*/1, "numpy.ndarray"); + + // Throw an exception if the array has the wrong type or dimensions. + Local::validate2DNumPyArray(arrayObj, /*N=*/3, /*desiredType=*/"float"); + + // Copy values from the array to the vector. + copyVecArray(arrayObj, points); + } + + // Extract the list of triangle indices from the arguments to this method. + std::vector triangles; + if (!trianglesObj.is_none()) { + auto arrayObj = extractValueArg( + trianglesObj, Local::methodName(), /*argIdx=*/2, "numpy.ndarray"); + Local::validate2DNumPyArray(arrayObj, /*N=*/3, /*desiredType=*/"int"); + copyVecArray(arrayObj, triangles); + } + + // Extract the list of quad indices from the arguments to this method. + std::vector quads; + if (!quadsObj.is_none()) { + auto arrayObj = extractValueArg( + quadsObj, Local::methodName(), /*argIdx=*/3, "numpy.ndarray"); + Local::validate2DNumPyArray(arrayObj, /*N=*/4, /*desiredType=*/"int"); + copyVecArray(arrayObj, quads); + } + + // Generate and return a level set grid. + return tools::meshToLevelSet(*xform, points, triangles, quads, halfWidth); +} + + +template +inline py::object +volumeToQuadMesh(const GridType& grid, py::object isovalueObj) +{ + const double isovalue = pyutil::extractArg( + isovalueObj, "convertToQuads", /*className=*/nullptr, /*argIdx=*/2, "float"); + + // Mesh the input grid and populate lists of mesh vertices and face vertex indices. + std::vector points; + std::vector quads; + tools::volumeToMesh(grid, points, quads, isovalue); + +#ifdef PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + const py::object own; + auto dtype = py::numpy::dtype::get_builtin(); + auto shape = py::make_tuple(points.size(), 3); + auto stride = py::make_tuple(3 * sizeof(Vec3s::value_type), sizeof(Vec3s::value_type)); + // Create a deep copy of the array (because the point vector will be destroyed + // when this function returns). + auto pointArrayObj = py::numpy::from_data(points.data(), dtype, shape, stride, own).copy(); + + dtype = py::numpy::dtype::get_builtin(); + shape = py::make_tuple(quads.size(), 4); + stride = py::make_tuple(4 * sizeof(Vec4I::value_type), sizeof(Vec4I::value_type)); + auto quadArrayObj = py::numpy::from_data( + quads.data(), dtype, shape, stride, own).copy(); // deep copy +#else // !defined PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + // Copy vertices into an N x 3 NumPy array. + py::object pointArrayObj = py::numeric::array(py::list(), "float32"); + if (!points.empty()) { + npy_intp dims[2] = { npy_intp(points.size()), 3 }; + // Construct a NumPy array that wraps the point vector. + if (PyArrayObject* arrayObj = reinterpret_cast( + PyArray_SimpleNewFromData(/*nd=*/2, dims, NPY_FLOAT, &points[0]))) + { + // Create a deep copy of the array (because the point vector will be + // destroyed when this function returns). + pointArrayObj = copyNumPyArray(arrayObj, NPY_CORDER); + } + } + + // Copy face indices into an N x 4 NumPy array. + py::object quadArrayObj = py::numeric::array(py::list(), "uint32"); + if (!quads.empty()) { + npy_intp dims[2] = { npy_intp(quads.size()), 4 }; + if (PyArrayObject* arrayObj = reinterpret_cast( + PyArray_SimpleNewFromData(/*dims=*/2, dims, NPY_UINT32, &quads[0]))) + { + quadArrayObj = copyNumPyArray(arrayObj, NPY_CORDER); + } + } +#endif // PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + + return py::make_tuple(pointArrayObj, quadArrayObj); +} + + +template +inline py::object +volumeToMesh(const GridType& grid, py::object isovalueObj, py::object adaptivityObj) +{ + const double isovalue = pyutil::extractArg( + isovalueObj, "convertToPolygons", /*className=*/nullptr, /*argIdx=*/2, "float"); + const double adaptivity = pyutil::extractArg( + adaptivityObj, "convertToPolygons", /*className=*/nullptr, /*argIdx=*/3, "float"); + + // Mesh the input grid and populate lists of mesh vertices and face vertex indices. + std::vector points; + std::vector triangles; + std::vector quads; + tools::volumeToMesh(grid, points, triangles, quads, isovalue, adaptivity); + +#ifdef PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + const py::object own; + auto dtype = py::numpy::dtype::get_builtin(); + auto shape = py::make_tuple(points.size(), 3); + auto stride = py::make_tuple(3 * sizeof(Vec3s::value_type), sizeof(Vec3s::value_type)); + // Create a deep copy of the array (because the point vector will be destroyed + // when this function returns). + auto pointArrayObj = py::numpy::from_data(points.data(), dtype, shape, stride, own).copy(); + + dtype = py::numpy::dtype::get_builtin(); + shape = py::make_tuple(triangles.size(), 3); + stride = py::make_tuple(3 * sizeof(Vec3I::value_type), sizeof(Vec3I::value_type)); + auto triangleArrayObj = py::numpy::from_data( + triangles.data(), dtype, shape, stride, own).copy(); // deep copy + + dtype = py::numpy::dtype::get_builtin(); + shape = py::make_tuple(quads.size(), 4); + stride = py::make_tuple(4 * sizeof(Vec4I::value_type), sizeof(Vec4I::value_type)); + auto quadArrayObj = py::numpy::from_data( + quads.data(), dtype, shape, stride, own).copy(); // deep copy +#else // !defined PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + // Copy vertices into an N x 3 NumPy array. + py::object pointArrayObj = py::numeric::array(py::list(), "float32"); + if (!points.empty()) { + npy_intp dims[2] = { npy_intp(points.size()), 3 }; + // Construct a NumPy array that wraps the point vector. + if (PyArrayObject* arrayObj = reinterpret_cast( + PyArray_SimpleNewFromData(/*dims=*/2, dims, NPY_FLOAT, &points[0]))) + { + // Create a deep copy of the array (because the point vector will be + // destroyed when this function returns). + pointArrayObj = copyNumPyArray(arrayObj, NPY_CORDER); + } + } + + // Copy triangular face indices into an N x 3 NumPy array. + py::object triangleArrayObj = py::numeric::array(py::list(), "uint32"); + if (!triangles.empty()) { + npy_intp dims[2] = { npy_intp(triangles.size()), 3 }; + if (PyArrayObject* arrayObj = reinterpret_cast( + PyArray_SimpleNewFromData(/*dims=*/2, dims, NPY_UINT32, &triangles[0]))) + { + triangleArrayObj = copyNumPyArray(arrayObj, NPY_CORDER); + } + } + + // Copy quadrilateral face indices into an N x 4 NumPy array. + py::object quadArrayObj = py::numeric::array(py::list(), "uint32"); + if (!quads.empty()) { + npy_intp dims[2] = { npy_intp(quads.size()), 4 }; + if (PyArrayObject* arrayObj = reinterpret_cast( + PyArray_SimpleNewFromData(/*dims=*/2, dims, NPY_UINT32, &quads[0]))) + { + quadArrayObj = copyNumPyArray(arrayObj, NPY_CORDER); + } + } +#endif // PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + + return py::make_tuple(pointArrayObj, triangleArrayObj, quadArrayObj); +} + +#endif // defined(PY_OPENVDB_USE_NUMPY) + + +//////////////////////////////////////// + + +template +inline void +applyMap(const char* methodName, GridType& grid, py::object funcObj) +{ + using ValueT = typename GridType::ValueType; + + for (IterType it = grid.tree().template begin(); it; ++it) { + // Evaluate the functor. + py::object result = funcObj(*it); + + // Verify that the result is of type GridType::ValueType. + py::extract val(result); + if (!val.check()) { + PyErr_Format(PyExc_TypeError, + "expected callable argument to %s.%s() to return %s, found %s", + pyutil::GridTraits::name(), + methodName, + openvdb::typeNameAsString(), + pyutil::className(result).c_str()); + py::throw_error_already_set(); + } + + it.setValue(val()); + } +} + + +template +inline void +mapOn(GridType& grid, py::object funcObj) +{ + applyMap("mapOn", grid, funcObj); +} + + +template +inline void +mapOff(GridType& grid, py::object funcObj) +{ + applyMap("mapOff", grid, funcObj); +} + + +template +inline void +mapAll(GridType& grid, py::object funcObj) +{ + applyMap("mapAll", grid, funcObj); +} + + +//////////////////////////////////////// + + +template +struct TreeCombineOp +{ + using TreeT = typename GridType::TreeType; + using ValueT = typename GridType::ValueType; + + TreeCombineOp(py::object _op): op(_op) {} + void operator()(const ValueT& a, const ValueT& b, ValueT& result) + { + py::object resultObj = op(a, b); + + py::extract val(resultObj); + if (!val.check()) { + PyErr_Format(PyExc_TypeError, + "expected callable argument to %s.combine() to return %s, found %s", + pyutil::GridTraits::name(), + openvdb::typeNameAsString(), + pyutil::className(resultObj).c_str()); + py::throw_error_already_set(); + } + + result = val(); + } + py::object op; +}; + + +template +inline void +combine(GridType& grid, py::object otherGridObj, py::object funcObj) +{ + using GridPtr = typename GridType::Ptr; + GridPtr otherGrid = extractValueArg(otherGridObj, + "combine", 1, pyutil::GridTraits::name()); + TreeCombineOp op(funcObj); + grid.tree().combine(otherGrid->tree(), op, /*prune=*/true); +} + + +//////////////////////////////////////// + + +template +inline typename GridType::Ptr +createLevelSetSphere(float radius, const openvdb::Vec3f& center, float voxelSize, float halfWidth) +{ + return tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); +} + + +//////////////////////////////////////// + + +template class IterWrap; // forward declaration + +// +// Type traits for various iterators +// +template struct IterTraits +{ + // IterT the type of the iterator + // name() function returning the base name of the iterator type (e.g., "ValueOffIter") + // descr() function returning a string describing the iterator + // begin() function returning a begin iterator for a given grid +}; + +template struct IterTraits +{ + using IterT = typename GridT::ValueOnCIter; + static std::string name() { return "ValueOnCIter"; } + static std::string descr() + { + return std::string("Read-only iterator over the active values (tile and voxel)\nof a ") + + pyutil::GridTraits::type>::name(); + } + static IterWrap begin(typename GridT::Ptr g) + { + return IterWrap(g, g->cbeginValueOn()); + } +}; // IterTraits + +template struct IterTraits +{ + using IterT = typename GridT::ValueOffCIter; + static std::string name() { return "ValueOffCIter"; } + static std::string descr() + { + return std::string("Read-only iterator over the inactive values (tile and voxel)\nof a ") + + pyutil::GridTraits::type>::name(); + } + static IterWrap begin(typename GridT::Ptr g) + { + return IterWrap(g, g->cbeginValueOff()); + } +}; // IterTraits + +template struct IterTraits +{ + using IterT = typename GridT::ValueAllCIter; + static std::string name() { return "ValueAllCIter"; } + static std::string descr() + { + return std::string("Read-only iterator over all tile and voxel values of a ") + + pyutil::GridTraits::type>::name(); + } + static IterWrap begin(typename GridT::Ptr g) + { + return IterWrap(g, g->cbeginValueAll()); + } +}; // IterTraits + +template struct IterTraits +{ + using IterT = typename GridT::ValueOnIter; + static std::string name() { return "ValueOnIter"; } + static std::string descr() + { + return std::string("Read/write iterator over the active values (tile and voxel)\nof a ") + + pyutil::GridTraits::type>::name(); + } + static IterWrap begin(typename GridT::Ptr g) + { + return IterWrap(g, g->beginValueOn()); + } +}; // IterTraits + +template struct IterTraits +{ + using IterT = typename GridT::ValueOffIter; + static std::string name() { return "ValueOffIter"; } + static std::string descr() + { + return std::string("Read/write iterator over the inactive values (tile and voxel)\nof a ") + + pyutil::GridTraits::type>::name(); + } + static IterWrap begin(typename GridT::Ptr g) + { + return IterWrap(g, g->beginValueOff()); + } +}; // IterTraits + +template struct IterTraits +{ + using IterT = typename GridT::ValueAllIter; + static std::string name() { return "ValueAllIter"; } + static std::string descr() + { + return std::string("Read/write iterator over all tile and voxel values of a ") + + pyutil::GridTraits::type>::name(); + } + static IterWrap begin(typename GridT::Ptr g) + { + return IterWrap(g, g->beginValueAll()); + } +}; // IterTraits + + +//////////////////////////////////////// + + +// Helper class to modify a grid through a non-const iterator +template +struct IterItemSetter +{ + using ValueT = typename GridT::ValueType; + static void setValue(const IterT& iter, const ValueT& val) { iter.setValue(val); } + static void setActive(const IterT& iter, bool on) { iter.setActiveState(on); } +}; + +// Partial specialization for const iterators +template +struct IterItemSetter +{ + using ValueT = typename GridT::ValueType; + static void setValue(const IterT&, const ValueT&) + { + PyErr_SetString(PyExc_AttributeError, "can't set attribute 'value'"); + py::throw_error_already_set(); + } + static void setActive(const IterT&, bool /*on*/) + { + PyErr_SetString(PyExc_AttributeError, "can't set attribute 'active'"); + py::throw_error_already_set(); + } +}; + + +/// @brief Value returned by the next() method of a grid's value iterator +/// @details This class allows both dictionary-style (e.g., items["depth"]) and +/// attribute access (e.g., items.depth) to the items returned by an iterator. +/// @todo Create a reusable base class for "named dicts" like this? +template +class IterValueProxy +{ +public: + using GridT = _GridT; + using IterT = _IterT; + using ValueT = typename GridT::ValueType; + using SetterT = IterItemSetter; + + IterValueProxy(typename GridT::ConstPtr grid, const IterT& iter): mGrid(grid), mIter(iter) {} + + IterValueProxy copy() const { return *this; } + + typename GridT::ConstPtr parent() const { return mGrid; } + + ValueT getValue() const { return *mIter; } + bool getActive() const { return mIter.isValueOn(); } + Index getDepth() const { return mIter.getDepth(); } + Coord getBBoxMin() const { return mIter.getBoundingBox().min(); } + Coord getBBoxMax() const { return mIter.getBoundingBox().max(); } + Index64 getVoxelCount() const { return mIter.getVoxelCount(); } + + void setValue(const ValueT& val) { SetterT::setValue(mIter, val); } + void setActive(bool on) { SetterT::setActive(mIter, on); } + + /// Return this dictionary's keys as a list of C strings. + static const char* const * keys() + { + static const char* const sKeys[] = { + "value", "active", "depth", "min", "max", "count", nullptr + }; + return sKeys; + } + + /// Return @c true if the given string is a valid key. + static bool hasKey(const std::string& key) + { + for (int i = 0; keys()[i] != nullptr; ++i) { + if (key == keys()[i]) return true; + } + return false; + } + + /// Return this dictionary's keys as a Python list of Python strings. + static py::list getKeys() + { + py::list keyList; + for (int i = 0; keys()[i] != nullptr; ++i) keyList.append(keys()[i]); + return keyList; + } + + /// @brief Return the value for the given key. + /// @throw KeyError if the key is invalid + py::object getItem(py::object keyObj) const + { + py::extract x(keyObj); + if (x.check()) { + const std::string key = x(); + if (key == "value") return py::object(this->getValue()); + else if (key == "active") return py::object(this->getActive()); + else if (key == "depth") return py::object(this->getDepth()); + else if (key == "min") return py::object(this->getBBoxMin()); + else if (key == "max") return py::object(this->getBBoxMax()); + else if (key == "count") return py::object(this->getVoxelCount()); + } + PyErr_SetObject(PyExc_KeyError, ("%s" % keyObj.attr("__repr__")()).ptr()); + py::throw_error_already_set(); + return py::object(); + } + + /// @brief Set the value for the given key. + /// @throw KeyError if the key is invalid + /// @throw AttributeError if the key refers to a read-only item + void setItem(py::object keyObj, py::object valObj) + { + py::extract x(keyObj); + if (x.check()) { + const std::string key = x(); + if (key == "value") { + this->setValue(py::extract(valObj)); return; + } else if (key == "active") { + this->setActive(py::extract(valObj)); return; + } else if (this->hasKey(key)) { + PyErr_SetObject(PyExc_AttributeError, + ("can't set attribute '%s'" % keyObj.attr("__repr__")()).ptr()); + py::throw_error_already_set(); + } + } + PyErr_SetObject(PyExc_KeyError, + ("'%s'" % keyObj.attr("__repr__")()).ptr()); + py::throw_error_already_set(); + } + + bool operator==(const IterValueProxy& other) const + { + return (other.getActive() == this->getActive() + && other.getDepth() == this->getDepth() + && math::isExactlyEqual(other.getValue(), this->getValue()) + && other.getBBoxMin() == this->getBBoxMin() + && other.getBBoxMax() == this->getBBoxMax() + && other.getVoxelCount() == this->getVoxelCount()); + } + bool operator!=(const IterValueProxy& other) const { return !(*this == other); } + + /// Print this dictionary to a stream. + std::ostream& put(std::ostream& os) const + { + // valuesAsStrings = ["%s: %s" % key, repr(this[key]) for key in this.keys()] + py::list valuesAsStrings; + for (int i = 0; this->keys()[i] != nullptr; ++i) { + py::str + key(this->keys()[i]), + val(this->getItem(key).attr("__repr__")()); + valuesAsStrings.append("'%s': %s" % py::make_tuple(key, val)); + } + // print ", ".join(valuesAsStrings) + py::object joined = py::str(", ").attr("join")(valuesAsStrings); + std::string s = py::extract(joined); + os << "{" << s << "}"; + return os; + } + /// Return a string describing this dictionary. + std::string info() const { std::ostringstream os; os << *this; return os.str(); } + +private: + // To keep the iterator's grid from being deleted (leaving the iterator dangling), + // store a shared pointer to the grid. + const typename GridT::ConstPtr mGrid; + const IterT mIter; // the iterator may not be incremented +}; // class IterValueProxy + + +template +inline std::ostream& +operator<<(std::ostream& os, const IterValueProxy& iv) { return iv.put(os); } + + +//////////////////////////////////////// + + +/// Wrapper for a grid's value iterator classes +template +class IterWrap +{ +public: + using GridT = _GridT; + using IterT = _IterT; + using ValueT = typename GridT::ValueType; + using IterValueProxyT = IterValueProxy; + using Traits = IterTraits; + + IterWrap(typename GridT::ConstPtr grid, const IterT& iter): mGrid(grid), mIter(iter) {} + + typename GridT::ConstPtr parent() const { return mGrid; } + + /// Return an IterValueProxy for the current iterator position. + IterValueProxyT next() + { + if (!mIter) { + PyErr_SetString(PyExc_StopIteration, "no more values"); + py::throw_error_already_set(); + } + IterValueProxyT result(mGrid, mIter); + ++mIter; + return result; + } + + static py::object returnSelf(const py::object& obj) { return obj; } + + /// @brief Define a Python wrapper class for this C++ class and another for + /// the IterValueProxy class returned by iterators of this type. + static void wrap() + { + const std::string + gridClassName = pyutil::GridTraits::type>::name(), + iterClassName = /*gridClassName +*/ Traits::name(), + valueClassName = /*gridClassName +*/ "Value"; + + py::class_( + iterClassName.c_str(), + /*docstring=*/Traits::descr().c_str(), + /*ctor=*/py::no_init) // can only be instantiated from C++, not from Python + + .add_property("parent", &IterWrap::parent, + ("the " + gridClassName + " over which to iterate").c_str()) + + .def("next", &IterWrap::next, ("next() -> " + valueClassName).c_str()) + .def("__next__", &IterWrap::next, ("__next__() -> " + valueClassName).c_str()) + .def("__iter__", &returnSelf); + + py::class_( + valueClassName.c_str(), + /*docstring=*/("Proxy for a tile or voxel value in a " + gridClassName).c_str(), + /*ctor=*/py::no_init) // can only be instantiated from C++, not from Python + + .def("copy", &IterValueProxyT::copy, + ("copy() -> " + valueClassName + "\n\n" + "Return a shallow copy of this value, i.e., one that shares\n" + "its data with the original.").c_str()) + + .add_property("parent", &IterValueProxyT::parent, + ("the " + gridClassName + " to which this value belongs").c_str()) + + .def("__str__", &IterValueProxyT::info) + .def("__repr__", &IterValueProxyT::info) + + .def("__eq__", &IterValueProxyT::operator==) + .def("__ne__", &IterValueProxyT::operator!=) + + .add_property("value", &IterValueProxyT::getValue, &IterValueProxyT::setValue, + "value of this tile or voxel") + .add_property("active", &IterValueProxyT::getActive, &IterValueProxyT::setActive, + "active state of this tile or voxel") + .add_property("depth", &IterValueProxyT::getDepth, + "tree depth at which this value is stored") + .add_property("min", &IterValueProxyT::getBBoxMin, + "lower bound of the axis-aligned bounding box of this tile or voxel") + .add_property("max", &IterValueProxyT::getBBoxMax, + "upper bound of the axis-aligned bounding box of this tile or voxel") + .add_property("count", &IterValueProxyT::getVoxelCount, + "number of voxels spanned by this value") + + .def("keys", &IterValueProxyT::getKeys, + "keys() -> list\n\n" + "Return a list of keys for this tile or voxel.") + .staticmethod("keys") + .def("__contains__", &IterValueProxyT::hasKey, + "__contains__(key) -> bool\n\n" + "Return True if the given key exists.") + .staticmethod("__contains__") + .def("__getitem__", &IterValueProxyT::getItem, + "__getitem__(key) -> value\n\n" + "Return the value of the item with the given key.") + .def("__setitem__", &IterValueProxyT::getItem, + "__setitem__(key, value)\n\n" + "Set the value of the item with the given key."); + } + +private: + // To keep this iterator's grid from being deleted, leaving the iterator dangling, + // store a shared pointer to the grid. + const typename GridT::ConstPtr mGrid; + IterT mIter; +}; // class IterWrap + + +//////////////////////////////////////// + + +template +struct PickleSuite: public py::pickle_suite +{ + using GridPtrT = typename GridT::Ptr; + + /// Return @c true, indicating that this pickler preserves a Grid's __dict__. + static bool getstate_manages_dict() { return true; } + + /// Return a tuple representing the state of the given Grid. + static py::tuple getstate(py::object gridObj) + { + py::tuple state; + + // Extract a Grid from the Python object. + GridPtrT grid; + py::extract x(gridObj); + if (x.check()) grid = x(); + + if (grid) { + // Serialize the Grid to a string. + std::ostringstream ostr(std::ios_base::binary); + { + openvdb::io::Stream strm(ostr); + strm.setGridStatsMetadataEnabled(false); + strm.write(openvdb::GridPtrVec(1, grid)); + } + // Construct a state tuple comprising the Python object's __dict__ + // and the serialized Grid. +#if PY_MAJOR_VERSION >= 3 + // Convert the byte string to a "bytes" sequence. + const std::string s = ostr.str(); + py::object bytesObj = pyutil::pyBorrow(PyBytes_FromStringAndSize(s.data(), s.size())); +#else + py::str bytesObj(ostr.str()); +#endif + state = py::make_tuple(gridObj.attr("__dict__"), bytesObj); + } + return state; + } + + /// Restore the given Grid to a saved state. + static void setstate(py::object gridObj, py::object stateObj) + { + GridPtrT grid; + { + py::extract x(gridObj); + if (x.check()) grid = x(); + } + if (!grid) return; + + py::tuple state; + { + py::extract x(stateObj); + if (x.check()) state = x(); + } + bool badState = (py::len(state) != 2); + + if (!badState) { + // Restore the object's __dict__. + py::extract x(state[0]); + if (x.check()) { + py::dict d = py::extract(gridObj.attr("__dict__"))(); + d.update(x()); + } else { + badState = true; + } + } + + std::string serialized; + if (!badState) { + // Extract the sequence containing the serialized Grid. + py::object bytesObj = state[1]; +#if PY_MAJOR_VERSION >= 3 + badState = true; + if (PyBytes_Check(bytesObj.ptr())) { + // Convert the "bytes" sequence to a byte string. + char* buf = nullptr; + Py_ssize_t length = 0; + if (-1 != PyBytes_AsStringAndSize(bytesObj.ptr(), &buf, &length)) { + if (buf != nullptr && length > 0) { + serialized.assign(buf, buf + length); + badState = false; + } + } + } +#else + py::extract x(bytesObj); + if (x.check()) serialized = x(); + else badState = true; +#endif + } + + if (badState) { + PyErr_SetObject(PyExc_ValueError, +#if PY_MAJOR_VERSION >= 3 + ("expected (dict, bytes) tuple in call to __setstate__; found %s" +#else + ("expected (dict, str) tuple in call to __setstate__; found %s" +#endif + % stateObj.attr("__repr__")()).ptr()); + py::throw_error_already_set(); + } + + // Restore the internal state of the C++ object. + GridPtrVecPtr grids; + { + std::istringstream istr(serialized, std::ios_base::binary); + io::Stream strm(istr); + grids = strm.getGrids(); // (note: file-level metadata is ignored) + } + if (grids && !grids->empty()) { + if (GridPtrT savedGrid = gridPtrCast((*grids)[0])) { + grid->MetaMap::operator=(*savedGrid); ///< @todo add a Grid::setMetadata() method? + grid->setTransform(savedGrid->transformPtr()); + grid->setTree(savedGrid->treePtr()); + } + } + } +}; // struct PickleSuite + + +//////////////////////////////////////// + + +/// Create a Python wrapper for a particular template instantiation of Grid. +template +inline void +exportGrid() +{ + using ValueT = typename GridType::ValueType; + using GridPtr = typename GridType::Ptr; + using Traits = pyutil::GridTraits; + + using ValueOnCIterT = typename GridType::ValueOnCIter; + using ValueOffCIterT = typename GridType::ValueOffCIter; + using ValueAllCIterT = typename GridType::ValueAllCIter; + using ValueOnIterT = typename GridType::ValueOnIter; + using ValueOffIterT = typename GridType::ValueOffIter; + using ValueAllIterT = typename GridType::ValueAllIter; + + math::Transform::Ptr (GridType::*getTransform)() = &GridType::transformPtr; + + const std::string pyGridTypeName = Traits::name(); + const std::string defaultCtorDescr = "Initialize with a background value of " + + pyutil::str(pyGrid::getZeroValue()) + "."; + + // Define the Grid wrapper class and make it the current scope. + { + py::class_ clss( + /*classname=*/pyGridTypeName.c_str(), + /*docstring=*/(Traits::descr()).c_str(), + /*ctor=*/py::init<>(defaultCtorDescr.c_str()) + ); + + py::scope gridClassScope = clss; + + clss.def(py::init(py::args("background"), + "Initialize with the given background value.")) + + .def("copy", &pyGrid::copyGrid, + ("copy() -> " + pyGridTypeName + "\n\n" + "Return a shallow copy of this grid, i.e., a grid\n" + "that shares its voxel data with this grid.").c_str()) + .def("deepCopy", &GridType::deepCopy, + ("deepCopy() -> " + pyGridTypeName + "\n\n" + "Return a deep copy of this grid.\n").c_str()) + + .def_pickle(pyGrid::PickleSuite()) + + .def("sharesWith", &pyGrid::sharesWith, + ("sharesWith(" + pyGridTypeName + ") -> bool\n\n" + "Return True if this grid shares its voxel data with the given grid.").c_str()) + + /// @todo Any way to set a docstring for a class property? + .add_static_property("valueTypeName", &pyGrid::getValueType) + /// @todo docstring = "name of this grid's value type" + .add_static_property("zeroValue", &pyGrid::getZeroValue) + /// @todo docstring = "zero, as expressed in this grid's value type" + .add_static_property("oneValue", &pyGrid::getOneValue) + /// @todo docstring = "one, as expressed in this grid's value type" + /// @todo Is Grid.typeName ever needed? + //.add_static_property("typeName", &GridType::gridType) + /// @todo docstring = to "name of this grid's type" + + .add_property("background", + &pyGrid::getGridBackground, &pyGrid::setGridBackground, + "value of this grid's background voxels") + .add_property("name", &GridType::getName, &pyGrid::setGridName, + "this grid's name") + .add_property("creator", &GridType::getCreator, &pyGrid::setGridCreator, + "description of this grid's creator") + + .add_property("transform", getTransform, &pyGrid::setGridTransform, + "transform associated with this grid") + + .add_property("gridClass", &pyGrid::getGridClass, &pyGrid::setGridClass, + "the class of volumetric data (level set, fog volume, etc.)\nstored in this grid") + + .add_property("vectorType", &pyGrid::getVecType, &pyGrid::setVecType, + "how transforms are applied to values stored in this grid") + + .def("getAccessor", &pyGrid::getAccessor, + ("getAccessor() -> " + pyGridTypeName + "Accessor\n\n" + "Return an accessor that provides random read and write access\n" + "to this grid's voxels.").c_str()) + .def("getConstAccessor", &pyGrid::getConstAccessor, + ("getConstAccessor() -> " + pyGridTypeName + "Accessor\n\n" + "Return an accessor that provides random read-only access\n" + "to this grid's voxels.").c_str()) + + // + // Metadata + // + .add_property("metadata", &pyGrid::getAllMetadata, &pyGrid::replaceAllMetadata, + "dict of this grid's metadata\n\n" + "Setting this attribute replaces all of this grid's metadata,\n" + "but mutating it in place has no effect on the grid, since\n" + "the value of this attribute is a only a copy of the metadata.\n" + "Use either indexing or updateMetadata() to mutate metadata in place.") + .def("updateMetadata", &pyGrid::updateMetadata, + "updateMetadata(dict)\n\n" + "Add metadata to this grid, replacing any existing items\n" + "having the same names as the new items.") + + .def("addStatsMetadata", &GridType::addStatsMetadata, + "addStatsMetadata()\n\n" + "Add metadata to this grid comprising the current values\n" + "of statistics like the active voxel count and bounding box.\n" + "(This metadata is not automatically kept up-to-date with\n" + "changes to this grid.)") + .def("getStatsMetadata", &pyGrid::getStatsMetadata, + "getStatsMetadata() -> dict\n\n" + "Return a (possibly empty) dict containing just the metadata\n" + "that was added to this grid with addStatsMetadata().") + + .def("__getitem__", &pyGrid::getMetadata, + "__getitem__(name) -> value\n\n" + "Return the metadata value associated with the given name.") + .def("__setitem__", &pyGrid::setMetadata, + "__setitem__(name, value)\n\n" + "Add metadata to this grid, replacing any existing item having\n" + "the same name as the new item.") + .def("__delitem__", &pyGrid::removeMetadata, + "__delitem__(name)\n\n" + "Remove the metadata with the given name.") + .def("__contains__", &pyGrid::hasMetadata, + "__contains__(name) -> bool\n\n" + "Return True if this grid contains metadata with the given name.") + .def("__iter__", &pyGrid::getMetadataKeys, + "__iter__() -> iterator\n\n" + "Return an iterator over this grid's metadata keys.") + .def("iterkeys", &pyGrid::getMetadataKeys, + "iterkeys() -> iterator\n\n" + "Return an iterator over this grid's metadata keys.") + + .add_property("saveFloatAsHalf", + &GridType::saveFloatAsHalf, &GridType::setSaveFloatAsHalf, + "if True, write floating-point voxel values as 16-bit half floats") + + // + // Statistics + // + .def("memUsage", &GridType::memUsage, + "memUsage() -> int\n\n" + "Return the memory usage of this grid in bytes.") + + .def("evalLeafBoundingBox", &pyGrid::evalLeafBoundingBox, + "evalLeafBoundingBox() -> xyzMin, xyzMax\n\n" + "Return the coordinates of opposite corners of the axis-aligned\n" + "bounding box of all leaf nodes.") + .def("evalLeafDim", &pyGrid::evalLeafDim, + "evalLeafDim() -> x, y, z\n\n" + "Return the dimensions of the axis-aligned bounding box\n" + "of all leaf nodes.") + + .def("evalActiveVoxelBoundingBox", &pyGrid::evalActiveVoxelBoundingBox, + "evalActiveVoxelBoundingBox() -> xyzMin, xyzMax\n\n" + "Return the coordinates of opposite corners of the axis-aligned\n" + "bounding box of all active voxels.") + .def("evalActiveVoxelDim", &GridType::evalActiveVoxelDim, + "evalActiveVoxelDim() -> x, y, z\n\n" + "Return the dimensions of the axis-aligned bounding box of all\n" + "active voxels.") + + .add_property("treeDepth", &pyGrid::treeDepth, + "depth of this grid's tree from root node to leaf node") + .def("nodeLog2Dims", &pyGrid::getNodeLog2Dims, + "list of Log2Dims of the nodes of this grid's tree\n" + "in order from root to leaf") + + .def("leafCount", &pyGrid::leafCount, + "leafCount() -> int\n\n" + "Return the number of leaf nodes in this grid's tree.") + .def("nonLeafCount", &pyGrid::nonLeafCount, + "nonLeafCount() -> int\n\n" + "Return the number of non-leaf nodes in this grid's tree.") + + .def("activeVoxelCount", &GridType::activeVoxelCount, + "activeVoxelCount() -> int\n\n" + "Return the number of active voxels in this grid.") + .def("activeLeafVoxelCount", &pyGrid::activeLeafVoxelCount, + "activeLeafVoxelCount() -> int\n\n" + "Return the number of active voxels that are stored\n" + "in the leaf nodes of this grid's tree.") + + .def("evalMinMax", &pyGrid::evalMinMax, + "evalMinMax() -> min, max\n\n" + "Return the minimum and maximum active values in this grid.") + + .def("getIndexRange", &pyGrid::getIndexRange, + "getIndexRange() -> min, max\n\n" + "Return the minimum and maximum coordinates that are represented\n" + "in this grid. These might include background voxels.") + //.def("expand", &pyGrid::expandIndexRange, + // py::arg("xyz"), + // "expand(xyz)\n\n" + // "Expand this grid's index range to include the given coordinates.") + + .def("info", &pyGrid::gridInfo, + py::arg("verbosity")=1, + "info(verbosity=1) -> str\n\n" + "Return a string containing information about this grid\n" + "with a specified level of verbosity.\n") + + // + // Tools + // + .def("fill", &pyGrid::fill, + (py::arg("min"), py::arg("max"), py::arg("value"), py::arg("active")=true), + "fill(min, max, value, active=True)\n\n" + "Set all voxels within a given axis-aligned box to\n" + "a constant value (either active or inactive).") + .def("signedFloodFill", &pyGrid::signedFloodFill, + "signedFloodFill()\n\n" + "Propagate the sign from a narrow-band level set into inactive\n" + "voxels and tiles.") + + .def("copyFromArray", &pyGrid::copyFromArray, + (py::arg("array"), py::arg("ijk")=Coord(0), + py::arg("tolerance")=pyGrid::getZeroValue()), + ("copyFromArray(array, ijk=(0, 0, 0), tolerance=0)\n\n" + "Populate this grid, starting at voxel (i, j, k), with values\nfrom a " + + std::string(openvdb::VecTraits::IsVec ? "four" : "three") + + "-dimensional array. Mark voxels as inactive\n" + "if and only if their values are equal to this grid's\n" + "background value within the given tolerance.").c_str()) + .def("copyToArray", &pyGrid::copyToArray, + (py::arg("array"), py::arg("ijk")=Coord(0)), + ("copyToArray(array, ijk=(0, 0, 0))\n\nPopulate a " + + std::string(openvdb::VecTraits::IsVec ? "four" : "three") + + "-dimensional array with values\n" + "from this grid, starting at voxel (i, j, k).").c_str()) + + .def("convertToQuads", + &pyGrid::volumeToQuadMesh, + (py::arg("isovalue")=0), + "convertToQuads(isovalue=0) -> points, quads\n\n" + "Uniformly mesh a scalar grid that has a continuous isosurface\n" + "at the given isovalue. Return a NumPy array of world-space\n" + "points and a NumPy array of 4-tuples of point indices, which\n" + "specify the vertices of the quadrilaterals that form the mesh.") + .def("convertToPolygons", + &pyGrid::volumeToMesh, + (py::arg("isovalue")=0, py::arg("adaptivity")=0), + "convertToPolygons(isovalue=0, adaptivity=0) -> points, triangles, quads\n\n" + "Adaptively mesh a scalar grid that has a continuous isosurface\n" + "at the given isovalue. Return a NumPy array of world-space\n" + "points and NumPy arrays of 3- and 4-tuples of point indices,\n" + "which specify the vertices of the triangles and quadrilaterals\n" + "that form the mesh. Adaptivity can vary from 0 to 1, where 0\n" + "produces a high-polygon-count mesh that closely approximates\n" + "the isosurface, and 1 produces a lower-polygon-count mesh\n" + "with some loss of surface detail.") + .def("createLevelSetFromPolygons", + &pyGrid::meshToLevelSet, + (py::arg("points"), + py::arg("triangles")=py::object(), + py::arg("quads")=py::object(), + py::arg("transform")=py::object(), + py::arg("halfWidth")=openvdb::LEVEL_SET_HALF_WIDTH), + ("createLevelSetFromPolygons(points, triangles=None, quads=None,\n" + " transform=None, halfWidth=" + + std::to_string(openvdb::LEVEL_SET_HALF_WIDTH) + ") -> " + + pyGridTypeName + "\n\n" + "Convert a triangle and/or quad mesh to a narrow-band level set volume.\n" + "The mesh must form a closed surface, but the surface need not be\n" + "manifold and may have self intersections and degenerate faces.\n" + "The mesh is described by a NumPy array of world-space points\n" + "and NumPy arrays of 3- and 4-tuples of point indices that specify\n" + "the vertices of the triangles and quadrilaterals that form the mesh.\n" + "Either the triangle or the quad array may be empty or None.\n" + "The resulting volume will have the given transform (or the identity\n" + "transform if no transform is given) and a narrow band width of\n" + "2 x halfWidth voxels.").c_str()) + .staticmethod("createLevelSetFromPolygons") + + .def("prune", &pyGrid::prune, + (py::arg("tolerance")=0), + "prune(tolerance=0)\n\n" + "Remove nodes whose values all have the same active state\n" + "and are equal to within a given tolerance.") + .def("pruneInactive", &pyGrid::pruneInactive, + (py::arg("value")=py::object()), + "pruneInactive(value=None)\n\n" + "Remove nodes whose values are all inactive and replace them\n" + "with either background tiles or tiles of the given value\n" + "(if the value is not None).") + + .def("empty", &GridType::empty, + "empty() -> bool\n\n" + "Return True if this grid contains only background voxels.") + .def("__nonzero__", &pyGrid::notEmpty) + + .def("clear", &GridType::clear, + "clear()\n\n" + "Remove all tiles from this grid and all nodes other than the root node.") + + .def("merge", &GridType::merge, + ("merge(" + pyGridTypeName + ")\n\n" + "Move child nodes from the other grid into this grid wherever\n" + "those nodes correspond to constant-value tiles in this grid,\n" + "and replace leaf-level inactive voxels in this grid with\n" + "corresponding voxels in the other grid that are active.\n\n" + "Note: this operation always empties the other grid.").c_str()) + + .def("mapOn", &pyGrid::mapOn, + py::arg("function"), + "mapOn(function)\n\n" + "Iterate over all the active (\"on\") values (tile and voxel)\n" + "of this grid and replace each value with function(value).\n\n" + "Example: grid.mapOn(lambda x: x * 2 if x < 0.5 else x)") + + .def("mapOff", &pyGrid::mapOff, + py::arg("function"), + "mapOff(function)\n\n" + "Iterate over all the inactive (\"off\") values (tile and voxel)\n" + "of this grid and replace each value with function(value).\n\n" + "Example: grid.mapOff(lambda x: x * 2 if x < 0.5 else x)") + + .def("mapAll", &pyGrid::mapAll, + py::arg("function"), + "mapAll(function)\n\n" + "Iterate over all values (tile and voxel) of this grid\n" + "and replace each value with function(value).\n\n" + "Example: grid.mapAll(lambda x: x * 2 if x < 0.5 else x)") + + .def("combine", &pyGrid::combine, + (py::arg("grid"), py::arg("function")), + "combine(grid, function)\n\n" + "Compute function(self, other) over all corresponding pairs\n" + "of values (tile or voxel) of this grid and the other grid\n" + "and store the result in this grid.\n\n" + "Note: this operation always empties the other grid.\n\n" + "Example: grid.combine(otherGrid, lambda a, b: min(a, b))") + + // + // Iterators + // + .def("citerOnValues", &pyGrid::IterTraits::begin, + "citerOnValues() -> iterator\n\n" + "Return a read-only iterator over this grid's active\ntile and voxel values.") + .def("citerOffValues", &pyGrid::IterTraits::begin, + "iterOffValues() -> iterator\n\n" + "Return a read-only iterator over this grid's inactive\ntile and voxel values.") + .def("citerAllValues", &pyGrid::IterTraits::begin, + "iterAllValues() -> iterator\n\n" + "Return a read-only iterator over all of this grid's\ntile and voxel values.") + + .def("iterOnValues", &pyGrid::IterTraits::begin, + "iterOnValues() -> iterator\n\n" + "Return a read/write iterator over this grid's active\ntile and voxel values.") + .def("iterOffValues", &pyGrid::IterTraits::begin, + "iterOffValues() -> iterator\n\n" + "Return a read/write iterator over this grid's inactive\ntile and voxel values.") + .def("iterAllValues", &pyGrid::IterTraits::begin, + "iterAllValues() -> iterator\n\n" + "Return a read/write iterator over all of this grid's\ntile and voxel values.") + + ; // py::class_ + + // Register the GridPtr-to-Python object converter explicitly + // if it is not already implicitly registered. + try { + py::object testObj{GridPtr()}; + } catch (py::error_already_set&) { + PyErr_Clear(); + py::register_ptr_to_python(); + } + + py::implicitly_convertible(); + py::implicitly_convertible(); + /// @todo Is there a way to implicitly convert GridType references to GridBase + /// references without wrapping the GridBase class? The following doesn't compile, + /// because GridBase has pure virtual functions: + /// @code + /// py::implicitly_convertible(); + /// @endcode + + // Wrap const and non-const value accessors and expose them + // as nested classes of the Grid class. + pyAccessor::AccessorWrap::wrap(); + pyAccessor::AccessorWrap::wrap(); + + // Wrap tree value iterators and expose them as nested classes of the Grid class. + IterWrap::wrap(); + IterWrap::wrap(); + IterWrap::wrap(); + IterWrap::wrap(); + IterWrap::wrap(); + IterWrap::wrap(); + + } // gridClassScope + + // Add the Python type object for this grid type to the module-level list. + py::extract(py::scope().attr("GridTypes"))().append( + py::scope().attr(pyGridTypeName.c_str())); +} + +} // namespace pyGrid + +#endif // OPENVDB_PYGRID_HAS_BEEN_INCLUDED diff --git a/openvdb/python/pyIntGrid.cc b/openvdb/python/pyIntGrid.cc new file mode 100644 index 00000000..c5190bad --- /dev/null +++ b/openvdb/python/pyIntGrid.cc @@ -0,0 +1,21 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file pyIntGrid.cc +/// @brief Boost.Python wrappers for scalar, integer-valued openvdb::Grid types + +#include "pyGrid.h" + + +void exportIntGrid(); + + +void +exportIntGrid() +{ + pyGrid::exportGrid(); +#ifdef PY_OPENVDB_WRAP_ALL_GRID_TYPES + pyGrid::exportGrid(); + pyGrid::exportGrid(); +#endif +} diff --git a/openvdb/python/pyMetadata.cc b/openvdb/python/pyMetadata.cc new file mode 100644 index 00000000..534fff3b --- /dev/null +++ b/openvdb/python/pyMetadata.cc @@ -0,0 +1,64 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include "openvdb/openvdb.h" + +namespace py = boost::python; +using namespace openvdb::OPENVDB_VERSION_NAME; + +namespace { + +class MetadataWrap: public Metadata, public py::wrapper +{ +public: + Name typeName() const { return static_cast(this->get_override("typeName")()); } + Metadata::Ptr copy() const { + return static_cast(this->get_override("copy")()); + } + void copy(const Metadata& other) { this->get_override("copy")(other); } + std::string str() const {return static_cast(this->get_override("str")());} + bool asBool() const { return static_cast(this->get_override("asBool")()); } + Index32 size() const { return static_cast(this->get_override("size")()); } + +protected: + void readValue(std::istream& is, Index32 numBytes) { + this->get_override("readValue")(is, numBytes); + } + void writeValue(std::ostream& os) const { + this->get_override("writeValue")(os); + } +}; + +// aliases disambiguate the different versions of copy +Metadata::Ptr (MetadataWrap::*copy0)() const = &MetadataWrap::copy; +void (MetadataWrap::*copy1)(const Metadata&) = &MetadataWrap::copy; + +} // end anonymous namespace + + +void exportMetadata(); + +void +exportMetadata() +{ + py::class_ clss( + /*classname=*/"Metadata", + /*docstring=*/ + "Class that holds the value of a single item of metadata of a type\n" + "for which no Python equivalent exists (typically a custom type)", + /*ctor=*/py::no_init // can only be instantiated from C++, not from Python + ); + clss.def("copy", py::pure_virtual(copy0), + "copy() -> Metadata\n\nReturn a copy of this value.") + .def("copy", py::pure_virtual(copy1), + "copy() -> Metadata\n\nReturn a copy of this value.") + .def("type", py::pure_virtual(&Metadata::typeName), + "type() -> str\n\nReturn the name of this value's type.") + .def("size", py::pure_virtual(&Metadata::size), + "size() -> int\n\nReturn the size of this value in bytes.") + .def("__nonzero__", py::pure_virtual(&Metadata::asBool)) + .def("__str__", py::pure_virtual(&Metadata::str)) + ; + py::register_ptr_to_python(); +} diff --git a/openvdb/python/pyOpenVDBModule.cc b/openvdb/python/pyOpenVDBModule.cc new file mode 100644 index 00000000..eada666c --- /dev/null +++ b/openvdb/python/pyOpenVDBModule.cc @@ -0,0 +1,958 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include // must be included before python on macos +#include // for strncmp(), strrchr(), etc. +#include +#include +#include // for std::make_pair() +#include +#include +#include +#ifndef DWA_BOOST_VERSION +#include +#define DWA_BOOST_VERSION (10 * BOOST_VERSION) +#endif +#if defined PY_OPENVDB_USE_NUMPY && DWA_BOOST_VERSION < 1065000 + #define PY_ARRAY_UNIQUE_SYMBOL PY_OPENVDB_ARRAY_API + #include + #ifdef NPY_1_7_API_VERSION + #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION + #endif + #include // for import_array() +#endif +#include "openvdb/openvdb.h" +#include "pyopenvdb.h" +#include "pyGrid.h" +#include "pyutil.h" + +namespace py = boost::python; + + +// Forward declarations +void exportTransform(); +void exportMetadata(); +void exportFloatGrid(); +void exportIntGrid(); +void exportVec3Grid(); +void exportPointGrid(); + + +namespace _openvdbmodule { + +using namespace openvdb; + + +/// Helper class to convert between a Python numeric sequence +/// (tuple, list, etc.) and an openvdb::Coord +struct CoordConverter +{ + /// @return a Python tuple object equivalent to the given Coord. + static PyObject* convert(const openvdb::Coord& xyz) + { + py::object obj = py::make_tuple(xyz[0], xyz[1], xyz[2]); + Py_INCREF(obj.ptr()); + ///< @todo is this the right way to ensure that the object + ///< doesn't get freed on exit? + return obj.ptr(); + } + + /// @return nullptr if the given Python object is not convertible to a Coord. + static void* convertible(PyObject* obj) + { + if (!PySequence_Check(obj)) return nullptr; // not a Python sequence + + Py_ssize_t len = PySequence_Length(obj); + if (len != 3 && len != 1) return nullptr; // not the right length + + return obj; + } + + /// Convert from a Python object to a Coord. + static void construct(PyObject* obj, + py::converter::rvalue_from_python_stage1_data* data) + { + // Construct a Coord in the provided memory location. + using StorageT = py::converter::rvalue_from_python_storage; + void* storage = reinterpret_cast(data)->storage.bytes; + new (storage) openvdb::Coord; // placement new + data->convertible = storage; + + openvdb::Coord* xyz = static_cast(storage); + + // Populate the Coord. + switch (PySequence_Length(obj)) { + case 1: + xyz->reset(pyutil::getSequenceItem(obj, 0)); + break; + case 3: + xyz->reset( + pyutil::getSequenceItem(obj, 0), + pyutil::getSequenceItem(obj, 1), + pyutil::getSequenceItem(obj, 2)); + break; + default: + PyErr_Format(PyExc_ValueError, + "expected a sequence of three integers"); + py::throw_error_already_set(); + break; + } + } + + /// Register both the Coord-to-tuple and the sequence-to-Coord converters. + static void registerConverter() + { + py::to_python_converter(); + py::converter::registry::push_back( + &CoordConverter::convertible, + &CoordConverter::construct, + py::type_id()); + } +}; // struct CoordConverter + +/// @todo CoordBBoxConverter? + + +//////////////////////////////////////// + + +/// Helper class to convert between a Python numeric sequence +/// (tuple, list, etc.) and an openvdb::Vec +template +struct VecConverter +{ + static PyObject* convert(const VecT& v) + { + py::object obj; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + switch (VecT::size) { // compile-time constant + case 2: obj = py::make_tuple(v[0], v[1]); break; + case 3: obj = py::make_tuple(v[0], v[1], v[2]); break; + case 4: obj = py::make_tuple(v[0], v[1], v[2], v[3]); break; + default: + { + py::list lst; + for (int n = 0; n < VecT::size; ++n) lst.append(v[n]); + obj = lst; + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + Py_INCREF(obj.ptr()); + return obj.ptr(); + } + + static void* convertible(PyObject* obj) + { + if (!PySequence_Check(obj)) return nullptr; // not a Python sequence + + Py_ssize_t len = PySequence_Length(obj); + if (len != VecT::size) return nullptr; + + // Check that all elements of the Python sequence are convertible + // to the Vec's value type. + py::object seq = pyutil::pyBorrow(obj); + for (int i = 0; i < VecT::size; ++i) { + if (!py::extract(seq[i]).check()) { + return nullptr; + } + } + return obj; + } + + static void construct(PyObject* obj, + py::converter::rvalue_from_python_stage1_data* data) + { + // Construct a Vec in the provided memory location. + using StorageT = py::converter::rvalue_from_python_storage; + void* storage = reinterpret_cast(data)->storage.bytes; + new (storage) VecT; // placement new + data->convertible = storage; + VecT* v = static_cast(storage); + + // Populate the vector. + for (int n = 0; n < VecT::size; ++n) { + (*v)[n] = pyutil::getSequenceItem(obj, n); + } + } + + static void registerConverter() + { + py::to_python_converter >(); + py::converter::registry::push_back( + &VecConverter::convertible, + &VecConverter::construct, + py::type_id()); + } +}; // struct VecConverter + + +//////////////////////////////////////// + + +/// Helper class to convert between a 2D Python numeric sequence +/// (tuple, list, etc.) and an openvdb::Mat +template +struct MatConverter +{ + /// Return the given matrix as a Python list of lists. + static py::object toList(const MatT& m) + { + py::list obj; + for (int i = 0; i < MatT::size; ++i) { + py::list rowObj; + for (int j = 0; j < MatT::size; ++j) { rowObj.append(m(i, j)); } + obj.append(rowObj); + } + return std::move(obj); + } + + /// Extract a matrix from a Python sequence of numeric sequences. + static MatT fromSeq(py::object obj) + { + MatT m = MatT::zero(); + if (py::len(obj) == MatT::size) { + for (int i = 0; i < MatT::size; ++i) { + py::object rowObj = obj[i]; + if (py::len(rowObj) != MatT::size) return MatT::zero(); + for (int j = 0; j < MatT::size; ++j) { + m(i, j) = py::extract(rowObj[j]); + } + } + } + return m; + } + + static PyObject* convert(const MatT& m) + { + py::object obj = toList(m); + Py_INCREF(obj.ptr()); + return obj.ptr(); + } + + static void* convertible(PyObject* obj) + { + if (!PySequence_Check(obj)) return nullptr; // not a Python sequence + + Py_ssize_t len = PySequence_Length(obj); + if (len != MatT::size) return nullptr; + + py::object seq = pyutil::pyBorrow(obj); + for (int i = 0; i < MatT::size; ++i) { + py::object rowObj = seq[i]; + if (py::len(rowObj) != MatT::size) return nullptr; + // Check that all elements of the Python sequence are convertible + // to the Mat's value type. + for (int j = 0; j < MatT::size; ++j) { + if (!py::extract(rowObj[j]).check()) { + return nullptr; + } + } + } + return obj; + } + + static void construct(PyObject* obj, + py::converter::rvalue_from_python_stage1_data* data) + { + // Construct a Mat in the provided memory location. + using StorageT = py::converter::rvalue_from_python_storage; + void* storage = reinterpret_cast(data)->storage.bytes; + new (storage) MatT; // placement new + data->convertible = storage; + *(static_cast(storage)) = fromSeq(pyutil::pyBorrow(obj)); + } + + static void registerConverter() + { + py::to_python_converter >(); + py::converter::registry::push_back( + &MatConverter::convertible, + &MatConverter::construct, + py::type_id()); + } +}; // struct MatConverter + + +//////////////////////////////////////// + + +/// Helper class to convert between a Python integer and a openvdb::PointIndex +template +struct PointIndexConverter +{ + using IntType = typename PointIndexT::IntType; + + /// @return a Python integer object equivalent to the given PointIndex. + static PyObject* convert(const PointIndexT& index) + { + py::object obj(static_cast(index)); + Py_INCREF(obj.ptr()); + return obj.ptr(); + } + + /// @return nullptr if the given Python object is not convertible to the PointIndex. + static void* convertible(PyObject* obj) + { +#if PY_MAJOR_VERSION >= 3 + if (!PyLong_Check(obj)) return nullptr; // not a Python integer +#else + if (!PyInt_Check(obj)) return nullptr; // not a Python integer +#endif + return obj; + } + + /// Convert from a Python object to a PointIndex. + static void construct(PyObject* obj, + py::converter::rvalue_from_python_stage1_data* data) + { + // Construct a PointIndex in the provided memory location. + using StorageT = py::converter::rvalue_from_python_storage; + void* storage = reinterpret_cast(data)->storage.bytes; + new (storage) PointIndexT; // placement new + data->convertible = storage; + + // Extract the PointIndex from the python integer + PointIndexT* index = static_cast(storage); +#if PY_MAJOR_VERSION >= 3 + *index = static_cast(PyLong_AsLong(obj)); +#else + *index = static_cast(PyInt_AsLong(obj)); +#endif + } + + /// Register both the PointIndex-to-integer and the integer-to-PointIndex converters. + static void registerConverter() + { + py::to_python_converter(); + py::converter::registry::push_back( + &PointIndexConverter::convertible, + &PointIndexConverter::construct, + py::type_id()); + } +}; // struct PointIndexConverter + + +//////////////////////////////////////// + + +/// Helper class to convert between a Python dict and an openvdb::MetaMap +/// @todo Consider implementing a separate, templated converter for +/// the various Metadata types. +struct MetaMapConverter +{ + static PyObject* convert(const MetaMap& metaMap) + { + py::dict ret; + for (MetaMap::ConstMetaIterator it = metaMap.beginMeta(); + it != metaMap.endMeta(); ++it) + { + if (Metadata::Ptr meta = it->second) { + py::object obj(meta); + const std::string typeName = meta->typeName(); + if (typeName == StringMetadata::staticTypeName()) { + obj = py::str(static_cast(*meta).value()); + } else if (typeName == DoubleMetadata::staticTypeName()) { + obj = py::object(static_cast(*meta).value()); + } else if (typeName == FloatMetadata::staticTypeName()) { + obj = py::object(static_cast(*meta).value()); + } else if (typeName == Int32Metadata::staticTypeName()) { + obj = py::object(static_cast(*meta).value()); + } else if (typeName == Int64Metadata::staticTypeName()) { + obj = py::object(static_cast(*meta).value()); + } else if (typeName == BoolMetadata::staticTypeName()) { + obj = py::object(static_cast(*meta).value()); + } else if (typeName == Vec2DMetadata::staticTypeName()) { + const Vec2d v = static_cast(*meta).value(); + obj = py::make_tuple(v[0], v[1]); + } else if (typeName == Vec2IMetadata::staticTypeName()) { + const Vec2i v = static_cast(*meta).value(); + obj = py::make_tuple(v[0], v[1]); + } else if (typeName == Vec2SMetadata::staticTypeName()) { + const Vec2s v = static_cast(*meta).value(); + obj = py::make_tuple(v[0], v[1]); + } else if (typeName == Vec3DMetadata::staticTypeName()) { + const Vec3d v = static_cast(*meta).value(); + obj = py::make_tuple(v[0], v[1], v[2]); + } else if (typeName == Vec3IMetadata::staticTypeName()) { + const Vec3i v = static_cast(*meta).value(); + obj = py::make_tuple(v[0], v[1], v[2]); + } else if (typeName == Vec3SMetadata::staticTypeName()) { + const Vec3s v = static_cast(*meta).value(); + obj = py::make_tuple(v[0], v[1], v[2]); + } else if (typeName == Vec4DMetadata::staticTypeName()) { + const Vec4d v = static_cast(*meta).value(); + obj = py::make_tuple(v[0], v[1], v[2], v[3]); + } else if (typeName == Vec4IMetadata::staticTypeName()) { + const Vec4i v = static_cast(*meta).value(); + obj = py::make_tuple(v[0], v[1], v[2], v[3]); + } else if (typeName == Vec4SMetadata::staticTypeName()) { + const Vec4s v = static_cast(*meta).value(); + obj = py::make_tuple(v[0], v[1], v[2], v[3]); + } else if (typeName == Mat4SMetadata::staticTypeName()) { + const Mat4s m = static_cast(*meta).value(); + obj = MatConverter::toList(m); + } else if (typeName == Mat4DMetadata::staticTypeName()) { + const Mat4d m = static_cast(*meta).value(); + obj = MatConverter::toList(m); + } + ret[it->first] = obj; + } + } + Py_INCREF(ret.ptr()); + return ret.ptr(); + } + + static void* convertible(PyObject* obj) + { + return (PyMapping_Check(obj) ? obj : nullptr); + } + + static void construct(PyObject* obj, + py::converter::rvalue_from_python_stage1_data* data) + { + // Construct a MetaMap in the provided memory location. + using StorageT = py::converter::rvalue_from_python_storage; + void* storage = reinterpret_cast(data)->storage.bytes; + new (storage) MetaMap; // placement new + data->convertible = storage; + MetaMap* metaMap = static_cast(storage); + + // Populate the map. + py::dict pyDict(pyutil::pyBorrow(obj)); + py::list keys = pyDict.keys(); + for (size_t i = 0, N = py::len(keys); i < N; ++i) { + std::string name; + py::object key = keys[i]; + if (py::extract(key).check()) { + name = py::extract(key); + } else { + const std::string + keyAsStr = py::extract(key.attr("__str__")()), + keyType = pyutil::className(key); + PyErr_Format(PyExc_TypeError, + "expected string as metadata name, found object" + " \"%s\" of type %s", keyAsStr.c_str(), keyType.c_str()); + py::throw_error_already_set(); + } + + // Note: the order of the following tests is significant, as it + // avoids unnecessary type promotion (e.g., of ints to floats). + py::object val = pyDict[keys[i]]; + Metadata::Ptr value; + if (py::extract(val).check()) { + value.reset(new StringMetadata(py::extract(val))); + } else if (bool(PyBool_Check(val.ptr()))) { + value.reset(new BoolMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + const Int64 n = py::extract(val); + if (n <= std::numeric_limits::max() + && n >= std::numeric_limits::min()) + { + value.reset(new Int32Metadata(static_cast(n))); + } else { + value.reset(new Int64Metadata(n)); + } + //} else if (py::extract(val).check()) { + // value.reset(new FloatMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new DoubleMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Vec2IMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Vec2DMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Vec2SMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Vec3IMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Vec3DMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Vec3SMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Vec4IMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Vec4DMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Vec4SMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Mat4DMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value.reset(new Mat4SMetadata(py::extract(val))); + } else if (py::extract(val).check()) { + value = py::extract(val); + } else { + const std::string + valAsStr = py::extract(val.attr("__str__")()), + valType = pyutil::className(val); + PyErr_Format(PyExc_TypeError, + "metadata value \"%s\" of type %s is not allowed", + valAsStr.c_str(), valType.c_str()); + py::throw_error_already_set(); + } + if (value) metaMap->insertMeta(name, *value); + } + } + + static void registerConverter() + { + py::to_python_converter(); + py::converter::registry::push_back( + &MetaMapConverter::convertible, + &MetaMapConverter::construct, + py::type_id()); + } +}; // struct MetaMapConverter + + +//////////////////////////////////////// + + +template void translateException(const T&) {} + +/// @brief Define a function that translates an OpenVDB exception into +/// the equivalent Python exception. +/// @details openvdb::Exception::what() typically returns a string of the form +/// ": ". To avoid duplication of the exception name in Python +/// stack traces, the function strips off the ": " prefix. To do that, +/// it needs the class name in the form of a string, hence the preprocessor macro. +#define PYOPENVDB_CATCH(_openvdbname, _pyname) \ + template<> \ + void translateException<_openvdbname>(const _openvdbname& e) \ + { \ + const char* name = #_openvdbname; \ + if (const char* c = std::strrchr(name, ':')) name = c + 1; \ + const int namelen = int(std::strlen(name)); \ + const char* msg = e.what(); \ + if (0 == std::strncmp(msg, name, namelen)) msg += namelen; \ + if (0 == std::strncmp(msg, ": ", 2)) msg += 2; \ + PyErr_SetString(_pyname, msg); \ + } + + +/// Define an overloaded function that translate all OpenVDB exceptions into +/// their Python equivalents. +/// @todo IllegalValueException and LookupError are redundant and should someday be removed. +PYOPENVDB_CATCH(openvdb::ArithmeticError, PyExc_ArithmeticError) +PYOPENVDB_CATCH(openvdb::IllegalValueException, PyExc_ValueError) +PYOPENVDB_CATCH(openvdb::IndexError, PyExc_IndexError) +PYOPENVDB_CATCH(openvdb::IoError, PyExc_IOError) +PYOPENVDB_CATCH(openvdb::KeyError, PyExc_KeyError) +PYOPENVDB_CATCH(openvdb::LookupError, PyExc_LookupError) +PYOPENVDB_CATCH(openvdb::NotImplementedError, PyExc_NotImplementedError) +PYOPENVDB_CATCH(openvdb::ReferenceError, PyExc_ReferenceError) +PYOPENVDB_CATCH(openvdb::RuntimeError, PyExc_RuntimeError) +PYOPENVDB_CATCH(openvdb::TypeError, PyExc_TypeError) +PYOPENVDB_CATCH(openvdb::ValueError, PyExc_ValueError) + +#undef PYOPENVDB_CATCH + + +//////////////////////////////////////// + + +py::object readFromFile(const std::string&, const std::string&); +py::tuple readAllFromFile(const std::string&); +py::dict readFileMetadata(const std::string&); +py::object readGridMetadataFromFile(const std::string&, const std::string&); +py::list readAllGridMetadataFromFile(const std::string&); +void writeToFile(const std::string&, py::object, py::object); + + +py::object +readFromFile(const std::string& filename, const std::string& gridName) +{ + io::File vdbFile(filename); + vdbFile.open(); + + if (!vdbFile.hasGrid(gridName)) { + PyErr_Format(PyExc_KeyError, + "file %s has no grid named \"%s\"", + filename.c_str(), gridName.c_str()); + py::throw_error_already_set(); + } + + return pyGrid::getGridFromGridBase(vdbFile.readGrid(gridName)); +} + + +py::tuple +readAllFromFile(const std::string& filename) +{ + io::File vdbFile(filename); + vdbFile.open(); + + GridPtrVecPtr grids = vdbFile.getGrids(); + MetaMap::Ptr metadata = vdbFile.getMetadata(); + vdbFile.close(); + + py::list gridList; + for (GridPtrVec::const_iterator it = grids->begin(); it != grids->end(); ++it) { + gridList.append(pyGrid::getGridFromGridBase(*it)); + } + + return py::make_tuple(gridList, py::dict(*metadata)); +} + + +py::dict +readFileMetadata(const std::string& filename) +{ + io::File vdbFile(filename); + vdbFile.open(); + + MetaMap::Ptr metadata = vdbFile.getMetadata(); + vdbFile.close(); + + return py::dict(*metadata); +} + + +py::object +readGridMetadataFromFile(const std::string& filename, const std::string& gridName) +{ + io::File vdbFile(filename); + vdbFile.open(); + + if (!vdbFile.hasGrid(gridName)) { + PyErr_Format(PyExc_KeyError, + "file %s has no grid named \"%s\"", + filename.c_str(), gridName.c_str()); + py::throw_error_already_set(); + } + + return pyGrid::getGridFromGridBase(vdbFile.readGridMetadata(gridName)); +} + + +py::list +readAllGridMetadataFromFile(const std::string& filename) +{ + io::File vdbFile(filename); + vdbFile.open(); + GridPtrVecPtr grids = vdbFile.readAllGridMetadata(); + vdbFile.close(); + + py::list gridList; + for (GridPtrVec::const_iterator it = grids->begin(); it != grids->end(); ++it) { + gridList.append(pyGrid::getGridFromGridBase(*it)); + } + return gridList; +} + + +void +writeToFile(const std::string& filename, py::object gridOrSeqObj, py::object dictObj) +{ + GridPtrVec gridVec; + try { + GridBase::Ptr base = pyopenvdb::getGridFromPyObject(gridOrSeqObj); + gridVec.push_back(base); + } catch (openvdb::TypeError&) { + for (py::stl_input_iterator it(gridOrSeqObj), end; it != end; ++it) { + if (GridBase::Ptr base = pyGrid::getGridBaseFromGrid(*it)) { + gridVec.push_back(base); + } + } + } + + io::File vdbFile(filename); + if (dictObj.is_none()) { + vdbFile.write(gridVec); + } else { + MetaMap metadata = py::extract(dictObj); + vdbFile.write(gridVec, metadata); + } + vdbFile.close(); +} + + +//////////////////////////////////////// + + +std::string getLoggingLevel(); +void setLoggingLevel(py::object); +void setProgramName(py::object, bool); + + +std::string +getLoggingLevel() +{ + switch (logging::getLevel()) { + case logging::Level::Debug: return "debug"; + case logging::Level::Info: return "info"; + case logging::Level::Warn: return "warn"; + case logging::Level::Error: return "error"; + case logging::Level::Fatal: break; + } + return "fatal"; +} + + +void +setLoggingLevel(py::object pyLevelObj) +{ + std::string levelStr; + if (!py::extract(pyLevelObj).check()) { + levelStr = py::extract(pyLevelObj.attr("__str__")()); + } else { + const py::str pyLevelStr = + py::extract(pyLevelObj.attr("lower")().attr("lstrip")("-")); + levelStr = py::extract(pyLevelStr); + if (levelStr == "debug") { logging::setLevel(logging::Level::Debug); return; } + else if (levelStr == "info") { logging::setLevel(logging::Level::Info); return; } + else if (levelStr == "warn") { logging::setLevel(logging::Level::Warn); return; } + else if (levelStr == "error") { logging::setLevel(logging::Level::Error); return; } + else if (levelStr == "fatal") { logging::setLevel(logging::Level::Fatal); return; } + } + PyErr_Format(PyExc_ValueError, + "expected logging level \"debug\", \"info\", \"warn\", \"error\", or \"fatal\"," + " got \"%s\"", levelStr.c_str()); + py::throw_error_already_set(); +} + + +void +setProgramName(py::object nameObj, bool color) +{ + if (py::extract(nameObj).check()) { + logging::setProgramName(py::extract(nameObj), color); + } else { + const std::string + str = py::extract(nameObj.attr("__str__")()), + typ = pyutil::className(nameObj).c_str(); + PyErr_Format(PyExc_TypeError, + "expected string as program name, got \"%s\" of type %s", + str.c_str(), typ.c_str()); + py::throw_error_already_set(); + } +} + + +//////////////////////////////////////// + + +// Descriptor for the openvdb::GridClass enum (for use with pyutil::StringEnum) +struct GridClassDescr +{ + static const char* name() { return "GridClass"; } + static const char* doc() + { + return "Classes of volumetric data (level set, fog volume, etc.)"; + } + static pyutil::CStringPair item(int i) + { + static const int sCount = 4; + static const char* const sStrings[sCount][2] = { + { "UNKNOWN", strdup(GridBase::gridClassToString(GRID_UNKNOWN).c_str()) }, + { "LEVEL_SET", strdup(GridBase::gridClassToString(GRID_LEVEL_SET).c_str()) }, + { "FOG_VOLUME", strdup(GridBase::gridClassToString(GRID_FOG_VOLUME).c_str()) }, + { "STAGGERED", strdup(GridBase::gridClassToString(GRID_STAGGERED).c_str()) } + }; + if (i >= 0 && i < sCount) return pyutil::CStringPair(&sStrings[i][0], &sStrings[i][1]); + return pyutil::CStringPair(static_cast(nullptr), static_cast(nullptr)); + } +}; + + +// Descriptor for the openvdb::VecType enum (for use with pyutil::StringEnum) +struct VecTypeDescr +{ + static const char* name() { return "VectorType"; } + static const char* doc() + { + return + "The type of a vector determines how transforms are applied to it.\n" + " - INVARIANT:\n" + " does not transform (e.g., tuple, uvw, color)\n" + " - COVARIANT:\n" + " apply inverse-transpose transformation with w = 0\n" + " and ignore translation (e.g., gradient/normal)\n" + " - COVARIANT_NORMALIZE:\n" + " apply inverse-transpose transformation with w = 0\n" + " and ignore translation, vectors are renormalized\n" + " (e.g., unit normal)\n" + " - CONTRAVARIANT_RELATIVE:\n" + " apply \"regular\" transformation with w = 0 and ignore\n" + " translation (e.g., displacement, velocity, acceleration)\n" + " - CONTRAVARIANT_ABSOLUTE:\n" + " apply \"regular\" transformation with w = 1 so that\n" + " vector translates (e.g., position)\n"; + } + static pyutil::CStringPair item(int i) + { + static const int sCount = 5; + static const char* const sStrings[sCount][2] = { + { "INVARIANT", strdup(GridBase::vecTypeToString(openvdb::VEC_INVARIANT).c_str()) }, + { "COVARIANT", strdup(GridBase::vecTypeToString(openvdb::VEC_COVARIANT).c_str()) }, + { "COVARIANT_NORMALIZE", + strdup(GridBase::vecTypeToString(openvdb::VEC_COVARIANT_NORMALIZE).c_str()) }, + { "CONTRAVARIANT_RELATIVE", + strdup(GridBase::vecTypeToString(openvdb::VEC_CONTRAVARIANT_RELATIVE).c_str()) }, + { "CONTRAVARIANT_ABSOLUTE", + strdup(GridBase::vecTypeToString(openvdb::VEC_CONTRAVARIANT_ABSOLUTE).c_str()) } + }; + if (i >= 0 && i < sCount) return std::make_pair(&sStrings[i][0], &sStrings[i][1]); + return pyutil::CStringPair(static_cast(nullptr), static_cast(nullptr)); + } +}; + +} // namespace _openvdbmodule + + +//////////////////////////////////////// + + +#ifdef DWA_OPENVDB +#define PY_OPENVDB_MODULE_NAME _openvdb +extern "C" { void init_openvdb(); } +#else +#define PY_OPENVDB_MODULE_NAME pyopenvdb +extern "C" { void initpyopenvdb(); } +#endif + +BOOST_PYTHON_MODULE(PY_OPENVDB_MODULE_NAME) +{ + // Don't auto-generate ugly, C++-style function signatures. + py::docstring_options docOptions; + docOptions.disable_signatures(); + docOptions.enable_user_defined(); + +#ifdef PY_OPENVDB_USE_NUMPY + // Initialize NumPy. +#ifdef PY_OPENVDB_USE_BOOST_PYTHON_NUMPY + boost::python::numpy::initialize(); +#else +#if PY_MAJOR_VERSION >= 3 + if (_import_array()) {} +#else + import_array(); +#endif +#endif +#endif + + using namespace openvdb::OPENVDB_VERSION_NAME; + + // Initialize OpenVDB. + initialize(); + + _openvdbmodule::CoordConverter::registerConverter(); + + _openvdbmodule::VecConverter::registerConverter(); + _openvdbmodule::VecConverter::registerConverter(); + _openvdbmodule::VecConverter::registerConverter(); + _openvdbmodule::VecConverter::registerConverter(); + + _openvdbmodule::VecConverter::registerConverter(); + _openvdbmodule::VecConverter::registerConverter(); + _openvdbmodule::VecConverter::registerConverter(); + _openvdbmodule::VecConverter::registerConverter(); + + _openvdbmodule::VecConverter::registerConverter(); + _openvdbmodule::VecConverter::registerConverter(); + _openvdbmodule::VecConverter::registerConverter(); + _openvdbmodule::VecConverter::registerConverter(); + + _openvdbmodule::MatConverter::registerConverter(); + _openvdbmodule::MatConverter::registerConverter(); + + _openvdbmodule::PointIndexConverter::registerConverter(); + + _openvdbmodule::MetaMapConverter::registerConverter(); + +#define PYOPENVDB_TRANSLATE_EXCEPTION(_classname) \ + py::register_exception_translator<_classname>(&_openvdbmodule::translateException<_classname>) + + PYOPENVDB_TRANSLATE_EXCEPTION(ArithmeticError); + PYOPENVDB_TRANSLATE_EXCEPTION(IllegalValueException); + PYOPENVDB_TRANSLATE_EXCEPTION(IndexError); + PYOPENVDB_TRANSLATE_EXCEPTION(IoError); + PYOPENVDB_TRANSLATE_EXCEPTION(KeyError); + PYOPENVDB_TRANSLATE_EXCEPTION(LookupError); + PYOPENVDB_TRANSLATE_EXCEPTION(NotImplementedError); + PYOPENVDB_TRANSLATE_EXCEPTION(ReferenceError); + PYOPENVDB_TRANSLATE_EXCEPTION(RuntimeError); + PYOPENVDB_TRANSLATE_EXCEPTION(TypeError); + PYOPENVDB_TRANSLATE_EXCEPTION(ValueError); + +#undef PYOPENVDB_TRANSLATE_EXCEPTION + + + // Export the python bindings. + exportTransform(); + exportMetadata(); + exportFloatGrid(); + exportIntGrid(); + exportVec3Grid(); + exportPointGrid(); + + + py::def("read", + &_openvdbmodule::readFromFile, + (py::arg("filename"), py::arg("gridname")), + "read(filename, gridname) -> Grid\n\n" + "Read a single grid from a .vdb file."); + + py::def("readAll", + &_openvdbmodule::readAllFromFile, + py::arg("filename"), + "readAll(filename) -> list, dict\n\n" + "Read a .vdb file and return a list of grids and\n" + "a dict of file-level metadata."); + + py::def("readMetadata", + &_openvdbmodule::readFileMetadata, + py::arg("filename"), + "readMetadata(filename) -> dict\n\n" + "Read file-level metadata from a .vdb file."); + + py::def("readGridMetadata", + &_openvdbmodule::readGridMetadataFromFile, + (py::arg("filename"), py::arg("gridname")), + "readGridMetadata(filename, gridname) -> Grid\n\n" + "Read a single grid's metadata and transform (but not its tree)\n" + "from a .vdb file."); + + py::def("readAllGridMetadata", + &_openvdbmodule::readAllGridMetadataFromFile, + py::arg("filename"), + "readAllGridMetadata(filename) -> list\n\n" + "Read a .vdb file and return a list of grids populated with\n" + "their metadata and transforms, but not their trees."); + + py::def("write", + &_openvdbmodule::writeToFile, + (py::arg("filename"), py::arg("grids"), py::arg("metadata") = py::object()), + "write(filename, grids, metadata=None)\n\n" + "Write a grid or a sequence of grids and, optionally, a dict\n" + "of (name, value) metadata pairs to a .vdb file."); + + py::def("getLoggingLevel", &_openvdbmodule::getLoggingLevel, + "getLoggingLevel() -> str\n\n" + "Return the severity threshold (\"debug\", \"info\", \"warn\", \"error\",\n" + "or \"fatal\") for error messages."); + py::def("setLoggingLevel", &_openvdbmodule::setLoggingLevel, + (py::arg("level")), + "setLoggingLevel(level)\n\n" + "Specify the severity threshold (\"debug\", \"info\", \"warn\", \"error\",\n" + "or \"fatal\") for error messages. Messages of lower severity\n" + "will be suppressed."); + py::def("setProgramName", &_openvdbmodule::setProgramName, + (py::arg("name"), py::arg("color") = true), + "setProgramName(name, color=True)\n\n" + "Specify the program name to be displayed in error messages,\n" + "and optionally specify whether to print error messages in color."); + + // Add some useful module-level constants. + py::scope().attr("LIBRARY_VERSION") = py::make_tuple( + openvdb::OPENVDB_LIBRARY_MAJOR_VERSION, + openvdb::OPENVDB_LIBRARY_MINOR_VERSION, + openvdb::OPENVDB_LIBRARY_PATCH_VERSION); + py::scope().attr("FILE_FORMAT_VERSION") = openvdb::OPENVDB_FILE_VERSION; + py::scope().attr("COORD_MIN") = openvdb::Coord::min(); + py::scope().attr("COORD_MAX") = openvdb::Coord::max(); + py::scope().attr("LEVEL_SET_HALF_WIDTH") = openvdb::LEVEL_SET_HALF_WIDTH; + + pyutil::StringEnum<_openvdbmodule::GridClassDescr>::wrap(); + pyutil::StringEnum<_openvdbmodule::VecTypeDescr>::wrap(); + +} // BOOST_PYTHON_MODULE diff --git a/openvdb/python/pyPointGrid.cc b/openvdb/python/pyPointGrid.cc new file mode 100644 index 00000000..a5902305 --- /dev/null +++ b/openvdb/python/pyPointGrid.cc @@ -0,0 +1,23 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file pyPointGrid.cc +/// @brief Boost.Python wrappers for point openvdb::Grid types + +#include + +#include "pyGrid.h" + +namespace py = boost::python; + + +void exportPointGrid(); + + +void +exportPointGrid() +{ +#ifdef PY_OPENVDB_WRAP_ALL_GRID_TYPES + pyGrid::exportGrid(); +#endif +} diff --git a/openvdb/python/pyTransform.cc b/openvdb/python/pyTransform.cc new file mode 100644 index 00000000..a4f6fa1f --- /dev/null +++ b/openvdb/python/pyTransform.cc @@ -0,0 +1,326 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include "openvdb/openvdb.h" +#include "pyutil.h" + +namespace py = boost::python; +using namespace openvdb::OPENVDB_VERSION_NAME; + +namespace pyTransform { + +inline void scale1(math::Transform& t, double s) { t.preScale(s); } +inline void scale3(math::Transform& t, const Vec3d& xyz) { t.preScale(xyz); } + +inline Vec3d voxelDim0(math::Transform& t) { return t.voxelSize(); } +inline Vec3d voxelDim1(math::Transform& t, const Vec3d& p) { return t.voxelSize(p); } + +inline double voxelVolume0(math::Transform& t) { return t.voxelVolume(); } +inline double voxelVolume1(math::Transform& t, const Vec3d& p) { return t.voxelVolume(p); } + +inline Vec3d indexToWorld(math::Transform& t, const Vec3d& p) { return t.indexToWorld(p); } +inline Vec3d worldToIndex(math::Transform& t, const Vec3d& p) { return t.worldToIndex(p); } + +inline Coord worldToIndexCellCentered(math::Transform& t, const Vec3d& p) { + return t.worldToIndexCellCentered(p); +} +inline Coord worldToIndexNodeCentered(math::Transform& t, const Vec3d& p) { + return t.worldToIndexNodeCentered(p); +} + + +inline std::string +info(math::Transform& t) +{ + std::ostringstream ostr; + t.print(ostr); + return ostr.str(); +} + + +inline math::Transform::Ptr +createLinearFromDim(double dim) +{ + return math::Transform::createLinearTransform(dim); +} + + +inline math::Transform::Ptr +createLinearFromMat(py::object obj) +{ + Mat4R m; + + // Verify that obj is a four-element sequence. + bool is4x4Seq = (PySequence_Check(obj.ptr()) && PySequence_Length(obj.ptr()) == 4); + if (is4x4Seq) { + for (int row = 0; is4x4Seq && row < 4; ++row) { + // Verify that each element of obj is itself a four-element sequence. + py::object rowObj = obj[row]; + if (PySequence_Check(rowObj.ptr()) && PySequence_Length(rowObj.ptr()) == 4) { + // Extract four numeric values from this row of the sequence. + for (int col = 0; is4x4Seq && col < 4; ++col) { + if (py::extract(rowObj[col]).check()) { + m[row][col] = py::extract(rowObj[col]); + } else { + is4x4Seq = false; + } + } + } else { + is4x4Seq = false; + } + } + } + if (!is4x4Seq) { + PyErr_Format(PyExc_ValueError, "expected a 4 x 4 sequence of numeric values"); + py::throw_error_already_set(); + } + + return math::Transform::createLinearTransform(m); +} + + +inline math::Transform::Ptr +createFrustum(const Coord& xyzMin, const Coord& xyzMax, + double taper, double depth, double voxelDim = 1.0) +{ + return math::Transform::createFrustumTransform( + BBoxd(xyzMin.asVec3d(), xyzMax.asVec3d()), taper, depth, voxelDim); +} + + +//////////////////////////////////////// + + +struct PickleSuite: public py::pickle_suite +{ + enum { STATE_DICT = 0, STATE_MAJOR, STATE_MINOR, STATE_FORMAT, STATE_XFORM }; + + /// Return @c true, indicating that this pickler preserves a Transform's __dict__. + static bool getstate_manages_dict() { return true; } + + /// Return a tuple representing the state of the given Transform. + static py::tuple getstate(py::object xformObj) + { + py::tuple state; + py::extract x(xformObj); + if (x.check()) { + // Extract a Transform from the Python object. + math::Transform xform = x(); + std::ostringstream ostr(std::ios_base::binary); + // Serialize the Transform to a string. + xform.write(ostr); + + // Construct a state tuple comprising the Python object's __dict__, + // the version numbers of the serialization format, + // and the serialized Transform. +#if PY_MAJOR_VERSION >= 3 + // Convert the byte string to a "bytes" sequence. + const std::string s = ostr.str(); + py::object bytesObj = pyutil::pyBorrow(PyBytes_FromStringAndSize(s.data(), s.size())); +#else + py::str bytesObj(ostr.str()); +#endif + state = py::make_tuple( + xformObj.attr("__dict__"), + uint32_t(OPENVDB_LIBRARY_MAJOR_VERSION), + uint32_t(OPENVDB_LIBRARY_MINOR_VERSION), + uint32_t(OPENVDB_FILE_VERSION), + bytesObj); + } + return state; + } + + /// Restore the given Transform to a saved state. + static void setstate(py::object xformObj, py::object stateObj) + { + math::Transform* xform = nullptr; + { + py::extract x(xformObj); + if (x.check()) xform = x(); + else return; + } + + py::tuple state; + { + py::extract x(stateObj); + if (x.check()) state = x(); + } + bool badState = (py::len(state) != 5); + + if (!badState) { + // Restore the object's __dict__. + py::extract x(state[int(STATE_DICT)]); + if (x.check()) { + py::dict d = py::extract(xformObj.attr("__dict__"))(); + d.update(x()); + } else { + badState = true; + } + } + + openvdb::VersionId libVersion; + uint32_t formatVersion = 0; + if (!badState) { + // Extract the serialization format version numbers. + const int idx[3] = { STATE_MAJOR, STATE_MINOR, STATE_FORMAT }; + uint32_t version[3] = { 0, 0, 0 }; + for (int i = 0; i < 3 && !badState; ++i) { + py::extract x(state[idx[i]]); + if (x.check()) version[i] = x(); + else badState = true; + } + libVersion.first = version[0]; + libVersion.second = version[1]; + formatVersion = version[2]; + } + + std::string serialized; + if (!badState) { + // Extract the sequence containing the serialized Transform. + py::object bytesObj = state[int(STATE_XFORM)]; +#if PY_MAJOR_VERSION >= 3 + badState = true; + if (PyBytes_Check(bytesObj.ptr())) { + // Convert the "bytes" sequence to a byte string. + char* buf = NULL; + Py_ssize_t length = 0; + if (-1 != PyBytes_AsStringAndSize(bytesObj.ptr(), &buf, &length)) { + if (buf != NULL && length > 0) { + serialized.assign(buf, buf + length); + badState = false; + } + } + } +#else + py::extract x(bytesObj); + if (x.check()) serialized = x(); + else badState = true; +#endif + } + + if (badState) { + PyErr_SetObject(PyExc_ValueError, +#if PY_MAJOR_VERSION >= 3 + ("expected (dict, int, int, int, bytes) tuple in call to __setstate__; found %s" +#else + ("expected (dict, int, int, int, str) tuple in call to __setstate__; found %s" +#endif + % stateObj.attr("__repr__")()).ptr()); + py::throw_error_already_set(); + } + + // Restore the internal state of the C++ object. + std::istringstream istr(serialized, std::ios_base::binary); + io::setVersion(istr, libVersion, formatVersion); + xform->read(istr); + } +}; // struct PickleSuite + +} // namespace pyTransform + + +void exportTransform(); + +void +exportTransform() +{ + py::enum_("Axis") + .value("X", math::X_AXIS) + .value("Y", math::Y_AXIS) + .value("Z", math::Z_AXIS); + + py::class_("Transform", py::init<>()) + + .def("deepCopy", &math::Transform::copy, + "deepCopy() -> Transform\n\n" + "Return a copy of this transform.") + + /// @todo Should this also be __str__()? + .def("info", &pyTransform::info, + "info() -> str\n\n" + "Return a string containing a description of this transform.\n") + + .def_pickle(pyTransform::PickleSuite()) + + .add_property("typeName", &math::Transform::mapType, + "name of this transform's type") + .add_property("isLinear", &math::Transform::isLinear, + "True if this transform is linear") + + .def("rotate", &math::Transform::preRotate, + (py::arg("radians"), py::arg("axis") = math::X_AXIS), + "rotate(radians, axis)\n\n" + "Accumulate a rotation about either Axis.X, Axis.Y or Axis.Z.") + .def("translate", &math::Transform::postTranslate, py::arg("xyz"), + "translate((x, y, z))\n\n" + "Accumulate a translation.") + .def("scale", &pyTransform::scale1, py::arg("s"), + "scale(s)\n\n" + "Accumulate a uniform scale.") + .def("scale", &pyTransform::scale3, py::arg("sxyz"), + "scale((sx, sy, sz))\n\n" + "Accumulate a nonuniform scale.") + .def("shear", &math::Transform::preShear, + (py::arg("s"), py::arg("axis0"), py::arg("axis1")), + "shear(s, axis0, axis1)\n\n" + "Accumulate a shear (axis0 and axis1 are either\n" + "Axis.X, Axis.Y or Axis.Z).") + + .def("voxelSize", &pyTransform::voxelDim0, + "voxelSize() -> (dx, dy, dz)\n\n" + "Return the size of voxels of the linear component of this transform.") + .def("voxelSize", &pyTransform::voxelDim1, py::arg("xyz"), + "voxelSize((x, y, z)) -> (dx, dy, dz)\n\n" + "Return the size of the voxel at position (x, y, z).") + + .def("voxelVolume", &pyTransform::voxelVolume0, + "voxelVolume() -> float\n\n" + "Return the voxel volume of the linear component of this transform.") + .def("voxelVolume", &pyTransform::voxelVolume1, py::arg("xyz"), + "voxelVolume((x, y, z)) -> float\n\n" + "Return the voxel volume at position (x, y, z).") + + .def("indexToWorld", &pyTransform::indexToWorld, py::arg("xyz"), + "indexToWorld((x, y, z)) -> (x', y', z')\n\n" + "Apply this transformation to the given coordinates.") + .def("worldToIndex", &pyTransform::worldToIndex, py::arg("xyz"), + "worldToIndex((x, y, z)) -> (x', y', z')\n\n" + "Apply the inverse of this transformation to the given coordinates.") + .def("worldToIndexCellCentered", &pyTransform::worldToIndexCellCentered, + py::arg("xyz"), + "worldToIndexCellCentered((x, y, z)) -> (i, j, k)\n\n" + "Apply the inverse of this transformation to the given coordinates\n" + "and round the result to the nearest integer coordinates.") + .def("worldToIndexNodeCentered", &pyTransform::worldToIndexNodeCentered, + py::arg("xyz"), + "worldToIndexNodeCentered((x, y, z)) -> (i, j, k)\n\n" + "Apply the inverse of this transformation to the given coordinates\n" + "and round the result down to the nearest integer coordinates.") + + // Allow Transforms to be compared for equality and inequality. + .def(py::self == py::other()) + .def(py::self != py::other()) + ; + + py::def("createLinearTransform", &pyTransform::createLinearFromMat, py::arg("matrix"), + "createLinearTransform(matrix) -> Transform\n\n" + "Create a new linear transform from a 4 x 4 matrix given as a sequence\n" + "of the form [[a, b, c, d], [e, f, g, h], [i, j, k, l], [m, n, o, p]],\n" + "where [m, n, o, p] is the translation component."); + + py::def("createLinearTransform", &pyTransform::createLinearFromDim, + (py::arg("voxelSize") = 1.0), + "createLinearTransform(voxelSize) -> Transform\n\n" + "Create a new linear transform with the given uniform voxel size."); + + py::def("createFrustumTransform", &pyTransform::createFrustum, + (py::arg("xyzMin"), py::arg("xyzMax"), + py::arg("taper"), py::arg("depth"), py::arg("voxelSize") = 1.0), + "createFrustumTransform(xyzMin, xyzMax, taper, depth, voxelSize) -> Transform\n\n" + "Create a new frustum transform with unit bounding box (xyzMin, xyzMax)\n" + "and the given taper, depth and uniform voxel size."); + + // allows Transform::Ptr Grid::getTransform() to work + py::register_ptr_to_python(); +} diff --git a/openvdb/python/pyVec3Grid.cc b/openvdb/python/pyVec3Grid.cc new file mode 100644 index 00000000..09e98e00 --- /dev/null +++ b/openvdb/python/pyVec3Grid.cc @@ -0,0 +1,21 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file pyVec3Grid.cc +/// @brief Boost.Python wrappers for vector-valued openvdb::Grid types + +#include "pyGrid.h" + + +void exportVec3Grid(); + + +void +exportVec3Grid() +{ + pyGrid::exportGrid(); +#ifdef PY_OPENVDB_WRAP_ALL_GRID_TYPES + pyGrid::exportGrid(); + pyGrid::exportGrid(); +#endif +} diff --git a/openvdb/python/pyopenvdb.h b/openvdb/python/pyopenvdb.h new file mode 100644 index 00000000..0acbf9bf --- /dev/null +++ b/openvdb/python/pyopenvdb.h @@ -0,0 +1,82 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file pyopenvdb.h +/// +/// @brief Glue functions for access to pyOpenVDB objects from C++ code +/// @details Use these functions in your own Python function implementations +/// to extract an OpenVDB grid from or wrap a grid in a @c PyObject. +/// For example (using Boost.Python), +/// @code +/// #include +/// #include +/// #include +/// +/// // Implementation of a Python function that processes pyOpenVDB grids +/// boost::python::object +/// processGrid(boost::python::object inObj) +/// { +/// boost::python::object outObj; +/// try { +/// // Extract an OpenVDB grid from the input argument. +/// if (openvdb::GridBase::Ptr grid = +/// pyopenvdb::getGridFromPyObject(inObj)) +/// { +/// grid = grid->deepCopyGrid(); +/// +/// // Process the grid... +/// +/// // Wrap the processed grid in a PyObject. +/// outObj = pyopenvdb::getPyObjectFromGrid(grid); +/// } +/// } catch (openvdb::TypeError& e) { +/// PyErr_Format(PyExc_TypeError, e.what()); +/// boost::python::throw_error_already_set(); +/// } +/// return outObj; +/// } +/// +/// BOOST_PYTHON_MODULE(mymodule) +/// { +/// openvdb::initialize(); +/// +/// // Definition of a Python function that processes pyOpenVDB grids +/// boost::python::def(/*name=*/"processGrid", &processGrid, /*argname=*/"grid"); +/// } +/// @endcode +/// Then, from Python, +/// @code +/// import openvdb +/// import mymodule +/// +/// grid = openvdb.read('myGrid.vdb', 'MyGrid') +/// grid = mymodule.processGrid(grid) +/// openvdb.write('myProcessedGrid.vdb', [grid]) +/// @endcode + +#ifndef PYOPENVDB_HAS_BEEN_INCLUDED +#define PYOPENVDB_HAS_BEEN_INCLUDED + +#include +#include "openvdb/Grid.h" + + +namespace pyopenvdb { + +//@{ +/// @brief Return a pointer to the OpenVDB grid held by the given Python object. +/// @throw openvdb::TypeError if the Python object is not one of the pyOpenVDB grid types. +/// (See the Python module's GridTypes global variable for the list of supported grid types.) +openvdb::GridBase::Ptr getGridFromPyObject(PyObject*); +openvdb::GridBase::Ptr getGridFromPyObject(const boost::python::object&); +//@} + +/// @brief Return a new Python object that holds the given OpenVDB grid. +/// @return @c None if the given grid pointer is null. +/// @throw openvdb::TypeError if the grid is not of a supported type. +/// (See the Python module's GridTypes global variable for the list of supported grid types.) +boost::python::object getPyObjectFromGrid(const openvdb::GridBase::Ptr&); + +} // namespace pyopenvdb + +#endif // PYOPENVDB_HAS_BEEN_INCLUDED diff --git a/openvdb/python/pyutil.h b/openvdb/python/pyutil.h new file mode 100644 index 00000000..4fd63960 --- /dev/null +++ b/openvdb/python/pyutil.h @@ -0,0 +1,255 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_PYUTIL_HAS_BEEN_INCLUDED +#define OPENVDB_PYUTIL_HAS_BEEN_INCLUDED + +#include "openvdb/openvdb.h" +#include "openvdb/points/PointDataGrid.h" +#include +#include +#include // for std::pair +#include +#include + + +namespace pyutil { + +/// Return a new @c boost::python::object that borrows (i.e., doesn't +/// take over ownership of) the given @c PyObject's reference. +inline boost::python::object +pyBorrow(PyObject* obj) +{ + return boost::python::object(boost::python::handle<>(boost::python::borrowed(obj))); +} + + +/// @brief Given a @c PyObject that implements the sequence protocol +/// (e.g., a @c PyListObject), return the value of type @c ValueT +/// at index @a idx in the sequence. +/// @details Raise a Python @c TypeError exception if the value +/// at index @a idx is not convertible to type @c ValueT. +template +inline ValueT +getSequenceItem(PyObject* obj, int idx) +{ + return boost::python::extract(pyBorrow(obj)[idx]); +} + + +//////////////////////////////////////// + + +template +struct GridTraitsBase +{ + /// @brief Return the name of the Python class that wraps this grid type + /// (e.g., "FloatGrid" for openvdb::FloatGrid). + /// + /// @note This name is not the same as GridType::type(). + /// The latter returns a name like "Tree_float_5_4_3". + static const char* name(); + + /// Return the name of this grid type's value type ("bool", "float", "vec3s", etc.). + static const char* valueTypeName() + { + return openvdb::typeNameAsString(); + } + + /// @brief Return a description of this grid type. + /// + /// @note This name is generated at runtime for each call to descr(). + static const std::string descr() + { + return std::string("OpenVDB grid with voxels of type ") + valueTypeName(); + } +}; // struct GridTraitsBase + + +template +struct GridTraits: public GridTraitsBase +{ +}; + +/// Map a grid type to a traits class that derives from GridTraitsBase +/// and that defines a name() method. +#define GRID_TRAITS(_typ, _name) \ + template<> struct GridTraits<_typ>: public GridTraitsBase<_typ> { \ + static const char* name() { return _name; } \ + } + +GRID_TRAITS(openvdb::FloatGrid, "FloatGrid"); +GRID_TRAITS(openvdb::Vec3SGrid, "Vec3SGrid"); +GRID_TRAITS(openvdb::BoolGrid, "BoolGrid"); +#ifdef PY_OPENVDB_WRAP_ALL_GRID_TYPES +GRID_TRAITS(openvdb::DoubleGrid, "DoubleGrid"); +GRID_TRAITS(openvdb::Int32Grid, "Int32Grid"); +GRID_TRAITS(openvdb::Int64Grid, "Int64Grid"); +GRID_TRAITS(openvdb::Vec3IGrid, "Vec3IGrid"); +GRID_TRAITS(openvdb::Vec3DGrid, "Vec3DGrid"); +GRID_TRAITS(openvdb::points::PointDataGrid, "PointDataGrid"); +#endif + +#undef GRID_TRAITS + + +//////////////////////////////////////// + + +// Note that the elements are pointers to C strings (char**), because +// boost::python::class_::def_readonly() requires a pointer to a static member. +typedef std::pair CStringPair; + + +/// @brief Enum-like mapping from string keys to string values, with characteristics +/// of both (Python) classes and class instances (as well as NamedTuples) +/// @details +/// - (@e key, @e value) pairs can be accessed as class attributes (\"MyClass.MY_KEY\") +/// - (@e key, @e value) pairs can be accessed via dict lookup on instances +/// (\"MyClass()['MY_KEY']\") +/// - (@e key, @e value) pairs can't be modified or reassigned +/// - instances are iterable (\"for key in MyClass(): ...\") +/// +/// A @c Descr class must implement the following interface: +/// @code +/// struct MyDescr +/// { +/// // Return the Python name for the enum class. +/// static const char* name(); +/// // Return the docstring for the enum class. +/// static const char* doc(); +/// // Return the ith (key, value) pair, in the form of +/// // a pair of *pointers* to C strings +/// static CStringPair item(int i); +/// }; +/// @endcode +template +struct StringEnum +{ + /// Return the (key, value) map as a Python dict. + static boost::python::dict items() + { + static tbb::mutex sMutex; + static boost::python::dict itemDict; + if (!itemDict) { + // The first time this function is called, populate + // the static dict with (key, value) pairs. + tbb::mutex::scoped_lock lock(sMutex); + if (!itemDict) { + for (int i = 0; ; ++i) { + const CStringPair item = Descr::item(i); + OPENVDB_START_THREADSAFE_STATIC_WRITE + if (item.first) { + itemDict[boost::python::str(*item.first)] = + boost::python::str(*item.second); + } + OPENVDB_FINISH_THREADSAFE_STATIC_WRITE + else break; + } + } + } + return itemDict; + } + + /// Return the keys as a Python list of strings. + static boost::python::object keys() { return items().attr("keys")(); } + /// Return the number of keys as a Python int. + boost::python::object numItems() const + { + return boost::python::object(boost::python::len(items())); + } + /// Return the value (as a Python string) for the given key. + boost::python::object getItem(boost::python::object keyObj) const { return items()[keyObj]; } + /// Return a Python iterator over the keys. + boost::python::object iter() const { return items().attr("__iter__")(); } + + /// Register this enum. + static void wrap() + { + boost::python::class_ cls( + /*classname=*/Descr::name(), + /*docstring=*/Descr::doc()); + cls.def("keys", &StringEnum::keys, "keys() -> list") + .staticmethod("keys") + .def("__len__", &StringEnum::numItems, "__len__() -> int") + .def("__iter__", &StringEnum::iter, "__iter__() -> iterator") + .def("__getitem__", &StringEnum::getItem, "__getitem__(str) -> str") + /*end*/; + // Add a read-only, class-level attribute for each (key, value) pair. + for (int i = 0; ; ++i) { + const CStringPair item = Descr::item(i); + if (item.first) cls.def_readonly(*item.first, item.second); + else break; + } + } +}; + + +//////////////////////////////////////// + + +/// @brief From the given Python object, extract a value of type @c T. +/// +/// If the object cannot be converted to type @c T, raise a @c TypeError with a more +/// Pythonic error message (incorporating the provided class and function names, etc.) +/// than the one that would be generated by boost::python::extract(), e.g., +/// "TypeError: expected float, found str as argument 2 to FloatGrid.prune()" instead of +/// "TypeError: No registered converter was able to produce a C++ rvalue of type +/// boost::shared_ptr +inline T +extractArg( + boost::python::object obj, + const char* functionName, + const char* className = nullptr, + int argIdx = 0, // args are numbered starting from 1 + const char* expectedType = nullptr) +{ + boost::python::extract val(obj); + if (!val.check()) { + // Generate an error string of the form + // "expected , found as argument + // to .()", where and + // are optional. + std::ostringstream os; + os << "expected "; + if (expectedType) os << expectedType; else os << openvdb::typeNameAsString(); + const std::string actualType = + boost::python::extract(obj.attr("__class__").attr("__name__")); + os << ", found " << actualType << " as argument"; + if (argIdx > 0) os << " " << argIdx; + os << " to "; + if (className) os << className << "."; + os << functionName << "()"; + + PyErr_SetString(PyExc_TypeError, os.str().c_str()); + boost::python::throw_error_already_set(); + } + return val(); +} + + +//////////////////////////////////////// + + +/// Return str(val) for the given value. +template +inline std::string +str(const T& val) +{ + return boost::python::extract(boost::python::str(val)); +} + + +/// Return the name of the given Python object's class. +inline std::string +className(boost::python::object obj) +{ + std::string s = boost::python::extract( + obj.attr("__class__").attr("__name__")); + return s; +} + +} // namespace pyutil + +#endif // OPENVDB_PYUTIL_HAS_BEEN_INCLUDED diff --git a/openvdb/python/test/TestOpenVDB.py b/openvdb/python/test/TestOpenVDB.py new file mode 100644 index 00000000..8c8f075e --- /dev/null +++ b/openvdb/python/test/TestOpenVDB.py @@ -0,0 +1,783 @@ +#!/usr/local/bin/python +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: MPL-2.0 + +""" +Unit tests for the OpenVDB Python module + +These are intended primarily to test the Python-to-C++ and +C++-to-Python bindings, not the OpenVDB library itself. +""" + +import os, os.path +import sys +import unittest +try: + from studio import openvdb +except ImportError: + import pyopenvdb as openvdb + + +def valueFactory(zeroValue, elemValue): + """ + Return elemValue converted to a value of the same type as zeroValue. + If zeroValue is a sequence, return a sequence of the same type and length, + with each element set to elemValue. + """ + val = zeroValue + typ = type(val) + try: + # If the type is a sequence type, return a sequence of the appropriate length. + size = len(val) + val = typ([elemValue]) * size + except TypeError: + # Return a scalar value of the appropriate type. + val = typ(elemValue) + return val + + +class TestOpenVDB(unittest.TestCase): + + def run(self, result=None, *args, **kwargs): + super(TestOpenVDB, self).run(result, *args, **kwargs) + + def setUp(self): + # Make output files and directories world-writable. + self.umask = os.umask(0) + + def tearDown(self): + os.umask(self.umask) + + + def testModule(self): + # At a minimum, BoolGrid, FloatGrid and Vec3SGrid should exist. + self.assertTrue(openvdb.BoolGrid in openvdb.GridTypes) + self.assertTrue(openvdb.FloatGrid in openvdb.GridTypes) + self.assertTrue(openvdb.Vec3SGrid in openvdb.GridTypes) + + # Verify that it is possible to construct a grid of each supported type. + for cls in openvdb.GridTypes: + grid = cls() + acc = grid.getAccessor() + acc.setValueOn((-1, -2, 3)) + self.assertEqual(grid.activeVoxelCount(), 1) + + + def testTransform(self): + xform1 = openvdb.createLinearTransform( + [[.5, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 2, 0], + [1, 2, 3, 1]]) + self.assertTrue(xform1.typeName != '') + self.assertEqual(xform1.indexToWorld((1, 1, 1)), (1.5, 3, 5)) + xform2 = xform1 + self.assertEqual(xform2, xform1) + xform2 = xform1.deepCopy() + self.assertEqual(xform2, xform1) + xform2 = openvdb.createFrustumTransform(taper=0.5, depth=100, + xyzMin=(0, 0, 0), xyzMax=(100, 100, 100), voxelSize=0.25) + self.assertNotEqual(xform2, xform1) + worldp = xform2.indexToWorld((10, 10, 10)) + worldp = [int(round(x * 1000000)) for x in worldp] + self.assertEqual(worldp, [-110000, -110000, 2500000]) + + grid = openvdb.FloatGrid() + self.assertEqual(grid.transform, openvdb.createLinearTransform()) + grid.transform = openvdb.createLinearTransform(2.0) + self.assertEqual(grid.transform, openvdb.createLinearTransform(2.0)) + + + def testGridCopy(self): + grid = openvdb.FloatGrid() + self.assertTrue(grid.sharesWith(grid)) + self.assertFalse(grid.sharesWith([])) # wrong type; Grid expected + + copyOfGrid = grid.copy() + self.assertTrue(copyOfGrid.sharesWith(grid)) + + deepCopyOfGrid = grid.deepCopy() + self.assertFalse(deepCopyOfGrid.sharesWith(grid)) + self.assertFalse(deepCopyOfGrid.sharesWith(copyOfGrid)) + + + def testGridProperties(self): + expected = { + openvdb.BoolGrid: ('bool', False, True), + openvdb.FloatGrid: ('float', 0.0, 1.0), + openvdb.Vec3SGrid: ('vec3s', (0, 0, 0), (-1, 0, 1)), + } + + for factory in expected: + valType, bg, newbg = expected[factory] + + grid = factory() + + self.assertEqual(grid.valueTypeName, valType) + def setValueType(obj): + obj.valueTypeName = 'double' + # Grid.valueTypeName is read-only, so setting it raises an exception. + self.assertRaises(AttributeError, lambda obj=grid: setValueType(obj)) + + self.assertEqual(grid.background, bg) + grid.background = newbg + self.assertEqual(grid.background, newbg) + + self.assertEqual(grid.name, '') + grid.name = 'test' + self.assertEqual(grid.name, 'test') + + self.assertFalse(grid.saveFloatAsHalf) + grid.saveFloatAsHalf = True + self.assertTrue(grid.saveFloatAsHalf) + + self.assertTrue(grid.treeDepth > 2) + + + def testGridMetadata(self): + grid = openvdb.BoolGrid() + + self.assertEqual(grid.metadata, {}) + + meta = { + 'name': 'test', + 'xyz': (-1, 0, 1), + 'xyzw': (1.0, 2.25, 3.5, 4.0), + 'intval': 42, + 'floatval': 1.25, + 'mat4val': [[1]*4]*4, + 'saveFloatAsHalf': True, + } + grid.metadata = meta + self.assertEqual(grid.metadata, meta) + + meta['xyz'] = (-100, 100, 0) + grid.updateMetadata(meta) + self.assertEqual(grid.metadata, meta) + + self.assertEqual(set(grid.iterkeys()), set(meta.keys())) + + for name in meta: + self.assertTrue(name in grid) + self.assertEqual(grid[name], meta[name]) + self.assertEqual(type(grid[name]), type(meta[name])) + + for name in grid: + self.assertTrue(name in grid) + self.assertEqual(grid[name], meta[name]) + self.assertEqual(type(grid[name]), type(meta[name])) + + self.assertTrue('xyz' in grid) + del grid['xyz'] + self.assertFalse('xyz' in grid) + grid['xyz'] = meta['xyz'] + self.assertTrue('xyz' in grid) + + grid.addStatsMetadata() + meta = grid.getStatsMetadata() + self.assertEqual(0, meta["file_voxel_count"]) + + + def testGridFill(self): + grid = openvdb.FloatGrid() + acc = grid.getAccessor() + ijk = (1, 1, 1) + + self.assertRaises(TypeError, lambda: grid.fill("", (7, 7, 7), 1, False)) + self.assertRaises(TypeError, lambda: grid.fill((0, 0, 0), "", 1, False)) + self.assertRaises(TypeError, lambda: grid.fill((0, 0, 0), (7, 7, 7), "", False)) + + self.assertFalse(acc.isValueOn(ijk)) + grid.fill((0, 0, 0), (7, 7, 7), 1, active=False) + self.assertEqual(acc.getValue(ijk), 1) + self.assertFalse(acc.isValueOn(ijk)) + + grid.fill((0, 0, 0), (7, 7, 7), 2, active=True) + self.assertEqual(acc.getValue(ijk), 2) + self.assertTrue(acc.isValueOn(ijk)) + + activeCount = grid.activeVoxelCount() + acc.setValueOn(ijk, 2.125) + self.assertEqual(grid.activeVoxelCount(), activeCount) + + grid.fill(ijk, ijk, 2.125, active=True) + self.assertEqual(acc.getValue(ijk), 2.125) + self.assertTrue(acc.isValueOn(ijk)) + self.assertEqual(grid.activeVoxelCount(), activeCount) + leafCount = grid.leafCount() + + grid.prune() + self.assertAlmostEqual(acc.getValue(ijk), 2.125) + self.assertTrue(acc.isValueOn(ijk)) + self.assertEqual(grid.leafCount(), leafCount) + self.assertEqual(grid.activeVoxelCount(), activeCount) + + grid.prune(tolerance=0.2) + self.assertEqual(grid.activeVoxelCount(), activeCount) + self.assertEqual(acc.getValue(ijk), 2.0) # median + self.assertTrue(acc.isValueOn(ijk)) + self.assertTrue(grid.leafCount() < leafCount) + + + def testGridIterators(self): + onCoords = set([(-10, -10, -10), (0, 0, 0), (1, 1, 1)]) + + for factory in openvdb.GridTypes: + grid = factory() + acc = grid.getAccessor() + for c in onCoords: + acc.setValueOn(c) + + coords = set(value.min for value in grid.iterOnValues()) + self.assertEqual(coords, onCoords) + + n = 0 + for _ in grid.iterAllValues(): + n += 1 + for _ in grid.iterOffValues(): + n -= 1 + self.assertEqual(n, len(onCoords)) + + grid = factory() + grid.fill((0, 0, 1), (18, 18, 18), grid.oneValue) # make active + activeCount = grid.activeVoxelCount() + + # Iterate over active values (via a const iterator) and verify + # that the cumulative active voxel count matches the grid's. + count = 0 + for value in grid.citerOnValues(): + count += value.count + self.assertEqual(count, activeCount) + + # Via a non-const iterator, turn off every other active value. + # Then verify that the cumulative active voxel count is half the original count. + state = True + for value in grid.iterOnValues(): + count -= value.count + value.active = state + state = not state + self.assertEqual(grid.activeVoxelCount(), activeCount / 2) + + # Verify that writing through a const iterator is not allowed. + value = grid.citerOnValues().next() + self.assertRaises(AttributeError, lambda: setattr(value, 'active', 0)) + self.assertRaises(AttributeError, lambda: setattr(value, 'depth', 0)) + # Verify that some value attributes are immutable, even given a non-const iterator. + value = grid.iterOnValues().next() + self.assertRaises(AttributeError, lambda: setattr(value, 'min', (0, 0, 0))) + self.assertRaises(AttributeError, lambda: setattr(value, 'max', (0, 0, 0))) + self.assertRaises(AttributeError, lambda: setattr(value, 'count', 1)) + + + def testMap(self): + grid = openvdb.BoolGrid() + grid.fill((-4, -4, -4), (5, 5, 5), grid.zeroValue) # make active + grid.mapOn(lambda x: not x) # replace active False values with True + n = sum(item.value for item in grid.iterOnValues()) + self.assertEqual(n, 10 * 10 * 10) + + grid = openvdb.FloatGrid() + grid.fill((-4, -4, -4), (5, 5, 5), grid.oneValue) + grid.mapOn(lambda x: x * 2) + n = sum(item.value for item in grid.iterOnValues()) + self.assertEqual(n, 10 * 10 * 10 * 2) + + grid = openvdb.Vec3SGrid() + grid.fill((-4, -4, -4), (5, 5, 5), grid.zeroValue) + grid.mapOn(lambda x: (0, 1, 0)) + n = sum(item.value[1] for item in grid.iterOnValues()) + self.assertEqual(n, 10 * 10 * 10) + + + def testValueAccessor(self): + coords = set([(-10, -10, -10), (0, 0, 0), (1, 1, 1)]) + + for factory in openvdb.GridTypes: + # skip value accessor tests for PointDataGrids (value setting methods are disabled) + if factory.valueTypeName.startswith('ptdataidx'): + continue + grid = factory() + zero, one = grid.zeroValue, grid.oneValue + acc = grid.getAccessor() + cacc = grid.getConstAccessor() + leafDepth = grid.treeDepth - 1 + + self.assertRaises(TypeError, lambda: cacc.setValueOn((5, 5, 5), zero)) + self.assertRaises(TypeError, lambda: cacc.setValueOff((5, 5, 5), zero)) + self.assertRaises(TypeError, lambda: cacc.setActiveState((5, 5, 5), True)) + self.assertRaises(TypeError, lambda: acc.setValueOn("", zero)) + self.assertRaises(TypeError, lambda: acc.setValueOff("", zero)) + if grid.valueTypeName != "bool": + self.assertRaises(TypeError, lambda: acc.setValueOn((5, 5, 5), object())) + self.assertRaises(TypeError, lambda: acc.setValueOff((5, 5, 5), object())) + + for c in coords: + grid.clear() + + # All voxels are inactive, background (0), and stored at the root. + self.assertEqual(acc.getValue(c), zero) + self.assertEqual(cacc.getValue(c), zero) + self.assertFalse(acc.isValueOn(c)) + self.assertFalse(cacc.isValueOn(c)) + self.assertEqual(acc.getValueDepth(c), -1) + self.assertEqual(cacc.getValueDepth(c), -1) + + acc.setValueOn(c) # active / 0 / leaf + self.assertEqual(acc.getValue(c), zero) + self.assertEqual(cacc.getValue(c), zero) + self.assertTrue(acc.isValueOn(c)) + self.assertTrue(cacc.isValueOn(c)) + self.assertEqual(acc.getValueDepth(c), leafDepth) + self.assertEqual(cacc.getValueDepth(c), leafDepth) + + acc.setValueOff(c, grid.oneValue) # inactive / 1 / leaf + self.assertEqual(acc.getValue(c), one) + self.assertEqual(cacc.getValue(c), one) + self.assertFalse(acc.isValueOn(c)) + self.assertFalse(cacc.isValueOn(c)) + self.assertEqual(acc.getValueDepth(c), leafDepth) + self.assertEqual(cacc.getValueDepth(c), leafDepth) + + # Verify that an accessor remains valid even after its grid is deleted + # (because the C++ wrapper retains a reference to the C++ grid). + def scoped(): + grid = factory() + acc = grid.getAccessor() + cacc = grid.getConstAccessor() + one = grid.oneValue + acc.setValueOn((0, 0, 0), one) + del grid + self.assertEqual(acc.getValue((0, 0, 0)), one) + self.assertEqual(cacc.getValue((0, 0, 0)), one) + scoped() + + + def testValueAccessorCopy(self): + xyz = (0, 0, 0) + grid = openvdb.BoolGrid() + + acc = grid.getAccessor() + self.assertEqual(acc.getValue(xyz), False) + self.assertFalse(acc.isValueOn(xyz)) + + copyOfAcc = acc.copy() + self.assertEqual(copyOfAcc.getValue(xyz), False) + self.assertFalse(copyOfAcc.isValueOn(xyz)) + + # Verify that changes made to the grid through one accessor are reflected in the other. + acc.setValueOn(xyz, True) + self.assertEqual(acc.getValue(xyz), True) + self.assertTrue(acc.isValueOn(xyz)) + self.assertEqual(copyOfAcc.getValue(xyz), True) + self.assertTrue(copyOfAcc.isValueOn(xyz)) + + copyOfAcc.setValueOff(xyz) + self.assertEqual(acc.getValue(xyz), True) + self.assertFalse(acc.isValueOn(xyz)) + self.assertEqual(copyOfAcc.getValue(xyz), True) + self.assertFalse(copyOfAcc.isValueOn(xyz)) + + # Verify that the two accessors are distinct, by checking that they + # have cached different sets of nodes. + xyz2 = (-1, -1, -1) + copyOfAcc.setValueOn(xyz2) + self.assertTrue(copyOfAcc.isCached(xyz2)) + self.assertFalse(copyOfAcc.isCached(xyz)) + self.assertTrue(acc.isCached(xyz)) + self.assertFalse(acc.isCached(xyz2)) + + + def testPickle(self): + import pickle + + # Test pickling of transforms of various types. + testXforms = [ + openvdb.createLinearTransform(voxelSize=0.1), + openvdb.createLinearTransform(matrix=[[1,0,0,0],[0,2,0,0],[0,0,3,0],[4,3,2,1]]), + openvdb.createFrustumTransform((0,0,0), (10,10,10), taper=0.8, depth=10.0), + ] + for xform in testXforms: + s = pickle.dumps(xform) + restoredXform = pickle.loads(s) + self.assertEqual(restoredXform, xform) + + # Test pickling of grids of various types. + for factory in openvdb.GridTypes: + + # Construct a grid. + grid = factory() + # Add some metadata to the grid. + meta = { 'name': 'test', 'saveFloatAsHalf': True, 'xyz': (-1, 0, 1) } + grid.metadata = meta + # Add some voxel data to the grid. + active = True + for width in range(63, 0, -10): + val = valueFactory(grid.zeroValue, width) + grid.fill((0, 0, 0), (width,)*3, val, active) + active = not active + + # Pickle the grid to a string, then unpickle the string. + s = pickle.dumps(grid) + restoredGrid = pickle.loads(s) + + # Verify that the original and unpickled grids' metadata are equal. + self.assertEqual(restoredGrid.metadata, meta) + + # Verify that the original and unpickled grids have the same active values. + for restored, original in zip(restoredGrid.iterOnValues(), grid.iterOnValues()): + self.assertEqual(restored, original) + # Verify that the original and unpickled grids have the same inactive values. + for restored, original in zip(restoredGrid.iterOffValues(), grid.iterOffValues()): + self.assertEqual(restored, original) + + + def testGridCombine(self): + # Construct two grids and add some voxel data to each. + aGrid, bGrid = openvdb.FloatGrid(), openvdb.FloatGrid(background=1.0) + for width in range(63, 1, -10): + aGrid.fill((0, 0, 0), (width,)*3, width) + bGrid.fill((0, 0, 0), (width,)*3, 2 * width) + + # Save a copy of grid A. + copyOfAGrid = aGrid.deepCopy() + + # Combine corresponding values of the two grids, storing the result in grid A. + # (Since the grids have the same topology and B's active values are twice A's, + # the function computes 2*min(a, 2*a) + 3*max(a, 2*a) = 2*a + 3*(2*a) = 8*a + # for active values, and 2*min(0, 1) + 3*max(0, 1) = 2*0 + 3*1 = 3 + # for inactive values.) + aGrid.combine(bGrid, lambda a, b: 2 * min(a, b) + 3 * max(a, b)) + + self.assertTrue(bGrid.empty()) + + # Verify that the resulting grid's values are as expected. + for original, combined in zip(copyOfAGrid.iterOnValues(), aGrid.iterOnValues()): + self.assertEqual(combined.min, original.min) + self.assertEqual(combined.max, original.max) + self.assertEqual(combined.depth, original.depth) + self.assertEqual(combined.value, 8 * original.value) + for original, combined in zip(copyOfAGrid.iterOffValues(), aGrid.iterOffValues()): + self.assertEqual(combined.min, original.min) + self.assertEqual(combined.max, original.max) + self.assertEqual(combined.depth, original.depth) + self.assertEqual(combined.value, 3) + + + def testLevelSetSphere(self): + HALF_WIDTH = 4 + sphere = openvdb.createLevelSetSphere(halfWidth=HALF_WIDTH, voxelSize=1.0, radius=100.0) + lo, hi = sphere.evalMinMax() + self.assertTrue(lo >= -HALF_WIDTH) + self.assertTrue(hi <= HALF_WIDTH) + + + def testCopyFromArray(self): + import random + import time + + # Skip this test if NumPy is not available. + try: + import numpy as np + except ImportError: + return + + # Skip this test if the OpenVDB module was built without NumPy support. + arr = np.zeros((1, 2, 1)) + grid = openvdb.FloatGrid() + try: + grid.copyFromArray(arr) + except NotImplementedError: + return + + # Verify that a non-three-dimensional array can't be copied into a grid. + grid = openvdb.FloatGrid() + self.assertRaises(TypeError, lambda: grid.copyFromArray('abc')) + arr = np.zeros((1, 2)) + self.assertRaises(ValueError, lambda: grid.copyFromArray(arr)) + + # Verify that complex-valued arrays are not supported. + arr = np.zeros((1, 2, 1), dtype = complex) + grid = openvdb.FloatGrid() + self.assertRaises(TypeError, lambda: grid.copyFromArray(arr)) + + ARRAY_DIM = 201 + BG, FG = 0, 1 + + # Generate some random voxel coordinates. + random.seed(0) + def randCoord(): + return tuple(random.randint(0, ARRAY_DIM-1) for i in range(3)) + coords = set(randCoord() for i in range(200)) + + def createArrays(): + # Test both scalar- and vec3-valued (i.e., four-dimensional) arrays. + for shape in ( + (ARRAY_DIM, ARRAY_DIM, ARRAY_DIM), # scalar array + (ARRAY_DIM, ARRAY_DIM, ARRAY_DIM, 3) # vec3 array + ): + for dtype in (np.float32, np.int32, np.float64, np.int64, np.uint32, np.bool): + # Create a NumPy array, fill it with the background value, + # then set some elements to the foreground value. + arr = np.ndarray(shape, dtype) + arr.fill(BG) + bg = arr[0, 0, 0] + for c in coords: + arr[c] = FG + + yield arr + + # Test copying from arrays of various types to grids of various types. + for cls in openvdb.GridTypes: + # skip copying test for PointDataGrids + if cls.valueTypeName.startswith('ptdataidx'): + continue + for arr in createArrays(): + isScalarArray = (len(arr.shape) == 3) + isScalarGrid = False + try: + len(cls.zeroValue) # values of vector grids are sequences, which have a length + except TypeError: + isScalarGrid = True # values of scalar grids have no length + + gridBG = valueFactory(cls.zeroValue, BG) + gridFG = valueFactory(cls.zeroValue, FG) + + # Create an empty grid. + grid = cls(gridBG) + acc = grid.getAccessor() + + # Verify that scalar arrays can't be copied into vector grids + # and vector arrays can't be copied into scalar grids. + if isScalarGrid != isScalarArray: + self.assertRaises(ValueError, lambda: grid.copyFromArray(arr)) + continue + + # Copy values from the NumPy array to the grid, marking + # background values as inactive and all other values as active. + now = time.clock() + grid.copyFromArray(arr) + elapsed = time.clock() - now + #print 'copied %d voxels from %s array to %s in %f sec' % ( + # arr.shape[0] * arr.shape[1] * arr.shape[2], + # str(arr.dtype) + ('' if isScalarArray else '[]'), + # grid.__class__.__name__, elapsed) + + # Verify that the grid's active voxels match the array's foreground elements. + self.assertEqual(grid.activeVoxelCount(), len(coords)) + for c in coords: + self.assertEqual(acc.getValue(c), gridFG) + for value in grid.iterOnValues(): + self.assertTrue(value.min in coords) + + + def testCopyToArray(self): + import random + import time + + # Skip this test if NumPy is not available. + try: + import numpy as np + except ImportError: + return + + # Skip this test if the OpenVDB module was built without NumPy support. + arr = np.zeros((1, 2, 1)) + grid = openvdb.FloatGrid() + try: + grid.copyFromArray(arr) + except NotImplementedError: + return + + # Verify that a grid can't be copied into a non-three-dimensional array. + grid = openvdb.FloatGrid() + self.assertRaises(TypeError, lambda: grid.copyToArray('abc')) + arr = np.zeros((1, 2)) + self.assertRaises(ValueError, lambda: grid.copyToArray(arr)) + + # Verify that complex-valued arrays are not supported. + arr = np.zeros((1, 2, 1), dtype = complex) + grid = openvdb.FloatGrid() + self.assertRaises(TypeError, lambda: grid.copyToArray(arr)) + + ARRAY_DIM = 201 + BG, FG = 0, 1 + + # Generate some random voxel coordinates. + random.seed(0) + def randCoord(): + return tuple(random.randint(0, ARRAY_DIM-1) for i in range(3)) + coords = set(randCoord() for i in range(200)) + + def createArrays(): + # Test both scalar- and vec3-valued (i.e., four-dimensional) arrays. + for shape in ( + (ARRAY_DIM, ARRAY_DIM, ARRAY_DIM), # scalar array + (ARRAY_DIM, ARRAY_DIM, ARRAY_DIM, 3) # vec3 array + ): + for dtype in (np.float32, np.int32, np.float64, np.int64, np.uint32, np.bool): + # Return a new NumPy array. + arr = np.ndarray(shape, dtype) + arr.fill(-100) + yield arr + + # Test copying from arrays of various types to grids of various types. + for cls in openvdb.GridTypes: + # skip copying test for PointDataGrids + if cls.valueTypeName.startswith('ptdataidx'): + continue + for arr in createArrays(): + isScalarArray = (len(arr.shape) == 3) + isScalarGrid = False + try: + len(cls.zeroValue) # values of vector grids are sequences, which have a length + except TypeError: + isScalarGrid = True # values of scalar grids have no length + + gridBG = valueFactory(cls.zeroValue, BG) + gridFG = valueFactory(cls.zeroValue, FG) + + # Create an empty grid, fill it with the background value, + # then set some elements to the foreground value. + grid = cls(gridBG) + acc = grid.getAccessor() + for c in coords: + acc.setValueOn(c, gridFG) + + # Verify that scalar grids can't be copied into vector arrays + # and vector grids can't be copied into scalar arrays. + if isScalarGrid != isScalarArray: + self.assertRaises(ValueError, lambda: grid.copyToArray(arr)) + continue + + # Copy values from the grid to the NumPy array. + now = time.clock() + grid.copyToArray(arr) + elapsed = time.clock() - now + #print 'copied %d voxels from %s to %s array in %f sec' % ( + # arr.shape[0] * arr.shape[1] * arr.shape[2], grid.__class__.__name__, + # str(arr.dtype) + ('' if isScalarArray else '[]'), elapsed) + + # Verify that the grid's active voxels match the array's foreground elements. + for c in coords: + self.assertEqual(arr[c] if isScalarArray else tuple(arr[c]), gridFG) + arr[c] = gridBG + self.assertEqual(np.amin(arr), BG) + self.assertEqual(np.amax(arr), BG) + + + def testMeshConversion(self): + import time + + # Skip this test if NumPy is not available. + try: + import numpy as np + except ImportError: + return + + # Test mesh to volume conversion. + + # Generate the vertices of a cube. + cubeVertices = [(x, y, z) for x in (0, 100) for y in (0, 100) for z in (0, 100)] + cubePoints = np.array(cubeVertices, float) + + # Generate the faces of a cube. + cubeQuads = np.array([ + (0, 1, 3, 2), # left + (0, 2, 6, 4), # front + (4, 6, 7, 5), # right + (5, 7, 3, 1), # back + (2, 3, 7, 6), # top + (0, 4, 5, 1), # bottom + ], float) + + voxelSize = 2.0 + halfWidth = 3.0 + xform = openvdb.createLinearTransform(voxelSize) + + # Only scalar, floating-point grids support createLevelSetFromPolygons() + # (and the OpenVDB module might have been compiled without DoubleGrid support). + grids = [] + for gridType in [n for n in openvdb.GridTypes + if n.__name__ in ('FloatGrid', 'DoubleGrid')]: + + # Skip this test if the OpenVDB module was built without NumPy support. + try: + grid = gridType.createLevelSetFromPolygons( + cubePoints, quads=cubeQuads, transform=xform, halfWidth=halfWidth) + except NotImplementedError: + return + + #openvdb.write('/tmp/testMeshConversion.vdb', grid) + + self.assertEqual(grid.transform, xform) + self.assertEqual(grid.background, halfWidth * voxelSize) + + dim = grid.evalActiveVoxelDim() + self.assertTrue(50 < dim[0] < 58) + self.assertTrue(50 < dim[1] < 58) + self.assertTrue(50 < dim[2] < 58) + + grids.append(grid) + + # Boolean-valued grids can't be used to store level sets. + self.assertRaises(TypeError, lambda: openvdb.BoolGrid.createLevelSetFromPolygons( + cubePoints, quads=cubeQuads, transform=xform, halfWidth=halfWidth)) + # Vector-valued grids can't be used to store level sets. + self.assertRaises(TypeError, lambda: openvdb.Vec3SGrid.createLevelSetFromPolygons( + cubePoints, quads=cubeQuads, transform=xform, halfWidth=halfWidth)) + # The "points" argument to createLevelSetFromPolygons() must be a NumPy array. + self.assertRaises(TypeError, lambda: openvdb.FloatGrid.createLevelSetFromPolygons( + cubeVertices, quads=cubeQuads, transform=xform, halfWidth=halfWidth)) + # The "points" argument to createLevelSetFromPolygons() must be a NumPy float or int array. + self.assertRaises(TypeError, lambda: openvdb.FloatGrid.createLevelSetFromPolygons( + np.array(cubeVertices, bool), quads=cubeQuads, transform=xform, halfWidth=halfWidth)) + # The "triangles" argument to createLevelSetFromPolygons() must be an N x 3 NumPy array. + self.assertRaises(TypeError, lambda: openvdb.FloatGrid.createLevelSetFromPolygons( + cubePoints, triangles=cubeQuads, transform=xform, halfWidth=halfWidth)) + + # Test volume to mesh conversion. + + # Vector-valued grids can't be meshed. + self.assertRaises(TypeError, lambda: openvdb.Vec3SGrid().convertToQuads()) + + for grid in grids: + points, quads = grid.convertToQuads() + + # These checks are intended mainly to test the Python/C++ bindings, + # not the OpenVDB volume to mesh converter. + self.assertTrue(len(points) > 8) + self.assertTrue(len(quads) > 6) + pmin, pmax = points.min(0), points.max(0) + self.assertTrue(-2 < pmin[0] < 2) + self.assertTrue(-2 < pmin[1] < 2) + self.assertTrue(-2 < pmin[2] < 2) + self.assertTrue(98 < pmax[0] < 102) + self.assertTrue(98 < pmax[1] < 102) + self.assertTrue(98 < pmax[2] < 102) + + points, triangles, quads = grid.convertToPolygons(adaptivity=1) + + self.assertTrue(len(points) > 8) + pmin, pmax = points.min(0), points.max(0) + self.assertTrue(-2 < pmin[0] < 2) + self.assertTrue(-2 < pmin[1] < 2) + self.assertTrue(-2 < pmin[2] < 2) + self.assertTrue(98 < pmax[0] < 102) + self.assertTrue(98 < pmax[1] < 102) + self.assertTrue(98 < pmax[2] < 102) + + +if __name__ == '__main__': + print('Testing %s' % os.path.dirname(openvdb.__file__)) + sys.stdout.flush() + + args = sys.argv + + # Unlike CppUnit, PyUnit doesn't use the "-t" flag to identify + # test names, so for consistency, strip out any "-t" arguments, + # so that, e.g., "TestOpenVDB.py -t TestOpenVDB.testTransform" + # is equivalent to "TestOpenVDB.py TestOpenVDB.testTransform". + args = [a for a in args if a != '-t'] + + unittest.main(argv=args) + diff --git a/openvdb/tools/ChangeBackground.h b/openvdb/tools/ChangeBackground.h new file mode 100644 index 00000000..19f80440 --- /dev/null +++ b/openvdb/tools/ChangeBackground.h @@ -0,0 +1,247 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file ChangeBackground.h +/// +/// @brief Efficient multi-threaded replacement of the background +/// values in tree. +/// +/// @author Ken Museth + +#ifndef OPENVDB_TOOLS_ChangeBACKGROUND_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_ChangeBACKGROUND_HAS_BEEN_INCLUDED + +#include // for isNegative and negative +#include // for Index typedef +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Replace the background value in all the nodes of a tree. +/// @details The sign of the background value is preserved, and only +/// inactive values equal to the old background value are replaced. +/// +/// @note If a LeafManager is used the cached leaf nodes are reused, +/// resulting in slightly better overall performance. +/// +/// @param tree Tree (or LeafManager) that will have its background value changed +/// @param background the new background value +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 32) +template +inline void +changeBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& background, + bool threaded = true, + size_t grainSize = 32); + + +/// @brief Replace the background value in all the nodes of a floating-point tree +/// containing a symmetric narrow-band level set. +/// @details All inactive values will be set to +| @a halfWidth | if outside +/// and -| @a halfWidth | if inside, where @a halfWidth is half the width +/// of the symmetric narrow band. +/// +/// @note This method is faster than changeBackground since it does not +/// perform tests to see if inactive values are equal to the old background value. +/// @note If a LeafManager is used the cached leaf nodes are reused, +/// resulting in slightly better overall performance. +/// +/// @param tree Tree (or LeafManager) that will have its background value changed +/// @param halfWidth half of the width of the symmetric narrow band +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 32) +/// +/// @throw ValueError if @a halfWidth is negative (as defined by math::isNegative) +template +inline void +changeLevelSetBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& halfWidth, + bool threaded = true, + size_t grainSize = 32); + + +/// @brief Replace the background values in all the nodes of a floating-point tree +/// containing a possibly asymmetric narrow-band level set. +/// @details All inactive values will be set to +| @a outsideWidth | if outside +/// and -| @a insideWidth | if inside, where @a outsideWidth is the outside +/// width of the narrow band and @a insideWidth is its inside width. +/// +/// @note This method is faster than changeBackground since it does not +/// perform tests to see if inactive values are equal to the old background value. +/// @note If a LeafManager is used the cached leaf nodes are reused, +/// resulting in slightly better overall performance. +/// +/// @param tree Tree (or LeafManager) that will have its background value changed +/// @param outsideWidth The width of the outside of the narrow band +/// @param insideWidth The width of the inside of the narrow band +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 32) +/// +/// @throw ValueError if @a outsideWidth is negative or @a insideWidth is +/// not negative (as defined by math::isNegative) +template +inline void +changeAsymmetricLevelSetBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& outsideWidth, + const typename TreeOrLeafManagerT::ValueType& insideWidth, + bool threaded = true, + size_t grainSize = 32); + + +////////////////////////////////////////////////////// + + +// Replaces the background value in a Tree of any type. +template +class ChangeBackgroundOp +{ +public: + typedef typename TreeOrLeafManagerT::ValueType ValueT; + typedef typename TreeOrLeafManagerT::RootNodeType RootT; + typedef typename TreeOrLeafManagerT::LeafNodeType LeafT; + + + ChangeBackgroundOp(const TreeOrLeafManagerT& tree, const ValueT& newValue) + : mOldValue(tree.root().background()) + , mNewValue(newValue) + { + } + void operator()(RootT& root) const + { + for (typename RootT::ValueOffIter it = root.beginValueOff(); it; ++it) this->set(it); + root.setBackground(mNewValue, false); + } + void operator()(LeafT& node) const + { + for (typename LeafT::ValueOffIter it = node.beginValueOff(); it; ++it) this->set(it); + } + template + void operator()(NodeT& node) const + { + typename NodeT::NodeMaskType mask = node.getValueOffMask(); + for (typename NodeT::ValueOnIter it(mask.beginOn(), &node); it; ++it) this->set(it); + } +private: + + template + inline void set(IterT& iter) const + { + if (math::isApproxEqual(*iter, mOldValue)) { + iter.setValue(mNewValue); + } else if (math::isApproxEqual(*iter, math::negative(mOldValue))) { + iter.setValue(math::negative(mNewValue)); + } + } + const ValueT mOldValue, mNewValue; +};// ChangeBackgroundOp + + +// Replaces the background value in a Tree assumed to represent a +// level set. It is generally faster than ChangeBackgroundOp. +// Note that is follows the sign-convention that outside is positive +// and inside is negative! +template +class ChangeLevelSetBackgroundOp +{ +public: + typedef typename TreeOrLeafManagerT::ValueType ValueT; + typedef typename TreeOrLeafManagerT::RootNodeType RootT; + typedef typename TreeOrLeafManagerT::LeafNodeType LeafT; + + /// @brief Constructor for asymmetric narrow-bands + ChangeLevelSetBackgroundOp(const ValueT& outside, const ValueT& inside) + : mOutside(outside) + , mInside(inside) + { + if (math::isNegative(mOutside)) { + OPENVDB_THROW(ValueError, + "ChangeLevelSetBackgroundOp: the outside value cannot be negative!"); + } + if (!math::isNegative(mInside)) { + OPENVDB_THROW(ValueError, + "ChangeLevelSetBackgroundOp: the inside value must be negative!"); + } + } + void operator()(RootT& root) const + { + for (typename RootT::ValueOffIter it = root.beginValueOff(); it; ++it) this->set(it); + root.setBackground(mOutside, false); + } + void operator()(LeafT& node) const + { + for(typename LeafT::ValueOffIter it = node.beginValueOff(); it; ++it) this->set(it); + } + template + void operator()(NodeT& node) const + { + typedef typename NodeT::ValueOffIter IterT; + for (IterT it(node.getChildMask().beginOff(), &node); it; ++it) this->set(it); + } +private: + + template + inline void set(IterT& iter) const + { + //this is safe since we know ValueType is_floating_point + ValueT& v = const_cast(*iter); + v = v < 0 ? mInside : mOutside; + } + const ValueT mOutside, mInside; +};// ChangeLevelSetBackgroundOp + + +template +inline void +changeBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& background, + bool threaded, + size_t grainSize) +{ + tree::NodeManager linearTree(tree); + ChangeBackgroundOp op(tree, background); + linearTree.foreachTopDown(op, threaded, grainSize); +} + + +template +inline void +changeAsymmetricLevelSetBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& outsideValue, + const typename TreeOrLeafManagerT::ValueType& insideValue, + bool threaded, + size_t grainSize) +{ + tree::NodeManager linearTree(tree); + ChangeLevelSetBackgroundOp op(outsideValue, insideValue); + linearTree.foreachTopDown(op, threaded, grainSize); +} + + +// If the narrow-band is symmetric only one background value is required +template +inline void +changeLevelSetBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& background, + bool threaded, + size_t grainSize) +{ + changeAsymmetricLevelSetBackground( + tree, background, math::negative(background), threaded, grainSize); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_CHANGEBACKGROUND_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Clip.h b/openvdb/tools/Clip.h new file mode 100644 index 00000000..5c982914 --- /dev/null +++ b/openvdb/tools/Clip.h @@ -0,0 +1,567 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file Clip.h +/// +/// @brief Functions to clip a grid against a bounding box, a camera frustum, +/// or another grid's active voxel topology + +#ifndef OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED + +#include +#include // for math::isNegative() +#include // for math::NonlinearFrustumMap +#include +#include "GridTransformer.h" // for tools::resampleToMatch() +#include "Prune.h" +#include +#include +#include // for std::enable_if, std::is_same +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Clip the given grid against a world-space bounding box +/// and return a new grid containing the result. +/// @param grid the grid to be clipped +/// @param bbox a world-space bounding box +/// @param keepInterior if true, discard voxels that lie outside the bounding box; +/// if false, discard voxels that lie inside the bounding box +/// @warning Clipping a level set will likely produce a grid that is +/// no longer a valid level set. +template +inline typename GridType::Ptr +clip(const GridType& grid, const BBoxd& bbox, bool keepInterior = true); + +/// @brief Clip the given grid against a frustum and return a new grid containing the result. +/// @param grid the grid to be clipped +/// @param frustum a frustum map +/// @param keepInterior if true, discard voxels that lie outside the frustum; +/// if false, discard voxels that lie inside the frustum +/// @warning Clipping a level set will likely produce a grid that is +/// no longer a valid level set. +template +inline typename GridType::Ptr +clip(const GridType& grid, const math::NonlinearFrustumMap& frustum, bool keepInterior = true); + +/// @brief Clip a grid against the active voxels of another grid +/// and return a new grid containing the result. +/// @param grid the grid to be clipped +/// @param mask a grid whose active voxels form a boolean clipping mask +/// @param keepInterior if true, discard voxels that do not intersect the mask; +/// if false, discard voxels that intersect the mask +/// @details The mask grid need not have the same transform as the source grid. +/// Also, if the mask grid is a level set, consider using tools::sdfInteriorMask +/// to construct a new mask comprising the interior (rather than the narrow band) +/// of the level set. +/// @warning Clipping a level set will likely produce a grid that is +/// no longer a valid level set. +template +inline typename GridType::Ptr +clip(const GridType& grid, const Grid& mask, bool keepInterior = true); + + +//////////////////////////////////////// + + +namespace clip_internal { + +// Use either MaskGrids or BoolGrids internally. +// (MaskGrids have a somewhat lower memory footprint.) +using MaskValueType = ValueMask; +//using MaskValueType = bool; + + +template +class MaskInteriorVoxels +{ +public: + using ValueT = typename TreeT::ValueType; + using LeafNodeT = typename TreeT::LeafNodeType; + + MaskInteriorVoxels(const TreeT& tree): mAcc(tree) {} + + template + void operator()(LeafNodeType& leaf, size_t /*leafIndex*/) const + { + const auto* refLeaf = mAcc.probeConstLeaf(leaf.origin()); + if (refLeaf) { + for (auto iter = leaf.beginValueOff(); iter; ++iter) { + const auto pos = iter.pos(); + leaf.setActiveState(pos, math::isNegative(refLeaf->getValue(pos))); + } + } + } + +private: + tree::ValueAccessor mAcc; +}; + + +//////////////////////////////////////// + + +template +class CopyLeafNodes +{ +public: + using MaskTreeT = typename TreeT::template ValueConverter::Type; + using MaskLeafManagerT = tree::LeafManager; + + CopyLeafNodes(const TreeT&, const MaskLeafManagerT&); + + void run(bool threaded = true); + + typename TreeT::Ptr tree() const { return mNewTree; } + + CopyLeafNodes(CopyLeafNodes&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(const CopyLeafNodes& rhs) { mNewTree->merge(*rhs.mNewTree); } + +private: + const MaskTreeT* mClipMask; + const TreeT* mTree; + const MaskLeafManagerT* mLeafNodes; + typename TreeT::Ptr mNewTree; +}; + + +template +CopyLeafNodes::CopyLeafNodes(const TreeT& tree, const MaskLeafManagerT& leafNodes) + : mTree(&tree) + , mLeafNodes(&leafNodes) + , mNewTree(new TreeT(mTree->background())) +{ +} + + +template +CopyLeafNodes::CopyLeafNodes(CopyLeafNodes& rhs, tbb::split) + : mTree(rhs.mTree) + , mLeafNodes(rhs.mLeafNodes) + , mNewTree(new TreeT(mTree->background())) +{ +} + + +template +void +CopyLeafNodes::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(mLeafNodes->getRange(), *this); + else (*this)(mLeafNodes->getRange()); +} + + +template +void +CopyLeafNodes::operator()(const tbb::blocked_range& range) +{ + tree::ValueAccessor acc(*mNewTree); + tree::ValueAccessor refAcc(*mTree); + + for (auto n = range.begin(); n != range.end(); ++n) { + const auto& maskLeaf = mLeafNodes->leaf(n); + const auto& ijk = maskLeaf.origin(); + const auto* refLeaf = refAcc.probeConstLeaf(ijk); + + auto* newLeaf = acc.touchLeaf(ijk); + + if (refLeaf) { + for (auto it = maskLeaf.cbeginValueOn(); it; ++it) { + const auto pos = it.pos(); + newLeaf->setValueOnly(pos, refLeaf->getValue(pos)); + newLeaf->setActiveState(pos, refLeaf->isValueOn(pos)); + } + } else { + typename TreeT::ValueType value; + bool isActive = refAcc.probeValue(ijk, value); + + for (auto it = maskLeaf.cbeginValueOn(); it; ++it) { + const auto pos = it.pos(); + newLeaf->setValueOnly(pos, value); + newLeaf->setActiveState(pos, isActive); + } + } + } +} + + +//////////////////////////////////////// + + +struct BoolSampler +{ + static const char* name() { return "bin"; } + static int radius() { return 2; } + static bool mipmap() { return false; } + static bool consistent() { return true; } + + template + static bool sample(const TreeT& inTree, + const Vec3R& inCoord, typename TreeT::ValueType& result) + { + return inTree.probeValue(Coord::floor(inCoord), result); + } +}; + + +//////////////////////////////////////// + + +// Convert a grid of one type to a grid of another type +template +struct ConvertGrid +{ + using FromGridCPtrT = typename FromGridT::ConstPtr; + using ToGridPtrT = typename ToGridT::Ptr; + ToGridPtrT operator()(const FromGridCPtrT& grid) { return ToGridPtrT(new ToGridT(*grid)); } +}; + +// Partial specialization that avoids copying when +// the input and output grid types are the same +template +struct ConvertGrid +{ + using GridCPtrT = typename GridT::ConstPtr; + GridCPtrT operator()(const GridCPtrT& grid) { return grid; } +}; + + +//////////////////////////////////////// + + +// Convert a grid of arbitrary type to a mask grid with the same tree configuration +// and return a pointer to the new grid. +/// @private +template +inline typename std::enable_if::value, + typename GridT::template ValueConverter::Type::Ptr>::type +convertToMaskGrid(const GridT& grid) +{ + using MaskGridT = typename GridT::template ValueConverter::Type; + auto mask = MaskGridT::create(/*background=*/false); + mask->topologyUnion(grid); + mask->setTransform(grid.constTransform().copy()); + return mask; +} + +// Overload that avoids any processing if the input grid is already a mask grid +/// @private +template +inline typename std::enable_if::value, + typename GridT::ConstPtr>::type +convertToMaskGrid(const GridT& grid) +{ + return grid.copy(); // shallow copy +} + + +//////////////////////////////////////// + + +/// @private +template +inline typename GridType::Ptr +doClip( + const GridType& grid, + const typename GridType::template ValueConverter::Type& clipMask, + bool keepInterior) +{ + using TreeT = typename GridType::TreeType; + using MaskTreeT = typename GridType::TreeType::template ValueConverter::Type; + + const auto gridClass = grid.getGridClass(); + const auto& tree = grid.tree(); + + MaskTreeT gridMask(false); + gridMask.topologyUnion(tree); + + if (gridClass == GRID_LEVEL_SET) { + tree::LeafManager leafNodes(gridMask); + leafNodes.foreach(MaskInteriorVoxels(tree)); + + tree::ValueAccessor acc(tree); + + typename MaskTreeT::ValueAllIter iter(gridMask); + iter.setMaxDepth(MaskTreeT::ValueAllIter::LEAF_DEPTH - 1); + + for ( ; iter; ++iter) { + iter.setActiveState(math::isNegative(acc.getValue(iter.getCoord()))); + } + } + + if (keepInterior) { + gridMask.topologyIntersection(clipMask.constTree()); + } else { + gridMask.topologyDifference(clipMask.constTree()); + } + + auto outGrid = grid.copyWithNewTree(); + { + // Copy voxel values and states. + tree::LeafManager leafNodes(gridMask); + CopyLeafNodes maskOp(tree, leafNodes); + maskOp.run(); + outGrid->setTree(maskOp.tree()); + } + { + // Copy tile values and states. + tree::ValueAccessor refAcc(tree); + tree::ValueAccessor maskAcc(gridMask); + + typename TreeT::ValueAllIter it(outGrid->tree()); + it.setMaxDepth(TreeT::ValueAllIter::LEAF_DEPTH - 1); + for ( ; it; ++it) { + Coord ijk = it.getCoord(); + + if (maskAcc.isValueOn(ijk)) { + typename TreeT::ValueType value; + bool isActive = refAcc.probeValue(ijk, value); + + it.setValue(value); + if (!isActive) it.setValueOff(); + } + } + } + + outGrid->setTransform(grid.transform().copy()); + if (gridClass != GRID_LEVEL_SET) outGrid->setGridClass(gridClass); + + return outGrid; +} + +} // namespace clip_internal + + +//////////////////////////////////////// + + +/// @private +template +inline typename GridType::Ptr +clip(const GridType& grid, const BBoxd& bbox, bool keepInterior) +{ + using MaskValueT = clip_internal::MaskValueType; + using MaskGridT = typename GridType::template ValueConverter::Type; + + // Transform the world-space bounding box into the source grid's index space. + Vec3d idxMin, idxMax; + math::calculateBounds(grid.constTransform(), bbox.min(), bbox.max(), idxMin, idxMax); + CoordBBox region(Coord::floor(idxMin), Coord::floor(idxMax)); + // Construct a boolean mask grid that is true inside the index-space bounding box + // and false everywhere else. + MaskGridT clipMask(/*background=*/false); + clipMask.fill(region, /*value=*/true, /*active=*/true); + + return clip_internal::doClip(grid, clipMask, keepInterior); +} + + +/// @private +template +inline typename SrcGridType::Ptr +clip(const SrcGridType& srcGrid, const Grid& clipGrid, bool keepInterior) +{ + using MaskValueT = clip_internal::MaskValueType; + using ClipGridType = Grid; + using SrcMaskGridType = typename SrcGridType::template ValueConverter::Type; + using ClipMaskGridType = typename ClipGridType::template ValueConverter::Type; + + // Convert the clipping grid to a boolean-valued mask grid with the same tree configuration. + auto maskGrid = clip_internal::convertToMaskGrid(clipGrid); + + // Resample the mask grid into the source grid's index space. + if (srcGrid.constTransform() != maskGrid->constTransform()) { + auto resampledMask = ClipMaskGridType::create(/*background=*/false); + resampledMask->setTransform(srcGrid.constTransform().copy()); + tools::resampleToMatch(*maskGrid, *resampledMask); + tools::prune(resampledMask->tree()); + maskGrid = resampledMask; + } + + // Convert the mask grid to a mask grid with the same tree configuration as the source grid. + auto clipMask = clip_internal::ConvertGrid< + /*from=*/ClipMaskGridType, /*to=*/SrcMaskGridType>()(maskGrid); + + // Clip the source grid against the mask grid. + return clip_internal::doClip(srcGrid, *clipMask, keepInterior); +} + + +/// @private +template +inline typename GridType::Ptr +clip(const GridType& inGrid, const math::NonlinearFrustumMap& frustumMap, bool keepInterior) +{ + using ValueT = typename GridType::ValueType; + using TreeT = typename GridType::TreeType; + using LeafT = typename TreeT::LeafNodeType; + + const auto& gridXform = inGrid.transform(); + const auto frustumIndexBBox = frustumMap.getBBox(); + + // Return true if index-space point (i,j,k) lies inside the frustum. + auto frustumContainsCoord = [&](const Coord& ijk) -> bool { + auto xyz = gridXform.indexToWorld(ijk); + xyz = frustumMap.applyInverseMap(xyz); + return frustumIndexBBox.isInside(xyz); + }; + + // Return the frustum index-space bounding box of the corners of + // the given grid index-space bounding box. + auto toFrustumIndexSpace = [&](const CoordBBox& inBBox) -> BBoxd { + const Coord bounds[2] = { inBBox.min(), inBBox.max() }; + Coord ijk; + BBoxd outBBox; + for (int i = 0; i < 8; ++i) { + ijk[0] = bounds[(i & 1) >> 0][0]; + ijk[1] = bounds[(i & 2) >> 1][1]; + ijk[2] = bounds[(i & 4) >> 2][2]; + auto xyz = gridXform.indexToWorld(ijk); + xyz = frustumMap.applyInverseMap(xyz); + outBBox.expand(xyz); + } + return outBBox; + }; + + // Construct an output grid with the same transform and metadata as the input grid. + auto outGrid = inGrid.copyWithNewTree(); + if (outGrid->getGridClass() == GRID_LEVEL_SET) { + // After clipping, a level set grid might no longer be a valid SDF. + outGrid->setGridClass(GRID_UNKNOWN); + } + + const auto& bg = outGrid->background(); + + auto outAcc = outGrid->getAccessor(); + + // Copy active and inactive tiles that intersect the clipping region + // from the input grid to the output grid. + // ("Clipping region" refers to either the interior or the exterior + // of the frustum, depending on the value of keepInterior.) + auto tileIter = inGrid.beginValueAll(); + tileIter.setMaxDepth(GridType::ValueAllIter::LEAF_DEPTH - 1); + CoordBBox tileBBox; + for ( ; tileIter; ++tileIter) { + const bool tileActive = tileIter.isValueOn(); + const auto& tileValue = tileIter.getValue(); + + // Skip background tiles. + if (!tileActive && math::isApproxEqual(tileValue, bg)) continue; + + // Transform the tile's bounding box into frustum index space. + tileIter.getBoundingBox(tileBBox); + const auto tileFrustumBBox = toFrustumIndexSpace(tileBBox); + + // Determine whether any or all of the tile intersects the clipping region. + enum class CopyTile { kNone, kPartial, kFull }; + auto copyTile = CopyTile::kNone; + if (keepInterior) { + if (frustumIndexBBox.isInside(tileFrustumBBox)) { + copyTile = CopyTile::kFull; + } else if (frustumIndexBBox.hasOverlap(tileFrustumBBox)) { + copyTile = CopyTile::kPartial; + } + } else { + if (!frustumIndexBBox.hasOverlap(tileFrustumBBox)) { + copyTile = CopyTile::kFull; + } else if (!frustumIndexBBox.isInside(tileFrustumBBox)) { + copyTile = CopyTile::kPartial; + } + } + switch (copyTile) { + case CopyTile::kNone: + break; + case CopyTile::kFull: + // Copy the entire tile. + outAcc.addTile(tileIter.getLevel(), tileBBox.min(), tileValue, tileActive); + break; + case CopyTile::kPartial: + // Copy only voxels inside the clipping region. + for (std::vector bboxVec = { tileBBox }; !bboxVec.empty(); ) { + // For efficiency, subdivide sufficiently large tiles and discard + // subregions based on additional bounding box intersection tests. + // The mimimum subregion size is chosen so that cost of the + // bounding box test is comparable to testing every voxel. + if (bboxVec.back().volume() > 64 && bboxVec.back().is_divisible()) { + // Subdivide this region in-place and append the other half to the list. + bboxVec.emplace_back(bboxVec.back(), tbb::split{}); + continue; + } + auto subBBox = bboxVec.back(); + bboxVec.pop_back(); + + // Discard the subregion if it lies completely outside the clipping region. + if (keepInterior) { + if (!frustumIndexBBox.hasOverlap(toFrustumIndexSpace(subBBox))) continue; + } else { + if (frustumIndexBBox.isInside(toFrustumIndexSpace(subBBox))) continue; + } + + // Test every voxel within the subregion. + for (const auto& ijk: subBBox) { + if (frustumContainsCoord(ijk) == keepInterior) { + if (tileActive) { + outAcc.setValueOn(ijk, tileValue); + } else { + outAcc.setValueOff(ijk, tileValue); + } + } + } + } + break; + } + } + tools::prune(outGrid->tree()); + + // Ensure that the output grid has the same leaf node topology as the input grid, + // with the exception of leaf nodes that lie completely outside the clipping region. + // (This operation is serial.) + for (auto leafIter = inGrid.constTree().beginLeaf(); leafIter; ++leafIter) { + const auto leafBBox = leafIter->getNodeBoundingBox(); + const auto leafFrustumBBox = toFrustumIndexSpace(leafBBox); + if (keepInterior) { + if (frustumIndexBBox.hasOverlap(leafFrustumBBox)) { + outAcc.touchLeaf(leafBBox.min()); + } + } else { + if (!frustumIndexBBox.hasOverlap(leafFrustumBBox) + || !frustumIndexBBox.isInside(leafFrustumBBox)) + { + outAcc.touchLeaf(leafBBox.min()); + } + } + } + + // In parallel across output leaf nodes, copy leaf voxels + // from the input grid to the output grid. + tree::LeafManager outLeafNodes{outGrid->tree()}; + outLeafNodes.foreach( + [&](LeafT& leaf, size_t /*idx*/) { + auto inAcc = inGrid.getConstAccessor(); + ValueT val; + for (auto voxelIter = leaf.beginValueAll(); voxelIter; ++voxelIter) { + const auto ijk = voxelIter.getCoord(); + if (frustumContainsCoord(ijk) == keepInterior) { + const bool active = inAcc.probeValue(ijk, val); + voxelIter.setValue(val); + voxelIter.setValueOn(active); + } + } + } + ); + + return outGrid; +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Composite.h b/openvdb/tools/Composite.h new file mode 100644 index 00000000..4d2a2015 --- /dev/null +++ b/openvdb/tools/Composite.h @@ -0,0 +1,1238 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Composite.h +/// +/// @brief Functions to efficiently perform various compositing operations on grids +/// +/// @authors Peter Cucka, Mihai Alden, Ken Museth + +#ifndef OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include // for isExactlyEqual() +#include "ValueTransformer.h" // for transformValues() +#include "Prune.h"// for prune +#include "SignedFloodFill.h" // for signedFloodFill() + +#include +#include +#include +#include +#include + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Given two level set grids, replace the A grid with the union of A and B. +/// @throw ValueError if the background value of either grid is not greater than zero. +/// @note This operation always leaves the B grid empty. +template OPENVDB_STATIC_SPECIALIZATION +inline void csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune = true); +/// @brief Given two level set grids, replace the A grid with the intersection of A and B. +/// @throw ValueError if the background value of either grid is not greater than zero. +/// @note This operation always leaves the B grid empty. +template OPENVDB_STATIC_SPECIALIZATION +inline void csgIntersection(GridOrTreeT& a, GridOrTreeT& b, bool prune = true); +/// @brief Given two level set grids, replace the A grid with the difference A / B. +/// @throw ValueError if the background value of either grid is not greater than zero. +/// @note This operation always leaves the B grid empty. +template OPENVDB_STATIC_SPECIALIZATION +inline void csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune = true); + +/// @brief Threaded CSG union operation that produces a new grid or tree from +/// immutable inputs. +/// @return The CSG union of the @a and @b level set inputs. +template OPENVDB_STATIC_SPECIALIZATION +inline typename GridOrTreeT::Ptr csgUnionCopy(const GridOrTreeT& a, const GridOrTreeT& b); +/// @brief Threaded CSG intersection operation that produces a new grid or tree from +/// immutable inputs. +/// @return The CSG intersection of the @a and @b level set inputs. +template OPENVDB_STATIC_SPECIALIZATION +inline typename GridOrTreeT::Ptr csgIntersectionCopy(const GridOrTreeT& a, const GridOrTreeT& b); +/// @brief Threaded CSG difference operation that produces a new grid or tree from +/// immutable inputs. +/// @return The CSG difference of the @a and @b level set inputs. +template OPENVDB_STATIC_SPECIALIZATION +inline typename GridOrTreeT::Ptr csgDifferenceCopy(const GridOrTreeT& a, const GridOrTreeT& b); + +/// @brief Given grids A and B, compute max(a, b) per voxel (using sparse traversal). +/// Store the result in the A grid and leave the B grid empty. +template OPENVDB_STATIC_SPECIALIZATION +inline void compMax(GridOrTreeT& a, GridOrTreeT& b); +/// @brief Given grids A and B, compute min(a, b) per voxel (using sparse traversal). +/// Store the result in the A grid and leave the B grid empty. +template OPENVDB_STATIC_SPECIALIZATION +inline void compMin(GridOrTreeT& a, GridOrTreeT& b); +/// @brief Given grids A and B, compute a + b per voxel (using sparse traversal). +/// Store the result in the A grid and leave the B grid empty. +template OPENVDB_STATIC_SPECIALIZATION +inline void compSum(GridOrTreeT& a, GridOrTreeT& b); +/// @brief Given grids A and B, compute a * b per voxel (using sparse traversal). +/// Store the result in the A grid and leave the B grid empty. +template OPENVDB_STATIC_SPECIALIZATION +inline void compMul(GridOrTreeT& a, GridOrTreeT& b); +/// @brief Given grids A and B, compute a / b per voxel (using sparse traversal). +/// Store the result in the A grid and leave the B grid empty. +template OPENVDB_STATIC_SPECIALIZATION +inline void compDiv(GridOrTreeT& a, GridOrTreeT& b); + +/// Copy the active voxels of B into A. +template OPENVDB_STATIC_SPECIALIZATION +inline void compReplace(GridOrTreeT& a, const GridOrTreeT& b); + + +//////////////////////////////////////// + + +namespace composite { + +// composite::min() and composite::max() for non-vector types compare with operator<(). +template inline +const typename std::enable_if::IsVec, T>::type& // = T if T is not a vector type +min(const T& a, const T& b) { return std::min(a, b); } + +template inline +const typename std::enable_if::IsVec, T>::type& +max(const T& a, const T& b) { return std::max(a, b); } + + +// composite::min() and composite::max() for OpenVDB vector types compare by magnitude. +template inline +const typename std::enable_if::IsVec, T>::type& // = T if T is a vector type +min(const T& a, const T& b) +{ + const typename T::ValueType aMag = a.lengthSqr(), bMag = b.lengthSqr(); + return (aMag < bMag ? a : (bMag < aMag ? b : std::min(a, b))); +} + +template inline +const typename std::enable_if::IsVec, T>::type& +max(const T& a, const T& b) +{ + const typename T::ValueType aMag = a.lengthSqr(), bMag = b.lengthSqr(); + return (aMag < bMag ? b : (bMag < aMag ? a : std::max(a, b))); +} + + +template inline +typename std::enable_if::value, T>::type // = T if T is not an integer type +divide(const T& a, const T& b) { return a / b; } + +template inline +typename std::enable_if::value, T>::type // = T if T is an integer type +divide(const T& a, const T& b) +{ + const T zero(0); + if (b != zero) return a / b; + if (a == zero) return 0; + return (a > 0 ? std::numeric_limits::max() : -std::numeric_limits::max()); +} + +// If b is true, return a / 1 = a. +// If b is false and a is true, return 1 / 0 = inf = MAX_BOOL = 1 = a. +// If b is false and a is false, return 0 / 0 = NaN = 0 = a. +inline bool divide(bool a, bool /*b*/) { return a; } + + +enum CSGOperation { CSG_UNION, CSG_INTERSECTION, CSG_DIFFERENCE }; + +template +struct BuildPrimarySegment +{ + using ValueType = typename TreeType::ValueType; + using TreePtrType = typename TreeType::Ptr; + using LeafNodeType = typename TreeType::LeafNodeType; + using NodeMaskType = typename LeafNodeType::NodeMaskType; + using RootNodeType = typename TreeType::RootNodeType; + using NodeChainType = typename RootNodeType::NodeChainType; + using InternalNodeType = typename boost::mpl::at >::type; + + BuildPrimarySegment(const TreeType& lhs, const TreeType& rhs) + : mSegment(new TreeType(lhs.background())) + , mLhsTree(&lhs) + , mRhsTree(&rhs) + { + } + + void operator()() const + { + std::vector leafNodes; + + { + std::vector internalNodes; + mLhsTree->getNodes(internalNodes); + + ProcessInternalNodes op(internalNodes, *mRhsTree, *mSegment, leafNodes); + tbb::parallel_reduce(tbb::blocked_range(0, internalNodes.size()), op); + } + + ProcessLeafNodes op(leafNodes, *mRhsTree, *mSegment); + tbb::parallel_reduce(tbb::blocked_range(0, leafNodes.size()), op); + } + + TreePtrType& segment() { return mSegment; } + +private: + + struct ProcessInternalNodes { + + ProcessInternalNodes(std::vector& lhsNodes, + const TreeType& rhsTree, TreeType& outputTree, + std::vector& outputLeafNodes) + : mLhsNodes(lhsNodes.empty() ? nullptr : &lhsNodes.front()) + , mRhsTree(&rhsTree) + , mLocalTree(mRhsTree->background()) + , mOutputTree(&outputTree) + , mLocalLeafNodes() + , mOutputLeafNodes(&outputLeafNodes) + { + } + + ProcessInternalNodes(ProcessInternalNodes& other, tbb::split) + : mLhsNodes(other.mLhsNodes) + , mRhsTree(other.mRhsTree) + , mLocalTree(mRhsTree->background()) + , mOutputTree(&mLocalTree) + , mLocalLeafNodes() + , mOutputLeafNodes(&mLocalLeafNodes) + { + } + + void join(ProcessInternalNodes& other) + { + mOutputTree->merge(*other.mOutputTree); + mOutputLeafNodes->insert(mOutputLeafNodes->end(), + other.mOutputLeafNodes->begin(), other.mOutputLeafNodes->end()); + } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor rhsAcc(*mRhsTree); + tree::ValueAccessor outputAcc(*mOutputTree); + + std::vector tmpLeafNodes; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const InternalNodeType& lhsNode = *mLhsNodes[n]; + const Coord& ijk = lhsNode.origin(); + const InternalNodeType * rhsNode = + rhsAcc.template probeConstNode(ijk); + + if (rhsNode) { + lhsNode.getNodes(*mOutputLeafNodes); + } else { + if (Operation == CSG_INTERSECTION) { + if (rhsAcc.getValue(ijk) < ValueType(0.0)) { + tmpLeafNodes.clear(); + lhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i])); + } + } + } else { // Union & Difference + if (!(rhsAcc.getValue(ijk) < ValueType(0.0))) { + tmpLeafNodes.clear(); + lhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i])); + } + } + } + } + } // end range loop + } + + InternalNodeType const * const * const mLhsNodes; + TreeType const * const mRhsTree; + TreeType mLocalTree; + TreeType * const mOutputTree; + + std::vector mLocalLeafNodes; + std::vector * const mOutputLeafNodes; + }; // struct ProcessInternalNodes + + struct ProcessLeafNodes { + + ProcessLeafNodes(std::vector& lhsNodes, + const TreeType& rhsTree, TreeType& output) + : mLhsNodes(lhsNodes.empty() ? nullptr : &lhsNodes.front()) + , mRhsTree(&rhsTree) + , mLocalTree(mRhsTree->background()) + , mOutputTree(&output) + { + } + + ProcessLeafNodes(ProcessLeafNodes& other, tbb::split) + : mLhsNodes(other.mLhsNodes) + , mRhsTree(other.mRhsTree) + , mLocalTree(mRhsTree->background()) + , mOutputTree(&mLocalTree) + { + } + + void join(ProcessLeafNodes& rhs) { mOutputTree->merge(*rhs.mOutputTree); } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor rhsAcc(*mRhsTree); + tree::ValueAccessor outputAcc(*mOutputTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const LeafNodeType& lhsNode = *mLhsNodes[n]; + const Coord& ijk = lhsNode.origin(); + + const LeafNodeType* rhsNodePt = rhsAcc.probeConstLeaf(ijk); + + if (rhsNodePt) { // combine overlapping nodes + + LeafNodeType* outputNode = outputAcc.touchLeaf(ijk); + ValueType * outputData = outputNode->buffer().data(); + NodeMaskType& outputMask = outputNode->getValueMask(); + + const ValueType * lhsData = lhsNode.buffer().data(); + const NodeMaskType& lhsMask = lhsNode.getValueMask(); + + const ValueType * rhsData = rhsNodePt->buffer().data(); + const NodeMaskType& rhsMask = rhsNodePt->getValueMask(); + + if (Operation == CSG_INTERSECTION) { + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + const bool fromRhs = lhsData[pos] < rhsData[pos]; + outputData[pos] = fromRhs ? rhsData[pos] : lhsData[pos]; + outputMask.set(pos, fromRhs ? rhsMask.isOn(pos) : lhsMask.isOn(pos)); + } + } else if (Operation == CSG_DIFFERENCE){ + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + const ValueType rhsVal = math::negative(rhsData[pos]); + const bool fromRhs = lhsData[pos] < rhsVal; + outputData[pos] = fromRhs ? rhsVal : lhsData[pos]; + outputMask.set(pos, fromRhs ? rhsMask.isOn(pos) : lhsMask.isOn(pos)); + } + } else { // Union + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + const bool fromRhs = lhsData[pos] > rhsData[pos]; + outputData[pos] = fromRhs ? rhsData[pos] : lhsData[pos]; + outputMask.set(pos, fromRhs ? rhsMask.isOn(pos) : lhsMask.isOn(pos)); + } + } + + } else { + if (Operation == CSG_INTERSECTION) { + if (rhsAcc.getValue(ijk) < ValueType(0.0)) { + outputAcc.addLeaf(new LeafNodeType(lhsNode)); + } + } else { // Union & Difference + if (!(rhsAcc.getValue(ijk) < ValueType(0.0))) { + outputAcc.addLeaf(new LeafNodeType(lhsNode)); + } + } + } + } // end range loop + } + + LeafNodeType const * const * const mLhsNodes; + TreeType const * const mRhsTree; + TreeType mLocalTree; + TreeType * const mOutputTree; + }; // struct ProcessLeafNodes + + TreePtrType mSegment; + TreeType const * const mLhsTree; + TreeType const * const mRhsTree; +}; // struct BuildPrimarySegment + + +template +struct BuildSecondarySegment +{ + using ValueType = typename TreeType::ValueType; + using TreePtrType = typename TreeType::Ptr; + using LeafNodeType = typename TreeType::LeafNodeType; + using NodeMaskType = typename LeafNodeType::NodeMaskType; + using RootNodeType = typename TreeType::RootNodeType; + using NodeChainType = typename RootNodeType::NodeChainType; + using InternalNodeType = typename boost::mpl::at >::type; + + BuildSecondarySegment(const TreeType& lhs, const TreeType& rhs) + : mSegment(new TreeType(lhs.background())) + , mLhsTree(&lhs) + , mRhsTree(&rhs) + { + } + + void operator()() const + { + std::vector leafNodes; + + { + std::vector internalNodes; + mRhsTree->getNodes(internalNodes); + + ProcessInternalNodes op(internalNodes, *mLhsTree, *mSegment, leafNodes); + tbb::parallel_reduce(tbb::blocked_range(0, internalNodes.size()), op); + } + + ProcessLeafNodes op(leafNodes, *mLhsTree, *mSegment); + tbb::parallel_reduce(tbb::blocked_range(0, leafNodes.size()), op); + } + + TreePtrType& segment() { return mSegment; } + +private: + + struct ProcessInternalNodes { + + ProcessInternalNodes(std::vector& rhsNodes, + const TreeType& lhsTree, TreeType& outputTree, + std::vector& outputLeafNodes) + : mRhsNodes(rhsNodes.empty() ? nullptr : &rhsNodes.front()) + , mLhsTree(&lhsTree) + , mLocalTree(mLhsTree->background()) + , mOutputTree(&outputTree) + , mLocalLeafNodes() + , mOutputLeafNodes(&outputLeafNodes) + { + } + + ProcessInternalNodes(ProcessInternalNodes& other, tbb::split) + : mRhsNodes(other.mRhsNodes) + , mLhsTree(other.mLhsTree) + , mLocalTree(mLhsTree->background()) + , mOutputTree(&mLocalTree) + , mLocalLeafNodes() + , mOutputLeafNodes(&mLocalLeafNodes) + { + } + + void join(ProcessInternalNodes& other) + { + mOutputTree->merge(*other.mOutputTree); + mOutputLeafNodes->insert(mOutputLeafNodes->end(), + other.mOutputLeafNodes->begin(), other.mOutputLeafNodes->end()); + } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor lhsAcc(*mLhsTree); + tree::ValueAccessor outputAcc(*mOutputTree); + + std::vector tmpLeafNodes; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const InternalNodeType& rhsNode = *mRhsNodes[n]; + const Coord& ijk = rhsNode.origin(); + const InternalNodeType * lhsNode = + lhsAcc.template probeConstNode(ijk); + + if (lhsNode) { + rhsNode.getNodes(*mOutputLeafNodes); + } else { + if (Operation == CSG_INTERSECTION) { + if (lhsAcc.getValue(ijk) < ValueType(0.0)) { + tmpLeafNodes.clear(); + rhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i])); + } + } + } else if (Operation == CSG_DIFFERENCE) { + if (lhsAcc.getValue(ijk) < ValueType(0.0)) { + tmpLeafNodes.clear(); + rhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + LeafNodeType* outputNode = new LeafNodeType(*tmpLeafNodes[i]); + outputNode->negate(); + outputAcc.addLeaf(outputNode); + } + } + } else { // Union + if (!(lhsAcc.getValue(ijk) < ValueType(0.0))) { + tmpLeafNodes.clear(); + rhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i])); + } + } + } + } + } // end range loop + } + + InternalNodeType const * const * const mRhsNodes; + TreeType const * const mLhsTree; + TreeType mLocalTree; + TreeType * const mOutputTree; + + std::vector mLocalLeafNodes; + std::vector * const mOutputLeafNodes; + }; // struct ProcessInternalNodes + + struct ProcessLeafNodes { + + ProcessLeafNodes(std::vector& rhsNodes, + const TreeType& lhsTree, TreeType& output) + : mRhsNodes(rhsNodes.empty() ? nullptr : &rhsNodes.front()) + , mLhsTree(&lhsTree) + , mLocalTree(mLhsTree->background()) + , mOutputTree(&output) + { + } + + ProcessLeafNodes(ProcessLeafNodes& rhs, tbb::split) + : mRhsNodes(rhs.mRhsNodes) + , mLhsTree(rhs.mLhsTree) + , mLocalTree(mLhsTree->background()) + , mOutputTree(&mLocalTree) + { + } + + void join(ProcessLeafNodes& rhs) { mOutputTree->merge(*rhs.mOutputTree); } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor lhsAcc(*mLhsTree); + tree::ValueAccessor outputAcc(*mOutputTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const LeafNodeType& rhsNode = *mRhsNodes[n]; + const Coord& ijk = rhsNode.origin(); + + const LeafNodeType* lhsNode = lhsAcc.probeConstLeaf(ijk); + + if (!lhsNode) { + if (Operation == CSG_INTERSECTION) { + if (lhsAcc.getValue(ijk) < ValueType(0.0)) { + outputAcc.addLeaf(new LeafNodeType(rhsNode)); + } + } else if (Operation == CSG_DIFFERENCE) { + if (lhsAcc.getValue(ijk) < ValueType(0.0)) { + LeafNodeType* outputNode = new LeafNodeType(rhsNode); + outputNode->negate(); + outputAcc.addLeaf(outputNode); + } + } else { // Union + if (!(lhsAcc.getValue(ijk) < ValueType(0.0))) { + outputAcc.addLeaf(new LeafNodeType(rhsNode)); + } + } + } + } // end range loop + } + + LeafNodeType const * const * const mRhsNodes; + TreeType const * const mLhsTree; + TreeType mLocalTree; + TreeType * const mOutputTree; + }; // struct ProcessLeafNodes + + TreePtrType mSegment; + TreeType const * const mLhsTree; + TreeType const * const mRhsTree; +}; // struct BuildSecondarySegment + + +template +inline typename TreeType::Ptr +doCSGCopy(const TreeType& lhs, const TreeType& rhs) +{ + BuildPrimarySegment primary(lhs, rhs); + BuildSecondarySegment secondary(lhs, rhs); + + // Exploiting nested parallelism + tbb::task_group tasks; + tasks.run(primary); + tasks.run(secondary); + tasks.wait(); + + primary.segment()->merge(*secondary.segment()); + + // The leafnode (level = 0) sign is set in the segment construction. + tools::signedFloodFill(*primary.segment(), /*threaded=*/true, /*grainSize=*/1, /*minLevel=*/1); + + return primary.segment(); +} + + +//////////////////////////////////////// + + +template +struct GridOrTreeConstructor +{ + using TreeTypePtr = typename TreeType::Ptr; + static TreeTypePtr construct(const TreeType&, TreeTypePtr& tree) { return tree; } +}; + + +template +struct GridOrTreeConstructor > +{ + using GridType = Grid; + using GridTypePtr = typename Grid::Ptr; + using TreeTypePtr = typename TreeType::Ptr; + + static GridTypePtr construct(const GridType& grid, TreeTypePtr& tree) { + GridTypePtr maskGrid(GridType::create(tree)); + maskGrid->setTransform(grid.transform().copy()); + maskGrid->insertMeta(grid); + return maskGrid; + } +}; + + +//////////////////////////////////////// + +/// @cond COMPOSITE_INTERNAL +/// List of pairs of leaf node pointers +template +using LeafPairList = std::vector>; +/// @endcond + +/// @cond COMPOSITE_INTERNAL +/// Transfers leaf nodes from a source tree into a +/// desitnation tree, unless it already exists in the destination tree +/// in which case pointers to both leaf nodes are added to a list for +/// subsequent compositing operations. +template +inline void transferLeafNodes(TreeT &srcTree, TreeT &dstTree, + LeafPairList &overlapping) +{ + using LeafT = typename TreeT::LeafNodeType; + tree::ValueAccessor acc(dstTree);//destination + std::vector srcLeafNodes; + srcLeafNodes.reserve(srcTree.leafCount()); + srcTree.stealNodes(srcLeafNodes); + srcTree.clear(); + for (LeafT *srcLeaf : srcLeafNodes) { + LeafT *dstLeaf = acc.probeLeaf(srcLeaf->origin()); + if (dstLeaf) { + overlapping.emplace_back(dstLeaf, srcLeaf);//dst, src + } else { + acc.addLeaf(srcLeaf); + } + } +} +/// @endcond + +/// @cond COMPOSITE_INTERNAL +/// Template specailization of compActiveLeafVoxels +template +inline +typename std::enable_if< + !std::is_same::value && + !std::is_same::value && + std::is_same::value>::type +doCompActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT op) +{ + using LeafT = typename TreeT::LeafNodeType; + LeafPairList overlapping;//dst, src + transferLeafNodes(srcTree, dstTree, overlapping); + + using RangeT = tbb::blocked_range; + tbb::parallel_for(RangeT(0, overlapping.size()), [op, &overlapping](const RangeT& r) { + for (auto i = r.begin(); i != r.end(); ++i) { + LeafT *dstLeaf = overlapping[i].first, *srcLeaf = overlapping[i].second; + dstLeaf->getValueMask() |= srcLeaf->getValueMask(); + auto *ptr = dstLeaf->buffer().data(); + for (auto v = srcLeaf->cbeginValueOn(); v; ++v) op(ptr[v.pos()], *v); + delete srcLeaf; + } + }); +} +/// @endcond + +/// @cond COMPOSITE_INTERNAL +/// Template specailization of compActiveLeafVoxels +template +inline +typename std::enable_if< + std::is_same::value && + std::is_same::value>::type +doCompActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT) +{ + using LeafT = typename TreeT::LeafNodeType; + LeafPairList overlapping;//dst, src + transferLeafNodes(srcTree, dstTree, overlapping); + + using RangeT = tbb::blocked_range; + tbb::parallel_for(RangeT(0, overlapping.size()), [&overlapping](const RangeT& r) { + for (auto i = r.begin(); i != r.end(); ++i) { + overlapping[i].first->getValueMask() |= overlapping[i].second->getValueMask(); + delete overlapping[i].second; + } + }); +} + +/// @cond COMPOSITE_INTERNAL +/// Template specailization of compActiveLeafVoxels +template +inline +typename std::enable_if< + std::is_same::value && + !std::is_same::value>::type +doCompActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT op) +{ + using LeafT = typename TreeT::LeafNodeType; + LeafPairList overlapping;//dst, src + transferLeafNodes(srcTree, dstTree, overlapping); + + using RangeT = tbb::blocked_range; + using WordT = typename LeafT::Buffer::WordType; + tbb::parallel_for(RangeT(0, overlapping.size()), [op, &overlapping](const RangeT& r) { + for (auto i = r.begin(); i != r.end(); ++i) { + LeafT *dstLeaf = overlapping[i].first, *srcLeaf = overlapping[i].second; + WordT *w1 = dstLeaf->buffer().data(); + const WordT *w2 = srcLeaf->buffer().data(); + const WordT *w3 = &(srcLeaf->getValueMask().template getWord(0)); + for (Index32 n = LeafT::Buffer::WORD_COUNT; n--; ++w1) { + WordT tmp = *w1, state = *w3++; + op (tmp, *w2++); + *w1 = (state & tmp) | (~state & *w1);//inactive values are unchanged + } + dstLeaf->getValueMask() |= srcLeaf->getValueMask(); + delete srcLeaf; + } + }); +} +/// @endcond + +/// @cond COMPOSITE_INTERNAL +/// Default functor for compActiveLeafVoxels +template +struct CopyOp +{ + using ValueT = typename TreeT::ValueType; + CopyOp() = default; + void operator()(ValueT& dst, const ValueT& src) const { dst = src; } +}; +/// @endcond + +} // namespace composite + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compMax(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + using ValueT = typename TreeT::ValueType; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(composite::max(args.a(), args.b())); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compMin(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + using ValueT = typename TreeT::ValueType; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(composite::min(args.a(), args.b())); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compSum(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(args.a() + args.b()); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compMul(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(args.a() * args.b()); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compDiv(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(composite::divide(args.a(), args.b())); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +//////////////////////////////////////// + + +template +struct CompReplaceOp +{ + TreeT* const aTree; + + CompReplaceOp(TreeT& _aTree): aTree(&_aTree) {} + + /// @note fill operation is not thread safe + void operator()(const typename TreeT::ValueOnCIter& iter) const + { + CoordBBox bbox; + iter.getBoundingBox(bbox); + aTree->fill(bbox, *iter); + } + + void operator()(const typename TreeT::LeafCIter& leafIter) const + { + tree::ValueAccessor acc(*aTree); + for (typename TreeT::LeafCIter::LeafNodeT::ValueOnCIter iter = + leafIter->cbeginValueOn(); iter; ++iter) + { + acc.setValue(iter.getCoord(), *iter); + } + } +}; + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compReplace(GridOrTreeT& aTree, const GridOrTreeT& bTree) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + using ValueOnCIterT = typename TreeT::ValueOnCIter; + + // Copy active states (but not values) from B to A. + Adapter::tree(aTree).topologyUnion(Adapter::tree(bTree)); + + CompReplaceOp op(Adapter::tree(aTree)); + + // Copy all active tile values from B to A. + ValueOnCIterT iter = bTree.cbeginValueOn(); + iter.setMaxDepth(iter.getLeafDepth() - 1); // don't descend into leaf nodes + foreach(iter, op, /*threaded=*/false); + + // Copy all active voxel values from B to A. + foreach(Adapter::tree(bTree).cbeginLeaf(), op); +} + + +//////////////////////////////////////// + + +/// Base visitor class for CSG operations +/// (not intended to be used polymorphically, so no virtual functions) +template +class CsgVisitorBase +{ +public: + using TreeT = TreeType; + using ValueT = typename TreeT::ValueType; + using ChildIterT = typename TreeT::LeafNodeType::ChildAllIter; + + enum { STOP = 3 }; + + CsgVisitorBase(const TreeT& aTree, const TreeT& bTree): + mAOutside(aTree.background()), + mAInside(math::negative(mAOutside)), + mBOutside(bTree.background()), + mBInside(math::negative(mBOutside)) + { + const ValueT zero = zeroVal(); + if (!(mAOutside > zero)) { + OPENVDB_THROW(ValueError, + "expected grid A outside value > 0, got " << mAOutside); + } + if (!(mAInside < zero)) { + OPENVDB_THROW(ValueError, + "expected grid A inside value < 0, got " << mAInside); + } + if (!(mBOutside > zero)) { + OPENVDB_THROW(ValueError, + "expected grid B outside value > 0, got " << mBOutside); + } + if (!(mBInside < zero)) { + OPENVDB_THROW(ValueError, + "expected grid B outside value < 0, got " << mBOutside); + } + } + +protected: + ValueT mAOutside, mAInside, mBOutside, mBInside; +}; + + +//////////////////////////////////////// + + +template +struct CsgUnionVisitor: public CsgVisitorBase +{ + using TreeT = TreeType; + using ValueT = typename TreeT::ValueType; + using ChildIterT = typename TreeT::LeafNodeType::ChildAllIter; + + enum { STOP = CsgVisitorBase::STOP }; + + CsgUnionVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase(a, b) {} + + /// Don't process nodes that are at different tree levels. + template + inline int operator()(AIterT&, BIterT&) { return 0; } + + /// Process root and internal nodes. + template + inline int operator()(IterT& aIter, IterT& bIter) + { + ValueT aValue = zeroVal(); + typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue); + if (!aChild && aValue < zeroVal()) { + // A is an inside tile. Leave it alone and stop traversing this branch. + return STOP; + } + + ValueT bValue = zeroVal(); + typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue); + if (!bChild && bValue < zeroVal()) { + // B is an inside tile. Make A an inside tile and stop traversing this branch. + aIter.setValue(this->mAInside); + aIter.setValueOn(bIter.isValueOn()); + delete aChild; + return STOP; + } + + if (!aChild && aValue > zeroVal()) { + // A is an outside tile. If B has a child, transfer it to A, + // otherwise leave A alone. + if (bChild) { + bIter.setValue(this->mBOutside); + bIter.setValueOff(); + bChild->resetBackground(this->mBOutside, this->mAOutside); + aIter.setChild(bChild); // transfer child + delete aChild; + } + return STOP; + } + + // If A has a child and B is an outside tile, stop traversing this branch. + // Continue traversal only if A and B both have children. + return (aChild && bChild) ? 0 : STOP; + } + + /// Process leaf node values. + inline int operator()(ChildIterT& aIter, ChildIterT& bIter) + { + ValueT aValue, bValue; + aIter.probeValue(aValue); + bIter.probeValue(bValue); + if (aValue > bValue) { // a = min(a, b) + aIter.setValue(bValue); + aIter.setValueOn(bIter.isValueOn()); + } + return 0; + } +}; + + + +//////////////////////////////////////// + + +template +struct CsgIntersectVisitor: public CsgVisitorBase +{ + using TreeT = TreeType; + using ValueT = typename TreeT::ValueType; + using ChildIterT = typename TreeT::LeafNodeType::ChildAllIter; + + enum { STOP = CsgVisitorBase::STOP }; + + CsgIntersectVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase(a, b) {} + + /// Don't process nodes that are at different tree levels. + template + inline int operator()(AIterT&, BIterT&) { return 0; } + + /// Process root and internal nodes. + template + inline int operator()(IterT& aIter, IterT& bIter) + { + ValueT aValue = zeroVal(); + typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue); + if (!aChild && !(aValue < zeroVal())) { + // A is an outside tile. Leave it alone and stop traversing this branch. + return STOP; + } + + ValueT bValue = zeroVal(); + typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue); + if (!bChild && !(bValue < zeroVal())) { + // B is an outside tile. Make A an outside tile and stop traversing this branch. + aIter.setValue(this->mAOutside); + aIter.setValueOn(bIter.isValueOn()); + delete aChild; + return STOP; + } + + if (!aChild && aValue < zeroVal()) { + // A is an inside tile. If B has a child, transfer it to A, + // otherwise leave A alone. + if (bChild) { + bIter.setValue(this->mBOutside); + bIter.setValueOff(); + bChild->resetBackground(this->mBOutside, this->mAOutside); + aIter.setChild(bChild); // transfer child + delete aChild; + } + return STOP; + } + + // If A has a child and B is an outside tile, stop traversing this branch. + // Continue traversal only if A and B both have children. + return (aChild && bChild) ? 0 : STOP; + } + + /// Process leaf node values. + inline int operator()(ChildIterT& aIter, ChildIterT& bIter) + { + ValueT aValue, bValue; + aIter.probeValue(aValue); + bIter.probeValue(bValue); + if (aValue < bValue) { // a = max(a, b) + aIter.setValue(bValue); + aIter.setValueOn(bIter.isValueOn()); + } + return 0; + } +}; + + +//////////////////////////////////////// + + +template +struct CsgDiffVisitor: public CsgVisitorBase +{ + using TreeT = TreeType; + using ValueT = typename TreeT::ValueType; + using ChildIterT = typename TreeT::LeafNodeType::ChildAllIter; + + enum { STOP = CsgVisitorBase::STOP }; + + CsgDiffVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase(a, b) {} + + /// Don't process nodes that are at different tree levels. + template + inline int operator()(AIterT&, BIterT&) { return 0; } + + /// Process root and internal nodes. + template + inline int operator()(IterT& aIter, IterT& bIter) + { + ValueT aValue = zeroVal(); + typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue); + if (!aChild && !(aValue < zeroVal())) { + // A is an outside tile. Leave it alone and stop traversing this branch. + return STOP; + } + + ValueT bValue = zeroVal(); + typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue); + if (!bChild && bValue < zeroVal()) { + // B is an inside tile. Make A an inside tile and stop traversing this branch. + aIter.setValue(this->mAOutside); + aIter.setValueOn(bIter.isValueOn()); + delete aChild; + return STOP; + } + + if (!aChild && aValue < zeroVal()) { + // A is an inside tile. If B has a child, transfer it to A, + // otherwise leave A alone. + if (bChild) { + bIter.setValue(this->mBOutside); + bIter.setValueOff(); + bChild->resetBackground(this->mBOutside, this->mAOutside); + aIter.setChild(bChild); // transfer child + bChild->negate(); + delete aChild; + } + return STOP; + } + + // If A has a child and B is an outside tile, stop traversing this branch. + // Continue traversal only if A and B both have children. + return (aChild && bChild) ? 0 : STOP; + } + + /// Process leaf node values. + inline int operator()(ChildIterT& aIter, ChildIterT& bIter) + { + ValueT aValue, bValue; + aIter.probeValue(aValue); + bIter.probeValue(bValue); + bValue = math::negative(bValue); + if (aValue < bValue) { // a = max(a, -b) + aIter.setValue(bValue); + aIter.setValueOn(bIter.isValueOn()); + } + return 0; + } +}; + + +//////////////////////////////////////// + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b); + CsgUnionVisitor visitor(aTree, bTree); + aTree.visit2(bTree, visitor); + if (prune) tools::pruneLevelSet(aTree); +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +csgIntersection(GridOrTreeT& a, GridOrTreeT& b, bool prune) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b); + CsgIntersectVisitor visitor(aTree, bTree); + aTree.visit2(bTree, visitor); + if (prune) tools::pruneLevelSet(aTree); +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b); + CsgDiffVisitor visitor(aTree, bTree); + aTree.visit2(bTree, visitor); + if (prune) tools::pruneLevelSet(aTree); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline typename GridOrTreeT::Ptr +csgUnionCopy(const GridOrTreeT& a, const GridOrTreeT& b) +{ + using Adapter = TreeAdapter; + using TreePtrT = typename Adapter::TreeType::Ptr; + + TreePtrT output = composite::doCSGCopy( + Adapter::tree(a), Adapter::tree(b)); + + return composite::GridOrTreeConstructor::construct(a, output); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline typename GridOrTreeT::Ptr +csgIntersectionCopy(const GridOrTreeT& a, const GridOrTreeT& b) +{ + using Adapter = TreeAdapter; + using TreePtrT = typename Adapter::TreeType::Ptr; + + TreePtrT output = composite::doCSGCopy( + Adapter::tree(a), Adapter::tree(b)); + + return composite::GridOrTreeConstructor::construct(a, output); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline typename GridOrTreeT::Ptr +csgDifferenceCopy(const GridOrTreeT& a, const GridOrTreeT& b) +{ + using Adapter = TreeAdapter; + using TreePtrT = typename Adapter::TreeType::Ptr; + + TreePtrT output = composite::doCSGCopy( + Adapter::tree(a), Adapter::tree(b)); + + return composite::GridOrTreeConstructor::construct(a, output); +} + +//////////////////////////////////////////////////////// + +/// @brief Composite the active values in leaf nodes, i.e. active +/// voxels, of a source tree into a destination tree. +/// +/// @param srcTree source tree from which active voxels are composited. +/// +/// @param dstTree destination tree into which active voxels are composited. +/// +/// @param op a functor of the form void op(T& dst, const T& src), +/// where @c T is the @c ValueType of the tree, that composites +/// a source value into a destination value. By default +/// it copies the value from src to dst. +/// +/// @details All active voxels in the source tree will +/// be active in the destination tree, and their value is +/// determined by a use-defined functor (OpT op) that operates on the +/// source and destination values. The only exception is when +/// the tree type is MaskTree, in which case no functor is +/// needed since by defintion a MaskTree has no values (only topology). +/// +/// @warning This function only operated on leaf node values, +/// i.e. tile values are ignored. +template > +inline void +compActiveLeafVoxels(TreeT &srcTree, TreeT &dstTree, OpT op = composite::CopyOp()) +{ + composite::doCompActiveLeafVoxels(srcTree, dstTree, op); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Dense.h b/openvdb/tools/Dense.h new file mode 100644 index 00000000..e42dc52f --- /dev/null +++ b/openvdb/tools/Dense.h @@ -0,0 +1,582 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file Dense.h +/// +/// @brief This file defines a simple dense grid and efficient +/// converters to and from VDB grids. + +#ifndef OPENVDB_TOOLS_DENSE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_DENSE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include "Prune.h" +#include +#include +#include +#include +#include // for std::pair +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Populate a dense grid with the values of voxels from a sparse grid, +/// where the sparse grid intersects the dense grid. +/// @param sparse an OpenVDB grid or tree from which to copy values +/// @param dense the dense grid into which to copy values +/// @param serial if false, process voxels in parallel +template +void +copyToDense( + const GridOrTreeT& sparse, + DenseT& dense, + bool serial = false); + + +/// @brief Populate a sparse grid with the values of all of the voxels of a dense grid. +/// @param dense the dense grid from which to copy values +/// @param sparse an OpenVDB grid or tree into which to copy values +/// @param tolerance values in the dense grid that are within this tolerance of the sparse +/// grid's background value become inactive background voxels or tiles in the sparse grid +/// @param serial if false, process voxels in parallel +template +void +copyFromDense( + const DenseT& dense, + GridOrTreeT& sparse, + const typename GridOrTreeT::ValueType& tolerance, + bool serial = false); + + +//////////////////////////////////////// + +/// We currently support the following two 3D memory layouts for dense +/// volumes: XYZ, i.e. x is the fastest moving index, and ZYX, i.e. z +/// is the fastest moving index. The ZYX memory layout leads to nested +/// for-loops of the order x, y, z, which we find to be the most +/// intuitive. Hence, ZYX is the layout used throughout VDB. However, +/// other data structures, e.g. Houdini and Maya, employ the XYZ +/// layout. Clearly a dense volume with the ZYX layout converts more +/// efficiently to a VDB, but we support both for convenience. +enum MemoryLayout { LayoutXYZ, LayoutZYX }; + +/// @brief Base class for Dense which is defined below. +/// @note The constructor of this class is protected to prevent direct +/// instantiation. +template class DenseBase; + +/// @brief Partial template specialization of DenseBase. +/// @note ZYX is the memory-layout in VDB. It leads to nested +/// for-loops of the order x, y, z which we find to be the most intuitive. +template +class DenseBase +{ +public: + /// @brief Return the linear offset into this grid's value array given by + /// unsigned coordinates (i, j, k), i.e., coordinates relative to + /// the origin of this grid's bounding box. + /// + /// @warning The input coordinates are assume to be relative to + /// the grid's origin, i.e. minimum of its index bounding box! + inline size_t coordToOffset(size_t i, size_t j, size_t k) const { return i*mX + j*mY + k; } + + /// @brief Return the local coordinate corresponding to the specified linear offset. + /// + /// @warning The returned coordinate is relative to the origin of this + /// grid's bounding box so add dense.origin() to get absolute coordinates. + inline Coord offsetToLocalCoord(size_t n) const + { + const size_t x = n / mX; + n -= mX*x; + const size_t y = n / mY; + return Coord(Coord::ValueType(x), Coord::ValueType(y), Coord::ValueType(n - mY*y)); + } + + /// @brief Return the stride of the array in the x direction ( = dimY*dimZ). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t xStride() const { return mX; } + + /// @brief Return the stride of the array in the y direction ( = dimZ). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t yStride() const { return mY; } + + /// @brief Return the stride of the array in the z direction ( = 1). + /// @note This method is required by both CopyToDense and CopyFromDense. + static size_t zStride() { return 1; } + +protected: + /// Protected constructor so as to prevent direct instantiation + DenseBase(const CoordBBox& bbox) : mBBox(bbox), mY(bbox.dim()[2]), mX(mY*bbox.dim()[1]) {} + + const CoordBBox mBBox;//signed coordinates of the domain represented by the grid + const size_t mY, mX;//strides in the y and x direction +};// end of DenseBase + +/// @brief Partial template specialization of DenseBase. +/// @note This is the memory-layout employed in Houdini and Maya. It leads +/// to nested for-loops of the order z, y, x. +template +class DenseBase +{ +public: + /// @brief Return the linear offset into this grid's value array given by + /// unsigned coordinates (i, j, k), i.e., coordinates relative to + /// the origin of this grid's bounding box. + /// + /// @warning The input coordinates are assume to be relative to + /// the grid's origin, i.e. minimum of its index bounding box! + inline size_t coordToOffset(size_t i, size_t j, size_t k) const { return i + j*mY + k*mZ; } + + /// @brief Return the index coordinate corresponding to the specified linear offset. + /// + /// @warning The returned coordinate is relative to the origin of this + /// grid's bounding box so add dense.origin() to get absolute coordinates. + inline Coord offsetToLocalCoord(size_t n) const + { + const size_t z = n / mZ; + n -= mZ*z; + const size_t y = n / mY; + return Coord(Coord::ValueType(n - mY*y), Coord::ValueType(y), Coord::ValueType(z)); + } + + /// @brief Return the stride of the array in the x direction ( = 1). + /// @note This method is required by both CopyToDense and CopyFromDense. + static size_t xStride() { return 1; } + + /// @brief Return the stride of the array in the y direction ( = dimX). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t yStride() const { return mY; } + + /// @brief Return the stride of the array in the y direction ( = dimX*dimY). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t zStride() const { return mZ; } + +protected: + /// Protected constructor so as to prevent direct instantiation + DenseBase(const CoordBBox& bbox) : mBBox(bbox), mY(bbox.dim()[0]), mZ(mY*bbox.dim()[1]) {} + + const CoordBBox mBBox;//signed coordinates of the domain represented by the grid + const size_t mY, mZ;//strides in the y and z direction +};// end of DenseBase + +/// @brief Dense is a simple dense grid API used by the CopyToDense and +/// CopyFromDense classes defined below. +/// @details Use the Dense class to efficiently produce a dense in-memory +/// representation of an OpenVDB grid. However, be aware that a dense grid +/// could have a memory footprint that is orders of magnitude larger than +/// the sparse grid from which it originates. +/// +/// @note This class can be used as a simple wrapper for existing dense grid +/// classes if they provide access to the raw data array. +/// @note This implementation allows for the 3D memory layout to be +/// defined by the MemoryLayout template parameter (see above for definition). +/// The default memory layout is ZYX since that's the layout used by OpenVDB grids. +template +class Dense : public DenseBase +{ +public: + using ValueType = ValueT; + using BaseT = DenseBase; + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + /// @brief Construct a dense grid with a given range of coordinates. + /// + /// @param bbox the bounding box of the (signed) coordinate range of this grid + /// @throw ValueError if the bounding box is empty. + /// @note The min and max coordinates of the bounding box are inclusive. + Dense(const CoordBBox& bbox) : BaseT(bbox) { this->init(); } + + /// @brief Construct a dense grid with a given range of coordinates and initial value + /// + /// @param bbox the bounding box of the (signed) coordinate range of this grid + /// @param value the initial value of the grid. + /// @throw ValueError if the bounding box is empty. + /// @note The min and max coordinates of the bounding box are inclusive. + Dense(const CoordBBox& bbox, const ValueT& value) : BaseT(bbox) + { + this->init(); + this->fill(value); + } + + /// @brief Construct a dense grid that wraps an external array. + /// + /// @param bbox the bounding box of the (signed) coordinate range of this grid + /// @param data a raw C-style array whose size is commensurate with + /// the coordinate domain of @a bbox + /// + /// @note The data array is assumed to have a stride of one in the @e z direction. + /// @throw ValueError if the bounding box is empty. + /// @note The min and max coordinates of the bounding box are inclusive. + Dense(const CoordBBox& bbox, ValueT* data) : BaseT(bbox), mData(data) + { + if (BaseT::mBBox.empty()) { + OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box"); + } + } + + /// @brief Construct a dense grid with a given origin and dimensions. + /// + /// @param dim the desired dimensions of the grid + /// @param min the signed coordinates of the first voxel in the dense grid + /// @throw ValueError if any of the dimensions are zero. + /// @note The @a min coordinate is inclusive, and the max coordinate will be + /// @a min + @a dim - 1. + Dense(const Coord& dim, const Coord& min = Coord(0)) + : BaseT(CoordBBox(min, min+dim.offsetBy(-1))) + { + this->init(); + } + + /// @brief Return the memory layout for this grid (see above for definitions). + static MemoryLayout memoryLayout() { return Layout; } + + /// @brief Return a raw pointer to this grid's value array. + /// @note This method is required by CopyToDense. + inline ValueT* data() { return mData; } + + /// @brief Return a raw pointer to this grid's value array. + /// @note This method is required by CopyFromDense. + inline const ValueT* data() const { return mData; } + + /// @brief Return the bounding box of the signed index domain of this grid. + /// @note This method is required by both CopyToDense and CopyFromDense. + inline const CoordBBox& bbox() const { return BaseT::mBBox; } + + /// Return the grid's origin in index coordinates. + inline const Coord& origin() const { return BaseT::mBBox.min(); } + + /// @brief Return the number of voxels contained in this grid. + inline Index64 valueCount() const { return BaseT::mBBox.volume(); } + + /// @brief Set the value of the voxel at the given array offset. + inline void setValue(size_t offset, const ValueT& value) { mData[offset] = value; } + + /// @brief Return a const reference to the value of the voxel at the given array offset. + const ValueT& getValue(size_t offset) const { return mData[offset]; } + + /// @brief Return a non-const reference to the value of the voxel at the given array offset. + ValueT& getValue(size_t offset) { return mData[offset]; } + + /// @brief Set the value of the voxel at unsigned index coordinates (i, j, k). + /// @note This is somewhat slower than using an array offset. + inline void setValue(size_t i, size_t j, size_t k, const ValueT& value) + { + mData[BaseT::coordToOffset(i,j,k)] = value; + } + + /// @brief Return a const reference to the value of the voxel + /// at unsigned index coordinates (i, j, k). + /// @note This is somewhat slower than using an array offset. + inline const ValueT& getValue(size_t i, size_t j, size_t k) const + { + return mData[BaseT::coordToOffset(i,j,k)]; + } + + /// @brief Return a non-const reference to the value of the voxel + /// at unsigned index coordinates (i, j, k). + /// @note This is somewhat slower than using an array offset. + inline ValueT& getValue(size_t i, size_t j, size_t k) + { + return mData[BaseT::coordToOffset(i,j,k)]; + } + + /// @brief Set the value of the voxel at the given signed coordinates. + /// @note This is slower than using either an array offset or unsigned index coordinates. + inline void setValue(const Coord& xyz, const ValueT& value) + { + mData[this->coordToOffset(xyz)] = value; + } + + /// @brief Return a const reference to the value of the voxel at the given signed coordinates. + /// @note This is slower than using either an array offset or unsigned index coordinates. + inline const ValueT& getValue(const Coord& xyz) const + { + return mData[this->coordToOffset(xyz)]; + } + + /// @brief Return a non-const reference to the value of the voxel + /// at the given signed coordinates. + /// @note This is slower than using either an array offset or unsigned index coordinates. + inline ValueT& getValue(const Coord& xyz) + { + return mData[this->coordToOffset(xyz)]; + } + + /// @brief Fill this grid with a constant value. + inline void fill(const ValueT& value) + { + size_t size = this->valueCount(); + ValueT* a = mData; + while(size--) *a++ = value; + } + + /// @brief Return the linear offset into this grid's value array given by + /// the specified signed coordinates, i.e., coordinates in the space of + /// this grid's bounding box. + /// + /// @note This method reflects the fact that we assume the same + /// layout of values as an OpenVDB grid, i.e., the fastest coordinate is @e z. + inline size_t coordToOffset(const Coord& xyz) const + { + assert(BaseT::mBBox.isInside(xyz)); + return BaseT::coordToOffset(size_t(xyz[0]-BaseT::mBBox.min()[0]), + size_t(xyz[1]-BaseT::mBBox.min()[1]), + size_t(xyz[2]-BaseT::mBBox.min()[2])); + } + + /// @brief Return the global coordinate corresponding to the specified linear offset. + inline Coord offsetToCoord(size_t n) const + { + return this->offsetToLocalCoord(n) + BaseT::mBBox.min(); + } + + /// @brief Return the memory footprint of this Dense grid in bytes. + inline Index64 memUsage() const + { + return sizeof(*this) + BaseT::mBBox.volume() * sizeof(ValueType); + } + + /// @brief Output a human-readable description of this grid to the + /// specified stream. + void print(const std::string& name = "", std::ostream& os = std::cout) const + { + const Coord dim = BaseT::mBBox.dim(); + os << "Dense Grid"; + if (!name.empty()) os << " \"" << name << "\""; + util::printBytes(os, this->memUsage(), ":\n Memory footprint: "); + os << " Dimensions of grid : " << dim[0] << " x " << dim[1] << " x " << dim[2] << "\n"; + os << " Number of voxels: " << util::formattedInt(this->valueCount()) << "\n"; + os << " Bounding box of voxels: " << BaseT::mBBox << "\n"; + os << " Memory layout: " << (Layout == LayoutZYX ? "ZYX (" : "XYZ (dis") + << "similar to VDB)\n"; + } + +private: + /// @brief Private method to initialize the dense value array. + void init() + { + if (BaseT::mBBox.empty()) { + OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box"); + } + mArray.reset(new ValueT[BaseT::mBBox.volume()]); + mData = mArray.get(); + } + + std::unique_ptr mArray; + ValueT* mData;//raw c-style pointer to values +};// end of Dense + +//////////////////////////////////////// + + +/// @brief Copy an OpenVDB tree into an existing dense grid. +/// +/// @note Only voxels that intersect the dense grid's bounding box are copied +/// from the OpenVDB tree. But both active and inactive voxels are copied, +/// so all existing values in the dense grid are overwritten, regardless of +/// the OpenVDB tree's topology. +template > +class CopyToDense +{ +public: + using DenseT = _DenseT; + using TreeT = _TreeT; + using ValueT = typename TreeT::ValueType; + + CopyToDense(const TreeT& tree, DenseT& dense) + : mRoot(&(tree.root())), mDense(&dense) {} + + void copy(bool serial = false) const + { + if (serial) { + mRoot->copyToDense(mDense->bbox(), *mDense); + } else { + tbb::parallel_for(mDense->bbox(), *this); + } + } + + /// @brief Public method called by tbb::parallel_for + void operator()(const CoordBBox& bbox) const + { + mRoot->copyToDense(bbox, *mDense); + } + +private: + const typename TreeT::RootNodeType* mRoot; + DenseT* mDense; +};// CopyToDense + + +// Convenient wrapper function for the CopyToDense class +template +void +copyToDense(const GridOrTreeT& sparse, DenseT& dense, bool serial) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + + CopyToDense op(Adapter::constTree(sparse), dense); + op.copy(serial); +} + + +//////////////////////////////////////// + + +/// @brief Copy the values from a dense grid into an OpenVDB tree. +/// +/// @details Values in the dense grid that are within a tolerance of +/// the background value are truncated to inactive background voxels or tiles. +/// This allows the tree to form a sparse representation of the dense grid. +/// +/// @note Since this class allocates leaf nodes concurrently it is recommended +/// to use a scalable implementation of @c new like the one provided by TBB, +/// rather than the mutex-protected standard library @c new. +template > +class CopyFromDense +{ +public: + using DenseT = _DenseT; + using TreeT = _TreeT; + using ValueT = typename TreeT::ValueType; + using LeafT = typename TreeT::LeafNodeType; + using AccessorT = tree::ValueAccessor; + + CopyFromDense(const DenseT& dense, TreeT& tree, const ValueT& tolerance) + : mDense(&dense), + mTree(&tree), + mBlocks(nullptr), + mTolerance(tolerance), + mAccessor(tree.empty() ? nullptr : new AccessorT(tree)) + { + } + CopyFromDense(const CopyFromDense& other) + : mDense(other.mDense), + mTree(other.mTree), + mBlocks(other.mBlocks), + mTolerance(other.mTolerance), + mAccessor(other.mAccessor.get() == nullptr ? nullptr : new AccessorT(*mTree)) + { + } + + /// @brief Copy values from the dense grid to the sparse tree. + void copy(bool serial = false) + { + mBlocks = new std::vector(); + const CoordBBox& bbox = mDense->bbox(); + // Pre-process: Construct a list of blocks aligned with (potential) leaf nodes + for (CoordBBox sub=bbox; sub.min()[0] <= bbox.max()[0]; sub.min()[0] = sub.max()[0] + 1) { + for (sub.min()[1] = bbox.min()[1]; sub.min()[1] <= bbox.max()[1]; + sub.min()[1] = sub.max()[1] + 1) + { + for (sub.min()[2] = bbox.min()[2]; sub.min()[2] <= bbox.max()[2]; + sub.min()[2] = sub.max()[2] + 1) + { + sub.max() = Coord::minComponent(bbox.max(), + (sub.min()&(~(LeafT::DIM-1u))).offsetBy(LeafT::DIM-1u)); + mBlocks->push_back(Block(sub)); + } + } + } + + // Multi-threaded process: Convert dense grid into leaf nodes and tiles + if (serial) { + (*this)(tbb::blocked_range(0, mBlocks->size())); + } else { + tbb::parallel_for(tbb::blocked_range(0, mBlocks->size()), *this); + } + + // Post-process: Insert leaf nodes and tiles into the tree, and prune the tiles only! + tree::ValueAccessor acc(*mTree); + for (size_t m=0, size = mBlocks->size(); m &r) const + { + assert(mBlocks); + LeafT* leaf = new LeafT(); + + for (size_t m=r.begin(), n=0, end = r.end(); m != end; ++m, ++n) { + + Block& block = (*mBlocks)[m]; + const CoordBBox &bbox = block.bbox; + + if (mAccessor.get() == nullptr) {//i.e. empty target tree + leaf->fill(mTree->background(), false); + } else {//account for existing leaf nodes in the target tree + if (const LeafT* target = mAccessor->probeConstLeaf(bbox.min())) { + (*leaf) = (*target); + } else { + ValueT value = zeroVal(); + bool state = mAccessor->probeValue(bbox.min(), value); + leaf->fill(value, state); + } + } + + leaf->copyFromDense(bbox, *mDense, mTree->background(), mTolerance); + + if (!leaf->isConstant(block.tile.first, block.tile.second, mTolerance)) { + leaf->setOrigin(bbox.min() & (~(LeafT::DIM - 1))); + block.leaf = leaf; + leaf = new LeafT(); + } + }// loop over blocks + + delete leaf; + } + +private: + struct Block { + CoordBBox bbox; + LeafT* leaf; + std::pair tile; + Block(const CoordBBox& b) : bbox(b), leaf(nullptr) {} + }; + + const DenseT* mDense; + TreeT* mTree; + std::vector* mBlocks; + ValueT mTolerance; + std::unique_ptr mAccessor; +};// CopyFromDense + + +// Convenient wrapper function for the CopyFromDense class +template +void +copyFromDense(const DenseT& dense, GridOrTreeT& sparse, + const typename GridOrTreeT::ValueType& tolerance, bool serial) +{ + using Adapter = TreeAdapter; + using TreeT = typename Adapter::TreeType; + + CopyFromDense op(dense, Adapter::tree(sparse), tolerance); + op.copy(serial); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_DENSE_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/DenseSparseTools.h b/openvdb/tools/DenseSparseTools.h new file mode 100644 index 00000000..93640f87 --- /dev/null +++ b/openvdb/tools/DenseSparseTools.h @@ -0,0 +1,1192 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include "Dense.h" +#include // for std::min() +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Selectively extract and transform data from a dense grid, producing a +/// sparse tree with leaf nodes only (e.g. create a tree from the square +/// of values greater than a cutoff.) +/// @param dense A dense grid that acts as a data source +/// @param functor A functor that selects and transforms data for output +/// @param background The background value of the resulting sparse grid +/// @param threaded Option to use threaded or serial code path +/// @return @c Ptr to tree with the valuetype and configuration defined +/// by typedefs in the @c functor. +/// @note To achieve optimal sparsity consider calling the prune() +/// method on the result. +/// @note To simply copy the all the data from a Dense grid to a +/// OpenVDB Grid, use tools::copyFromDense() for better performance. +/// +/// The type of the sparse tree is determined by the specified OtpType +/// functor by means of the typedef OptType::ResultTreeType +/// +/// The OptType function is responsible for the the transformation of +/// dense grid data to sparse grid data on a per-voxel basis. +/// +/// Only leaf nodes with active values will be added to the sparse grid. +/// +/// The OpType must struct that defines a the minimal form +/// @code +/// struct ExampleOp +/// { +/// using ResultTreeType = DesiredTreeType; +/// +/// template +/// void OpType::operator() (const DenseValueType a, const IndexOrCoord& ijk, +/// ResultTreeType::LeafNodeType* leaf); +/// }; +/// @endcode +/// +/// For example, to generate a tree with valuesOn +/// at locations greater than a given maskvalue +/// @code +/// template +/// class Rule +/// { +/// public: +/// // Standard tree type (e.g. MaskTree or FloatTree in openvdb.h) +/// using ResultTreeType = typename openvdb::tree::Tree4::Type; +/// +/// using ResultLeafNodeType = typename ResultTreeType::LeafNodeType; +/// using ResultValueType = typename ResultTreeType::ValueType; +/// +/// using DenseValueType = float; +/// +/// using Index = vdbmath::Coord::ValueType; +/// +/// Rule(const DenseValueType& value): mMaskValue(value){}; +/// +/// template +/// void operator()(const DenseValueType& a, const IndexOrCoord& offset, +/// ResultLeafNodeType* leaf) const +/// { +/// if (a > mMaskValue) { +/// leaf->setValueOn(offset, a); +/// } +/// } +/// +/// private: +/// const DenseValueType mMaskValue; +/// }; +/// @endcode +template +typename OpType::ResultTreeType::Ptr +extractSparseTree(const DenseType& dense, const OpType& functor, + const typename OpType::ResultValueType& background, + bool threaded = true); + +/// This struct that aids template resolution of a new tree type +/// has the same configuration at TreeType, but the ValueType from +/// DenseType. +template +struct DSConverter +{ + using ValueType = typename DenseType::ValueType; + using Type = typename TreeType::template ValueConverter::Type; +}; + + +/// @brief Copy data from the intersection of a sparse tree and a dense input grid. +/// The resulting tree has the same configuration as the sparse tree, but holds +/// the data type specified by the dense input. +/// @param dense A dense grid that acts as a data source +/// @param mask The active voxels and tiles intersected with dense define iteration mask +/// @param background The background value of the resulting sparse grid +/// @param threaded Option to use threaded or serial code path +/// @return @c Ptr to tree with the same configuration as @c mask but of value type +/// defined by @c dense. +template +typename DSConverter::Type::Ptr +extractSparseTreeWithMask(const DenseType& dense, + const MaskTreeType& mask, + const typename DenseType::ValueType& background, + bool threaded = true); + + +/// Apply a point-wise functor to the intersection of a dense grid and a given bounding box +/// @param dense A dense grid to be transformed +/// @param bbox Index space bounding box, define region where the transformation is applied +/// @param op A functor that acts on the dense grid value type +/// @param parallel Used to select multithreaded or single threaded +/// Minimally, the @c op class has to support a @c operator() method, +/// @code +/// // Square values in a grid +/// struct Op +/// { +/// ValueT operator()(const ValueT& in) const +/// { +/// // do work +/// ValueT result = in * in; +/// +/// return result; +/// } +/// }; +/// @endcode +/// NB: only Dense grids with memory layout zxy are supported +template +void transformDense(Dense& dense, + const openvdb::CoordBBox& bbox, const OpType& op, bool parallel=true); + +/// We currrently support the following operations when compositing sparse +/// data into a dense grid. +enum DSCompositeOp { + DS_OVER, DS_ADD, DS_SUB, DS_MIN, DS_MAX, DS_MULT, DS_SET +}; + +/// @brief Composite data from a sparse tree into a dense array of the same value type. +/// @param dense Dense grid to be altered by the operation +/// @param source Sparse data to composite into @c dense +/// @param alpha Sparse Alpha mask used in compositing operations. +/// @param beta Constant multiplier on src +/// @param strength Constant multiplier on alpha +/// @param threaded Enable threading for this operation. +template +void compositeToDense(Dense& dense, + const TreeT& source, + const TreeT& alpha, + const typename TreeT::ValueType beta, + const typename TreeT::ValueType strength, + bool threaded = true); + + +/// @brief Functor-based class used to extract data that satisfies some +/// criteria defined by the embedded @c OpType functor. The @c extractSparseTree +/// function wraps this class. +template +class SparseExtractor +{ +public: + using Index = openvdb::math::Coord::ValueType; + + using DenseValueType = typename DenseType::ValueType; + using ResultTreeType = typename OpType::ResultTreeType; + using ResultValueType = typename ResultTreeType::ValueType; + using ResultLeafNodeType = typename ResultTreeType::LeafNodeType; + using MaskTree = typename ResultTreeType::template ValueConverter::Type; + + using Range3d = tbb::blocked_range3d; + +private: + const DenseType& mDense; + const OpType& mFunctor; + const ResultValueType mBackground; + const openvdb::math::CoordBBox mBBox; + const Index mWidth; + typename ResultTreeType::Ptr mMask; + openvdb::math::Coord mMin; + +public: + SparseExtractor(const DenseType& dense, const OpType& functor, + const ResultValueType background) : + mDense(dense), mFunctor(functor), + mBackground(background), + mBBox(dense.bbox()), + mWidth(ResultLeafNodeType::DIM), + mMask( new ResultTreeType(mBackground)) + {} + + SparseExtractor(const DenseType& dense, + const openvdb::math::CoordBBox& bbox, + const OpType& functor, + const ResultValueType background) : + mDense(dense), mFunctor(functor), + mBackground(background), + mBBox(bbox), + mWidth(ResultLeafNodeType::DIM), + mMask( new ResultTreeType(mBackground)) + { + // mBBox must be inside the coordinate rage of the dense grid + if (!dense.bbox().isInside(mBBox)) { + OPENVDB_THROW(ValueError, "Data extraction window out of bound"); + } + } + + SparseExtractor(SparseExtractor& other, tbb::split): + mDense(other.mDense), mFunctor(other.mFunctor), + mBackground(other.mBackground), mBBox(other.mBBox), + mWidth(other.mWidth), + mMask(new ResultTreeType(mBackground)), + mMin(other.mMin) + {} + + typename ResultTreeType::Ptr extract(bool threaded = true) + { + // Construct 3D range of leaf nodes that + // intersect mBBox. + + // Snap the bbox to nearest leaf nodes min and max + + openvdb::math::Coord padded_min = mBBox.min(); + openvdb::math::Coord padded_max = mBBox.max(); + + + padded_min &= ~(mWidth - 1); + padded_max &= ~(mWidth - 1); + + padded_max[0] += mWidth - 1; + padded_max[1] += mWidth - 1; + padded_max[2] += mWidth - 1; + + + // number of leaf nodes in each direction + // division by leaf width, e.g. 8 in most cases + + const Index xleafCount = ( padded_max.x() - padded_min.x() + 1 ) / mWidth; + const Index yleafCount = ( padded_max.y() - padded_min.y() + 1 ) / mWidth; + const Index zleafCount = ( padded_max.z() - padded_min.z() + 1 ) / mWidth; + + mMin = padded_min; + + Range3d leafRange(0, xleafCount, 1, + 0, yleafCount, 1, + 0, zleafCount, 1); + + // Iterate over the leafnodes applying *this as a functor. + if (threaded) { + tbb::parallel_reduce(leafRange, *this); + } else { + (*this)(leafRange); + } + + return mMask; + } + + void operator()(const Range3d& range) + { + ResultLeafNodeType* leaf = nullptr; + + // Unpack the range3d item. + const Index imin = range.pages().begin(); + const Index imax = range.pages().end(); + + const Index jmin = range.rows().begin(); + const Index jmax = range.rows().end(); + + const Index kmin = range.cols().begin(); + const Index kmax = range.cols().end(); + + + // loop over all the candidate leafs. Adding only those with 'true' values + // to the tree + + for (Index i = imin; i < imax; ++i) { + for (Index j = jmin; j < jmax; ++j) { + for (Index k = kmin; k < kmax; ++k) { + + // Calculate the origin of candidate leaf + const openvdb::math::Coord origin = + mMin + openvdb::math::Coord(mWidth * i, + mWidth * j, + mWidth * k ); + + if (leaf == nullptr) { + leaf = new ResultLeafNodeType(origin, mBackground); + } else { + leaf->setOrigin(origin); + leaf->fill(mBackground); + leaf->setValuesOff(); + } + + // The bounding box for this leaf + + openvdb::math::CoordBBox localBBox = leaf->getNodeBoundingBox(); + + // Shrink to the intersection with mBBox (i.e. the dense + // volume) + + localBBox.intersect(mBBox); + + // Early out for non-intersecting leafs + + if (localBBox.empty()) continue; + + + const openvdb::math::Coord start = localBBox.getStart(); + const openvdb::math::Coord end = localBBox.getEnd(); + + // Order the looping to respect the memory layout in + // the Dense source + + if (mDense.memoryLayout() == openvdb::tools::LayoutZYX) { + + openvdb::math::Coord ijk; + Index offset; + const DenseValueType* dp; + for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) { + for (ijk[2] = start.z(), + offset = ResultLeafNodeType::coordToOffset(ijk), + dp = &mDense.getValue(ijk); + ijk[2] < end.z(); ++ijk[2], ++offset, ++dp) { + + mFunctor(*dp, offset, leaf); + } + } + } + + } else { + + openvdb::math::Coord ijk; + const DenseValueType* dp; + for (ijk[2] = start.z(); ijk[2] < end.z(); ++ijk[2]) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1]) { + for (ijk[0] = start.x(), + dp = &mDense.getValue(ijk); + ijk[0] < end.x(); ++ijk[0], ++dp) { + + mFunctor(*dp, ijk, leaf); + + } + } + } + } + + // Only add non-empty leafs (empty is defined as all inactive) + + if (!leaf->isEmpty()) { + mMask->addLeaf(leaf); + leaf = nullptr; + } + + } + } + } + + // Clean up an unused leaf. + + if (leaf != nullptr) delete leaf; + } + + void join(SparseExtractor& rhs) { + mMask->merge(*rhs.mMask); + } +}; // class SparseExtractor + + +template +typename OpType::ResultTreeType::Ptr +extractSparseTree(const DenseType& dense, const OpType& functor, + const typename OpType::ResultValueType& background, + bool threaded) +{ + // Construct the mask using a parallel reduce pattern. + // Each thread computes disjoint mask-trees. The join merges + // into a single tree. + + SparseExtractor extractor(dense, functor, background); + + return extractor.extract(threaded); +} + + +/// @brief Functor-based class used to extract data from a dense grid, at +/// the index-space intersection with a supplied mask in the form of a sparse tree. +/// The @c extractSparseTreeWithMask function wraps this class. +template +class SparseMaskedExtractor +{ +public: + using _ResultTreeType = typename DSConverter::Type; + using ResultTreeType = _ResultTreeType; + using ResultLeafNodeType = typename ResultTreeType::LeafNodeType; + using ResultValueType = typename ResultTreeType::ValueType; + using DenseValueType = ResultValueType; + + using MaskTree = typename ResultTreeType::template ValueConverter::Type; + using MaskLeafCIter = typename MaskTree::LeafCIter; + using MaskLeafVec = std::vector; + + + SparseMaskedExtractor(const DenseType& dense, + const ResultValueType& background, + const MaskLeafVec& leafVec + ): + mDense(dense), mBackground(background), mBBox(dense.bbox()), + mLeafVec(leafVec), + mResult(new ResultTreeType(mBackground)) + {} + + SparseMaskedExtractor(const SparseMaskedExtractor& other, tbb::split): + mDense(other.mDense), mBackground(other.mBackground), mBBox(other.mBBox), + mLeafVec(other.mLeafVec), mResult( new ResultTreeType(mBackground)) + {} + + typename ResultTreeType::Ptr extract(bool threaded = true) + { + tbb::blocked_range range(0, mLeafVec.size()); + + if (threaded) { + tbb::parallel_reduce(range, *this); + } else { + (*this)(range); + } + + return mResult; + } + + // Used in looping over leaf nodes in the masked grid + // and using the active mask to select data to + void operator()(const tbb::blocked_range& range) + { + ResultLeafNodeType* leaf = nullptr; + + // loop over all the candidate leafs. Adding only those with 'true' values + // to the tree + + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + + const typename MaskTree::LeafNodeType* maskLeaf = mLeafVec[idx]; + + // The bounding box for this leaf + + openvdb::math::CoordBBox localBBox = maskLeaf->getNodeBoundingBox(); + + // Shrink to the intersection with the dense volume + + localBBox.intersect(mBBox); + + // Early out if there was no intersection + + if (localBBox.empty()) continue; + + // Reset or allocate the target leaf + + if (leaf == nullptr) { + leaf = new ResultLeafNodeType(maskLeaf->origin(), mBackground); + } else { + leaf->setOrigin(maskLeaf->origin()); + leaf->fill(mBackground); + leaf->setValuesOff(); + } + + // Iterate over the intersecting bounding box + // copying active values to the result tree + + const openvdb::math::Coord start = localBBox.getStart(); + const openvdb::math::Coord end = localBBox.getEnd(); + + openvdb::math::Coord ijk; + + if (mDense.memoryLayout() == openvdb::tools::LayoutZYX + && maskLeaf->isDense()) { + + Index offset; + const DenseValueType* src; + for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) { + for (ijk[2] = start.z(), + offset = ResultLeafNodeType::coordToOffset(ijk), + src = &mDense.getValue(ijk); + ijk[2] < end.z(); ++ijk[2], ++offset, ++src) { + + // copy into leaf + leaf->setValueOn(offset, *src); + } + + } + } + + } else { + + Index offset; + for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) { + for (ijk[2] = start.z(), + offset = ResultLeafNodeType::coordToOffset(ijk); + ijk[2] < end.z(); ++ijk[2], ++offset) { + + if (maskLeaf->isValueOn(offset)) { + const ResultValueType denseValue = mDense.getValue(ijk); + leaf->setValueOn(offset, denseValue); + } + } + } + } + } + // Only add non-empty leafs (empty is defined as all inactive) + + if (!leaf->isEmpty()) { + mResult->addLeaf(leaf); + leaf = nullptr; + } + } + + // Clean up an unused leaf. + + if (leaf != nullptr) delete leaf; + } + + void join(SparseMaskedExtractor& rhs) { + mResult->merge(*rhs.mResult); + } + + +private: + const DenseType& mDense; + const ResultValueType mBackground; + const openvdb::math::CoordBBox& mBBox; + const MaskLeafVec& mLeafVec; + + typename ResultTreeType::Ptr mResult; + +}; // class SparseMaskedExtractor + + +/// @brief a simple utility class used by @c extractSparseTreeWithMask +template +struct ExtractAll +{ + using ResultTreeType = _ResultTreeType; + using ResultLeafNodeType = typename ResultTreeType::LeafNodeType; + + template inline void + operator()(const DenseValueType& a, const CoordOrIndex& offset, ResultLeafNodeType* leaf) const + { + leaf->setValueOn(offset, a); + } +}; + + +template +typename DSConverter::Type::Ptr +extractSparseTreeWithMask(const DenseType& dense, + const MaskTreeType& maskProxy, + const typename DenseType::ValueType& background, + bool threaded) +{ + using LeafExtractor = SparseMaskedExtractor; + using DenseValueType = typename LeafExtractor::DenseValueType; + using ResultTreeType = typename LeafExtractor::ResultTreeType; + using MaskLeafVec = typename LeafExtractor::MaskLeafVec; + using MaskTree = typename LeafExtractor::MaskTree; + using MaskLeafCIter = typename LeafExtractor::MaskLeafCIter; + using ExtractionRule = ExtractAll; + + // Use Mask tree to hold the topology + + MaskTree maskTree(maskProxy, false, TopologyCopy()); + + // Construct an array of pointers to the mask leafs. + + const size_t leafCount = maskTree.leafCount(); + MaskLeafVec leafarray(leafCount); + MaskLeafCIter leafiter = maskTree.cbeginLeaf(); + for (size_t n = 0; n != leafCount; ++n, ++leafiter) { + leafarray[n] = leafiter.getLeaf(); + } + + + // Extract the data that is masked leaf nodes in the mask. + + LeafExtractor leafextractor(dense, background, leafarray); + typename ResultTreeType::Ptr resultTree = leafextractor.extract(threaded); + + + // Extract data that is masked by tiles in the mask. + + + // Loop over the mask tiles, extracting the data into new trees. + // These trees will be leaf-orthogonal to the leafTree (i.e. no leaf + // nodes will overlap). Merge these trees into the result. + + typename MaskTreeType::ValueOnCIter tileIter(maskProxy); + tileIter.setMaxDepth(MaskTreeType::ValueOnCIter::LEAF_DEPTH - 1); + + // Return the leaf tree if the mask had no tiles + + if (!tileIter) return resultTree; + + ExtractionRule allrule; + + // Loop over the tiles in series, but the actual data extraction + // is in parallel. + + CoordBBox bbox; + for ( ; tileIter; ++tileIter) { + + // Find the intersection of the tile with the dense grid. + + tileIter.getBoundingBox(bbox); + bbox.intersect(dense.bbox()); + + if (bbox.empty()) continue; + + SparseExtractor copyData(dense, bbox, allrule, background); + typename ResultTreeType::Ptr fromTileTree = copyData.extract(threaded); + resultTree->merge(*fromTileTree); + } + + return resultTree; +} + + +/// @brief Class that applies a functor to the index space intersection +/// of a prescribed bounding box and the dense grid. +/// NB: This class only supports DenseGrids with ZYX memory layout. +template +class DenseTransformer +{ +public: + using ValueT = _ValueT; + using DenseT = Dense; + using IntType = openvdb::math::Coord::ValueType; + using RangeType = tbb::blocked_range2d; + +private: + DenseT& mDense; + const OpType& mOp; + openvdb::math::CoordBBox mBBox; + +public: + DenseTransformer(DenseT& dense, const openvdb::math::CoordBBox& bbox, const OpType& functor): + mDense(dense), mOp(functor), mBBox(dense.bbox()) + { + // The iteration space is the intersection of the + // input bbox and the index-space covered by the dense grid + mBBox.intersect(bbox); + } + + DenseTransformer(const DenseTransformer& other) : + mDense(other.mDense), mOp(other.mOp), mBBox(other.mBBox) {} + + void apply(bool threaded = true) { + + // Early out if the iteration space is empty + + if (mBBox.empty()) return; + + + const openvdb::math::Coord start = mBBox.getStart(); + const openvdb::math::Coord end = mBBox.getEnd(); + + // The iteration range only the slower two directions. + const RangeType range(start.x(), end.x(), 1, + start.y(), end.y(), 1); + + if (threaded) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + } + + void operator()(const RangeType& range) const { + + // The stride in the z-direction. + // Note: the bbox is [inclusive, inclusive] + + const size_t zlength = size_t(mBBox.max().z() - mBBox.min().z() + 1); + + const IntType imin = range.rows().begin(); + const IntType imax = range.rows().end(); + const IntType jmin = range.cols().begin(); + const IntType jmax = range.cols().end(); + + + openvdb::math::Coord xyz(imin, jmin, mBBox.min().z()); + for (xyz[0] = imin; xyz[0] != imax; ++xyz[0]) { + for (xyz[1] = jmin; xyz[1] != jmax; ++xyz[1]) { + + mOp.transform(mDense, xyz, zlength); + } + } + } +}; // class DenseTransformer + + +/// @brief a wrapper struct used to avoid unnecessary computation of +/// memory access from @c Coord when all offsets are guaranteed to be +/// within the dense grid. +template +struct ContiguousOp +{ + ContiguousOp(const PointWiseOp& op) : mOp(op){} + + using DenseT = Dense; + inline void transform(DenseT& dense, openvdb::math::Coord& ijk, size_t size) const + { + ValueT* dp = const_cast(&dense.getValue(ijk)); + + for (size_t offset = 0; offset < size; ++offset) { + dp[offset] = mOp(dp[offset]); + } + } + + const PointWiseOp mOp; +}; + + +/// Apply a point-wise functor to the intersection of a dense grid and a given bounding box +template +void +transformDense(Dense& dense, + const openvdb::CoordBBox& bbox, + const PointwiseOpT& functor, bool parallel) +{ + using OpT = ContiguousOp; + + // Convert the Op so it operates on a contiguous line in memory + + OpT op(functor); + + // Apply to the index space intersection in the dense grid + DenseTransformer transformer(dense, bbox, op); + transformer.apply(parallel); +} + + +template +class SparseToDenseCompositor +{ +public: + using TreeT = _TreeT; + using ValueT = typename TreeT::ValueType; + using LeafT = typename TreeT::LeafNodeType; + using MaskTreeT = typename TreeT::template ValueConverter::Type; + using MaskLeafT = typename MaskTreeT::LeafNodeType; + using DenseT = Dense; + using Index = openvdb::math::Coord::ValueType; + using Range3d = tbb::blocked_range3d; + + SparseToDenseCompositor(DenseT& dense, const TreeT& source, const TreeT& alpha, + const ValueT beta, const ValueT strength) : + mDense(dense), mSource(source), mAlpha(alpha), mBeta(beta), mStrength(strength) + {} + + SparseToDenseCompositor(const SparseToDenseCompositor& other): + mDense(other.mDense), mSource(other.mSource), mAlpha(other.mAlpha), + mBeta(other.mBeta), mStrength(other.mStrength) {} + + + void sparseComposite(bool threaded) + { + const ValueT beta = mBeta; + const ValueT strength = mStrength; + + // construct a tree that defines the iteration space + + MaskTreeT maskTree(mSource, false /*background*/, openvdb::TopologyCopy()); + maskTree.topologyUnion(mAlpha); + + // Composite regions that are represented by leafnodes in either mAlpha or mSource + // Parallelize over bool-leafs + + openvdb::tree::LeafManager maskLeafs(maskTree); + maskLeafs.foreach(*this, threaded); + + // Composite regions that are represented by tiles + // Parallelize within each tile. + + typename MaskTreeT::ValueOnCIter citer = maskTree.cbeginValueOn(); + citer.setMaxDepth(MaskTreeT::ValueOnCIter::LEAF_DEPTH - 1); + + if (!citer) return; + + typename tree::ValueAccessor alphaAccessor(mAlpha); + typename tree::ValueAccessor sourceAccessor(mSource); + + for (; citer; ++citer) { + + const openvdb::math::Coord org = citer.getCoord(); + + // Early out if both alpha and source are zero in this tile. + + const ValueT alphaValue = alphaAccessor.getValue(org); + const ValueT sourceValue = sourceAccessor.getValue(org); + + if (openvdb::math::isZero(alphaValue) && + openvdb::math::isZero(sourceValue)) continue; + + // Compute overlap of tile with the dense grid + + openvdb::math::CoordBBox localBBox = citer.getBoundingBox(); + localBBox.intersect(mDense.bbox()); + + // Early out if there is no intersection + + if (localBBox.empty()) continue; + + // Composite the tile-uniform values into the dense grid. + compositeFromTile(mDense, localBBox, sourceValue, + alphaValue, beta, strength, threaded); + } + } + + // Composites leaf values where the alpha values are active. + // Used in sparseComposite + void inline operator()(const MaskLeafT& maskLeaf, size_t /*i*/) const + { + using ULeaf = UniformLeaf; + openvdb::math::CoordBBox localBBox = maskLeaf.getNodeBoundingBox(); + localBBox.intersect(mDense.bbox()); + + // Early out for non-overlapping leafs + + if (localBBox.empty()) return; + + const openvdb::math::Coord org = maskLeaf.origin(); + const LeafT* alphaLeaf = mAlpha.probeLeaf(org); + const LeafT* sourceLeaf = mSource.probeLeaf(org); + + if (!sourceLeaf) { + + // Create a source leaf proxy with the correct value + ULeaf uniformSource(mSource.getValue(org)); + + if (!alphaLeaf) { + + // Create an alpha leaf proxy with the correct value + ULeaf uniformAlpha(mAlpha.getValue(org)); + + compositeFromLeaf(mDense, localBBox, uniformSource, uniformAlpha, + mBeta, mStrength); + } else { + + compositeFromLeaf(mDense, localBBox, uniformSource, *alphaLeaf, + mBeta, mStrength); + } + } else { + if (!alphaLeaf) { + + // Create an alpha leaf proxy with the correct value + ULeaf uniformAlpha(mAlpha.getValue(org)); + + compositeFromLeaf(mDense, localBBox, *sourceLeaf, uniformAlpha, + mBeta, mStrength); + } else { + + compositeFromLeaf(mDense, localBBox, *sourceLeaf, *alphaLeaf, + mBeta, mStrength); + } + } + } + // i.e. it assumes that all valueOff Alpha voxels have value 0. + + template + inline static void compositeFromLeaf(DenseT& dense, const openvdb::math::CoordBBox& bbox, + const LeafT1& source, const LeafT2& alpha, + const ValueT beta, const ValueT strength) + { + using IntType = openvdb::math::Coord::ValueType; + + const ValueT sbeta = strength * beta; + openvdb::math::Coord ijk = bbox.min(); + + + if (alpha.isDense() /*all active values*/) { + + // Optimal path for dense alphaLeaf + const IntType size = bbox.max().z() + 1 - bbox.min().z(); + + for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) { + for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) { + + ValueT* d = const_cast(&dense.getValue(ijk)); + const ValueT* a = &alpha.getValue(ijk); + const ValueT* s = &source.getValue(ijk); + + for (IntType idx = 0; idx < size; ++idx) { + d[idx] = CompositeMethod::apply(d[idx], a[idx], s[idx], + strength, beta, sbeta); + } + } + } + } else { + + // AlphaLeaf has non-active cells. + + for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) { + for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) { + for (ijk[2] = bbox.min().z(); ijk[2] < bbox.max().z() + 1; ++ijk[2]) { + + if (alpha.isValueOn(ijk)) { + dense.setValue(ijk, CompositeMethod::apply(dense.getValue(ijk), + alpha.getValue(ijk), source.getValue(ijk), strength, beta, sbeta)); + } + } + } + } + } + } + + inline static void compositeFromTile(DenseT& dense, openvdb::math::CoordBBox& bbox, + const ValueT& sourceValue, const ValueT& alphaValue, + const ValueT& beta, const ValueT& strength, + bool threaded) + { + using TileTransformer = UniformTransformer; + TileTransformer functor(sourceValue, alphaValue, beta, strength); + + // Transform the data inside the bbox according to the TileTranformer. + + transformDense(dense, bbox, functor, threaded); + } + + void denseComposite(bool threaded) + { + /// Construct a range that corresponds to the + /// bounding box of the dense volume + const openvdb::math::CoordBBox& bbox = mDense.bbox(); + + Range3d range(bbox.min().x(), bbox.max().x(), LeafT::DIM, + bbox.min().y(), bbox.max().y(), LeafT::DIM, + bbox.min().z(), bbox.max().z(), LeafT::DIM); + + // Iterate over the range, compositing into + // the dense grid using value accessors for + // sparse the grids. + if (threaded) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + } + + // Composites a dense region using value accessors + // into a dense grid + void operator()(const Range3d& range) const + { + // Use value accessors to alpha and source + + typename tree::ValueAccessor alphaAccessor(mAlpha); + typename tree::ValueAccessor sourceAccessor(mSource); + + const ValueT strength = mStrength; + const ValueT beta = mBeta; + const ValueT sbeta = strength * beta; + + // Unpack the range3d item. + const Index imin = range.pages().begin(); + const Index imax = range.pages().end(); + + const Index jmin = range.rows().begin(); + const Index jmax = range.rows().end(); + + const Index kmin = range.cols().begin(); + const Index kmax = range.cols().end(); + + openvdb::Coord ijk; + for (ijk[0] = imin; ijk[0] < imax; ++ijk[0]) { + for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) { + for (ijk[2] = kmin; ijk[2] < kmax; ++ijk[2]) { + const ValueT d_old = mDense.getValue(ijk); + const ValueT& alpha = alphaAccessor.getValue(ijk); + const ValueT& src = sourceAccessor.getValue(ijk); + + mDense.setValue(ijk, + CompositeMethod::apply(d_old, alpha, src, strength, beta, sbeta)); + } + } + } + } + +private: + // Internal class that wraps the templated composite method + // for use when both alpha and source are uniform over + // a prescribed bbox (e.g. a tile). + class UniformTransformer + { + public: + UniformTransformer(const ValueT& source, const ValueT& alpha, const ValueT& _beta, + const ValueT& _strength) : + mSource(source), mAlpha(alpha), mBeta(_beta), + mStrength(_strength), mSBeta(_strength * _beta) + {} + + ValueT operator()(const ValueT& input) const + { + return CompositeMethod::apply(input, mAlpha, mSource, mStrength, mBeta, mSBeta); + } + + private: + const ValueT mSource; + const ValueT mAlpha; + const ValueT mBeta; + const ValueT mStrength; + const ValueT mSBeta; + }; + + + // Simple Class structure that mimics a leaf + // with uniform values. Holds LeafT::DIM copies + // of a value in an array. + struct Line { ValueT mValues[LeafT::DIM]; }; + class UniformLeaf : private Line + { + public: + using ValueT = typename LeafT::ValueType; + + using BaseT = Line; + UniformLeaf(const ValueT& value) : BaseT(init(value)) {} + + static const BaseT init(const ValueT& value) { + BaseT tmp; + for (openvdb::Index i = 0; i < LeafT::DIM; ++i) { + tmp.mValues[i] = value; + } + return tmp; + } + + bool isDense() const { return true; } + bool isValueOn(openvdb::math::Coord&) const { return true; } + + const ValueT& getValue(const openvdb::math::Coord&) const { return BaseT::mValues[0]; } + }; + +private: + DenseT& mDense; + const TreeT& mSource; + const TreeT& mAlpha; + ValueT mBeta; + ValueT mStrength; +}; // class SparseToDenseCompositor + + +namespace ds +{ + //@{ + /// @brief Point wise methods used to apply various compositing operations. + template + struct OpOver + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT strength, + const ValueT beta, + const ValueT /*sbeta*/) + { return (u + strength * alpha * (beta * v - u)); } + }; + + template + struct OpAdd + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT /*strength*/, + const ValueT /*beta*/, + const ValueT sbeta) + { return (u + sbeta * alpha * v); } + }; + + template + struct OpSub + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT /*strength*/, + const ValueT /*beta*/, + const ValueT sbeta) + { return (u - sbeta * alpha * v); } + }; + + template + struct OpMin + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT s /*trength*/, + const ValueT beta, + const ValueT /*sbeta*/) + { return ( ( 1 - s * alpha) * u + s * alpha * std::min(u, beta * v) ); } + }; + + template + struct OpMax + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT s/*trength*/, + const ValueT beta, + const ValueT /*sbeta*/) + { return ( ( 1 - s * alpha ) * u + s * alpha * std::min(u, beta * v) ); } + }; + + template + struct OpMult + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT s/*trength*/, + const ValueT /*beta*/, + const ValueT sbeta) + { return ( ( 1 + alpha * (sbeta * v - s)) * u ); } + }; + //@} + + //@{ + /// Translator that converts an enum to compositing functor types + template + struct CompositeFunctorTranslator{}; + + template + struct CompositeFunctorTranslator{ using OpT = OpOver; }; + + template + struct CompositeFunctorTranslator{ using OpT = OpAdd; }; + + template + struct CompositeFunctorTranslator{ using OpT = OpSub; }; + + template + struct CompositeFunctorTranslator{ using OpT = OpMin; }; + + template + struct CompositeFunctorTranslator{ using OpT = OpMax; }; + + template + struct CompositeFunctorTranslator{ using OpT = OpMult; }; + //@} + +} // namespace ds + + +template +inline void +compositeToDense( + Dense& dense, + const TreeT& source, const TreeT& alpha, + const typename TreeT::ValueType beta, + const typename TreeT::ValueType strength, + bool threaded) +{ + using ValueT = typename TreeT::ValueType; + using Translator = ds::CompositeFunctorTranslator; + using Method = typename Translator::OpT; + + if (openvdb::math::isZero(strength)) return; + + SparseToDenseCompositor tool(dense, source, alpha, beta, strength); + + if (openvdb::math::isZero(alpha.background()) && + openvdb::math::isZero(source.background())) + { + // Use the sparsity of (alpha U source) as the iteration space. + tool.sparseComposite(threaded); + } else { + // Use the bounding box of dense as the iteration space. + tool.denseComposite(threaded); + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif //OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Diagnostics.h b/openvdb/tools/Diagnostics.h new file mode 100644 index 00000000..694d2155 --- /dev/null +++ b/openvdb/tools/Diagnostics.h @@ -0,0 +1,1331 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +/// +/// @file Diagnostics.h +/// +/// @author Ken Museth +/// +/// @brief Various diagnostic tools to identify potential issues with +/// for example narrow-band level sets or fog volumes +/// +#ifndef OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include // for std::isnan(), std::isfinite() +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Perform checks on a grid to see if it is a valid symmetric, +/// narrow-band level set. +/// +/// @param grid Grid to be checked +/// @param number Number of the checks to be performed (see below) +/// @return string with a message indicating the nature of the +/// issue. If no issue is detected the return string is empty. +/// +/// @details @a number refers to the following ordered list of +/// checks - always starting from the top. +/// Fast checks +/// 1: value type is floating point +/// 2: has level set class type +/// 3: has uniform scale +/// 4: background value is positive and n*dx +/// +/// Slower checks +/// 5: no active tiles +/// 6: all the values are finite, i.e not NaN or infinite +/// 7: active values in range between +-background +/// 8: abs of inactive values = background, i.e. assuming a symmetric +/// narrow band! +/// +/// Relatively slow check (however multithreaded) +/// 9: norm gradient is close to one, i.e. satisfied the Eikonal equation. +template +std::string +checkLevelSet(const GridType& grid, size_t number=9); + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Perform checks on a grid to see if it is a valid fog volume. +/// +/// @param grid Grid to be checked +/// @param number Number of the checks to be performed (see below) +/// @return string with a message indicating the nature of the +/// issue. If no issue is detected the return string is empty. +/// +/// @details @a number refers to the following ordered list of +/// checks - always starting from the top. +/// Fast checks +/// 1: value type is floating point +/// 2: has FOG volume class type +/// 3: background value is zero +/// +/// Slower checks +/// 4: all the values are finite, i.e not NaN or infinite +/// 5: inactive values are zero +/// 6: active values are in the range [0,1] +template +std::string +checkFogVolume(const GridType& grid, size_t number=6); + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Threaded method to find unique inactive values. +/// +/// @param grid A VDB volume. +/// @param values List of unique inactive values, returned by this method. +/// @param numValues Number of values to look for. +/// @return @c false if the @a grid has more than @a numValues inactive values. +template +bool +uniqueInactiveValues(const GridType& grid, + std::vector& values, size_t numValues); + + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Checks NaN values +template +struct CheckNan +{ + using ElementType = typename VecTraits::ElementType; + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits< + typename TreeIterT::NodeT, typename TreeIterT::ValueIterT>::template + NodeConverter::Type; + + /// @brief Default constructor + CheckNan() {} + + /// Return true if the scalar value is NaN + inline bool operator()(const ElementType& v) const { return std::isnan(v); } + + /// @brief This allows for vector values to be checked component-wise + template + inline typename std::enable_if::IsVec, bool>::type + operator()(const T& v) const + { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true;//should unroll + return false; + } + + /// @brief Return true if the tile at the iterator location is NaN + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the voxel at the iterator location is NaN + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const { return "NaN"; } + +};// CheckNan + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Checks for infinite values, e.g. 1/0 or -1/0 +template +struct CheckInf +{ + using ElementType = typename VecTraits::ElementType; + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits ::template NodeConverter< + typename GridT::TreeType::LeafNodeType>::Type; + + /// @brief Default constructor + CheckInf() {} + + /// Return true if the value is infinite + inline bool operator()(const ElementType& v) const { return std::isinf(v); } + + /// Return true if any of the vector components are infinite. + template + inline typename std::enable_if::IsVec, bool>::type + operator()(const T& v) const + { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the tile at the iterator location is infinite + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is infinite + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const { return "infinite"; } +};// CheckInf + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Checks for both NaN and inf values, i.e. any value that is not finite. +template +struct CheckFinite +{ + using ElementType = typename VecTraits::ElementType; + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits ::template NodeConverter< + typename GridT::TreeType::LeafNodeType>::Type; + + /// @brief Default constructor + CheckFinite() {} + + /// Return true if the value is NOT finite, i.e. it's NaN or infinite + inline bool operator()(const ElementType& v) const { return !std::isfinite(v); } + + /// Return true if any of the vector components are NaN or infinite. + template + inline typename std::enable_if::IsVec, bool>::type + operator()(const T& v) const { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the tile at the iterator location is NaN or infinite. + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is NaN or infinite. + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const { return "not finite"; } +};// CheckFinite + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Check that the magnitude of a value, a, is close to a fixed +/// magnitude, b, given a fixed tolerance c. That is | |a| - |b| | <= c +template +struct CheckMagnitude +{ + using ElementType = typename VecTraits::ElementType; + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits ::template NodeConverter< + typename GridT::TreeType::LeafNodeType>::Type; + + /// @brief Default constructor + CheckMagnitude(const ElementType& a, + const ElementType& t = math::Tolerance::value()) + : absVal(math::Abs(a)), tolVal(math::Abs(t)) + { + } + + /// Return true if the magnitude of the value is not approximately + /// equal to totVal. + inline bool operator()(const ElementType& v) const + { + return math::Abs(math::Abs(v) - absVal) > tolVal; + } + + /// Return true if any of the vector components are infinite. + template + inline typename std::enable_if::IsVec, bool>::type + operator()(const T& v) const + { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the tile at the iterator location is infinite + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is infinite + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "not equal to +/-"< +struct CheckRange +{ + using ElementType = typename VecTraits::ElementType; + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits ::template NodeConverter< + typename GridT::TreeType::LeafNodeType>::Type; + + // @brief Constructor taking a range to be tested against. + CheckRange(const ElementType& _min, const ElementType& _max) : minVal(_min), maxVal(_max) + { + if (minVal > maxVal) { + OPENVDB_THROW(ValueError, "CheckRange: Invalid range (min > max)"); + } + } + + /// Return true if the value is smaller than min or larger than max. + inline bool operator()(const ElementType& v) const + { + return (MinInclusive ? vmaxVal : v>=maxVal); + } + + /// Return true if any of the vector components are out of range. + template + inline typename std::enable_if::IsVec, bool>::type + operator()(const T& v) const { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the voxel at the iterator location is out of range. + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is out of range. + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "outside the value range " << (MinInclusive ? "[" : "]") + << minVal << "," << maxVal << (MaxInclusive ? "]" : "["); + return ss.str(); + } + + const ElementType minVal, maxVal; +};// CheckRange + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Checks a value against a minimum +template +struct CheckMin +{ + using ElementType = typename VecTraits::ElementType; + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits ::template NodeConverter< + typename GridT::TreeType::LeafNodeType>::Type; + + // @brief Constructor taking a minimum to be tested against. + CheckMin(const ElementType& _min) : minVal(_min) {} + + /// Return true if the value is smaller than min. + inline bool operator()(const ElementType& v) const { return v + inline typename std::enable_if::IsVec, bool>::type + operator()(const T& v) const { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the voxel at the iterator location is smaller than min. + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is smaller than min. + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "smaller than "< +struct CheckMax +{ + using ElementType = typename VecTraits::ElementType; + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits ::template NodeConverter< + typename GridT::TreeType::LeafNodeType>::Type; + + /// @brief Constructor taking a maximum to be tested against. + CheckMax(const ElementType& _max) : maxVal(_max) {} + + /// Return true if the value is larger than max. + inline bool operator()(const ElementType& v) const { return v>maxVal; } + + /// Return true if any of the vector components are larger than max. + template + inline typename std::enable_if::IsVec, bool>::type + operator()(const T& v) const { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the tile at the iterator location is larger than max. + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the voxel at the iterator location is larger than max. + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "larger than "<//math::WENO5_BIAS> +struct CheckNormGrad +{ + using ValueType = typename GridT::ValueType; + static_assert(std::is_floating_point::value, + "openvdb::tools::CheckNormGrad requires a scalar, floating-point grid"); + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits ::template NodeConverter< + typename GridT::TreeType::LeafNodeType>::Type; + using AccT = typename GridT::ConstAccessor; + + /// @brief Constructor taking a grid and a range to be tested against. + CheckNormGrad(const GridT& grid, const ValueType& _min, const ValueType& _max) + : acc(grid.getConstAccessor()) + , invdx2(ValueType(1.0/math::Pow2(grid.voxelSize()[0]))) + , minVal2(_min*_min) + , maxVal2(_max*_max) + { + if ( !grid.hasUniformVoxels() ) { + OPENVDB_THROW(ValueError, "CheckNormGrad: The transform must have uniform scale"); + } + if (_min > _max) { + OPENVDB_THROW(ValueError, "CheckNormGrad: Invalid range (min > max)"); + } + } + + CheckNormGrad(const CheckNormGrad& other) + : acc(other.acc.tree()) + , invdx2(other.invdx2) + , minVal2(other.minVal2) + , maxVal2(other.maxVal2) + { + } + + /// Return true if the value is smaller than min or larger than max. + inline bool operator()(const ValueType& v) const { return vmaxVal2; } + + /// @brief Return true if zero is outside the range. + /// @note We assume that the norm of the gradient of a tile is always zero. + inline bool operator()(const TreeIterT&) const { return (*this)(ValueType(0)); } + + /// @brief Return true if the norm of the gradient at a voxel + /// location of the iterator is out of range. + inline bool operator()(const VoxelIterT &iter) const + { + const Coord ijk = iter.getCoord(); + return (*this)(invdx2 * math::ISGradientNormSqrd::result(acc, ijk)); + } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "outside the range of NormGrad ["< >//math::GradStencil +struct CheckEikonal +{ + using ValueType = typename GridT::ValueType; + static_assert(std::is_floating_point::value, + "openvdb::tools::CheckEikonal requires a scalar, floating-point grid"); + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits ::template NodeConverter< + typename GridT::TreeType::LeafNodeType>::Type; + + /// @brief Constructor taking a grid and a range to be tested against. + CheckEikonal(const GridT& grid, const ValueType& _min, const ValueType& _max) + : stencil(grid), minVal(_min), maxVal(_max) + { + if ( !grid.hasUniformVoxels() ) { + OPENVDB_THROW(ValueError, "CheckEikonal: The transform must have uniform scale"); + } + if (minVal > maxVal) { + OPENVDB_THROW(ValueError, "CheckEikonal: Invalid range (min > max)"); + } + } + + CheckEikonal(const CheckEikonal& other) + : stencil(other.stencil.grid()), minVal(other.minVal), maxVal(other.maxVal) + { + } + + /// Return true if the value is smaller than min or larger than max. + inline bool operator()(const ValueType& v) const { return vmaxVal; } + + /// @brief Return true if zero is outside the range. + /// @note We assume that the norm of the gradient of a tile is always zero. + inline bool operator()(const TreeIterT&) const { return (*this)(ValueType(0)); } + + /// @brief Return true if the norm of the gradient at a + /// zero-crossing voxel location of the iterator is out of range. + inline bool operator()(const VoxelIterT &iter) const + { + stencil.moveTo(iter); + if (!stencil.zeroCrossing()) return false; + return (*this)(stencil.normSqGrad()); + } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "outside the range of NormGrad ["< +struct CheckDivergence +{ + using ValueType = typename GridT::ValueType; + using ElementType = typename VecTraits::ElementType; + static_assert(std::is_floating_point::value, + "openvdb::tools::CheckDivergence requires a floating-point vector grid"); + using TileIterT = TreeIterT; + using VoxelIterT = typename tree::IterTraits::template NodeConverter< + typename GridT::TreeType::LeafNodeType>::Type; + using AccT = typename GridT::ConstAccessor; + + /// @brief Constructor taking a grid and a range to be tested against. + CheckDivergence(const GridT& grid, + const ValueType& _min, + const ValueType& _max) + : acc(grid.getConstAccessor()) + , invdx(ValueType(1.0/grid.voxelSize()[0])) + , minVal(_min) + , maxVal(_max) + { + if ( !grid.hasUniformVoxels() ) { + OPENVDB_THROW(ValueError, "CheckDivergence: The transform must have uniform scale"); + } + if (minVal > maxVal) { + OPENVDB_THROW(ValueError, "CheckDivergence: Invalid range (min > max)"); + } + } + /// Return true if the value is smaller than min or larger than max. + inline bool operator()(const ElementType& v) const { return vmaxVal; } + + /// @brief Return true if zero is outside the range. + /// @note We assume that the divergence of a tile is always zero. + inline bool operator()(const TreeIterT&) const { return (*this)(ElementType(0)); } + + /// @brief Return true if the divergence at a voxel location of + /// the iterator is out of range. + inline bool operator()(const VoxelIterT &iter) const + { + const Coord ijk = iter.getCoord(); + return (*this)(invdx * math::ISDivergence::result(acc, ijk)); + } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "outside the range of divergence ["< +class Diagnose +{ +public: + using MaskType = typename GridT::template ValueConverter::Type; + + Diagnose(const GridT& grid) : mGrid(&grid), mMask(new MaskType()), mCount(0) + { + mMask->setTransform(grid.transformPtr()->copy()); + } + + template + std::string check(const CheckT& check, + bool updateMask = false, + bool checkVoxels = true, + bool checkTiles = true, + bool checkBackground = true) + { + typename MaskType::TreeType* mask = updateMask ? &(mMask->tree()) : nullptr; + CheckValues cc(mask, mGrid, check); + std::ostringstream ss; + if (checkBackground) ss << cc.checkBackground(); + if (checkTiles) ss << cc.checkTiles(); + if (checkVoxels) ss << cc.checkVoxels(); + mCount += cc.mCount; + return ss.str(); + } + + //@{ + /// @brief Return a boolean mask of all the values + /// (i.e. tiles and/or voxels) that have failed one or + /// more checks. + typename MaskType::ConstPtr mask() const { return mMask; } + typename MaskType::Ptr mask() { return mMask; } + //@} + + /// @brief Return the number of values (i.e. background, tiles or + /// voxels) that have failed one or more checks. + Index64 valueCount() const { return mMask->activeVoxelCount(); } + + /// @brief Return total number of failed checks + /// @note If only one check was performed and the mask was updated + /// failureCount equals valueCount. + Index64 failureCount() const { return mCount; } + + /// @brief Return a const reference to the grid + const GridT& grid() const { return *mGrid; } + + /// @brief Clear the mask and error counter + void clear() { mMask = new MaskType(); mCount = 0; } + +private: + // disallow copy construction and copy by assignment! + Diagnose(const Diagnose&);// not implemented + Diagnose& operator=(const Diagnose&);// not implemented + + const GridT* mGrid; + typename MaskType::Ptr mMask; + Index64 mCount; + + /// @brief Private class that performs the multithreaded checks + template + struct CheckValues + { + using MaskT = typename MaskType::TreeType; + using LeafT = typename GridT::TreeType::LeafNodeType; + using LeafManagerT = typename tree::LeafManager; + const bool mOwnsMask; + MaskT* mMask; + const GridT* mGrid; + const CheckT mCheck; + Index64 mCount; + + CheckValues(MaskT* mask, const GridT* grid, const CheckT& check) + : mOwnsMask(false) + , mMask(mask) + , mGrid(grid) + , mCheck(check) + , mCount(0) + { + } + CheckValues(CheckValues& other, tbb::split) + : mOwnsMask(true) + , mMask(other.mMask ? new MaskT() : nullptr) + , mGrid(other.mGrid) + , mCheck(other.mCheck) + , mCount(0) + { + } + ~CheckValues() { if (mOwnsMask) delete mMask; } + + std::string checkBackground() + { + std::ostringstream ss; + if (mCheck(mGrid->background())) { + ++mCount; + ss << "Background is " + mCheck.str() << std::endl; + } + return ss.str(); + } + + std::string checkTiles() + { + std::ostringstream ss; + const Index64 n = mCount; + typename CheckT::TileIterT i(mGrid->tree()); + for (i.setMaxDepth(GridT::TreeType::RootNodeType::LEVEL - 1); i; ++i) { + if (mCheck(i)) { + ++mCount; + if (mMask) mMask->fill(i.getBoundingBox(), true, true); + } + } + if (const Index64 m = mCount - n) { + ss << m << " tile" << (m==1 ? " is " : "s are ") + mCheck.str() << std::endl; + } + return ss.str(); + } + + std::string checkVoxels() + { + std::ostringstream ss; + LeafManagerT leafs(mGrid->tree()); + const Index64 n = mCount; + tbb::parallel_reduce(leafs.leafRange(), *this); + if (const Index64 m = mCount - n) { + ss << m << " voxel" << (m==1 ? " is " : "s are ") + mCheck.str() << std::endl; + } + return ss.str(); + } + + void operator()(const typename LeafManagerT::LeafRange& r) + { + using VoxelIterT = typename CheckT::VoxelIterT; + if (mMask) { + for (typename LeafManagerT::LeafRange::Iterator i=r.begin(); i; ++i) { + typename MaskT::LeafNodeType* maskLeaf = nullptr; + for (VoxelIterT j = tree::IterTraits::begin(*i); j; ++j) { + if (mCheck(j)) { + ++mCount; + if (maskLeaf == nullptr) maskLeaf = mMask->touchLeaf(j.getCoord()); + maskLeaf->setValueOn(j.pos(), true); + } + } + } + } else { + for (typename LeafManagerT::LeafRange::Iterator i=r.begin(); i; ++i) { + for (VoxelIterT j = tree::IterTraits::begin(*i); j; ++j) { + if (mCheck(j)) ++mCount; + } + } + } + } + void join(const CheckValues& other) + { + if (mMask) mMask->merge(*(other.mMask), openvdb::MERGE_ACTIVE_STATES_AND_NODES); + mCount += other.mCount; + } + };//End of private class CheckValues + +};// End of public class Diagnose + + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Class that performs various types of checks on narrow-band level sets. +/// +/// @note The most common usage is to simply call CheckLevelSet::check() +template +class CheckLevelSet +{ +public: + using ValueType = typename GridType::ValueType; + using MaskType = typename GridType::template ValueConverter::Type; + + CheckLevelSet(const GridType& grid) : mDiagnose(grid) {} + + //@{ + /// @brief Return a boolean mask of all the values + /// (i.e. tiles and/or voxels) that have failed one or + /// more checks. + typename MaskType::ConstPtr mask() const { return mDiagnose.mask(); } + typename MaskType::Ptr mask() { return mDiagnose.mask(); } + //@} + + /// @brief Return the number of values (i.e. background, tiles or + /// voxels) that have failed one or more checks. + Index64 valueCount() const { return mDiagnose.valueCount(); } + + /// @brief Return total number of failed checks + /// @note If only one check was performed and the mask was updated + /// failureCount equals valueCount. + Index64 failureCount() const { return mDiagnose.failureCount(); } + + /// @brief Return a const reference to the grid + const GridType& grid() const { return mDiagnose.grid(); } + + /// @brief Clear the mask and error counter + void clear() { mDiagnose.clear(); } + + /// @brief Return a nonempty message if the grid's value type is a floating point. + /// + /// @note No run-time overhead + static std::string checkValueType() + { + static const bool test = std::is_floating_point::value; + return test ? "" : "Value type is not floating point\n"; + } + + /// @brief Return message if the grid's class is a level set. + /// + /// @note Small run-time overhead + std::string checkClassType() const + { + const bool test = mDiagnose.grid().getGridClass() == GRID_LEVEL_SET; + return test ? "" : "Class type is not \"GRID_LEVEL_SET\"\n"; + } + + /// @brief Return a nonempty message if the grid's transform does not have uniform scaling. + /// + /// @note Small run-time overhead + std::string checkTransform() const + { + return mDiagnose.grid().hasUniformVoxels() ? "" : "Does not have uniform voxels\n"; + } + + /// @brief Return a nonempty message if the background value is larger than or + /// equal to the halfWidth*voxelSize. + /// + /// @note Small run-time overhead + std::string checkBackground(Real halfWidth = LEVEL_SET_HALF_WIDTH) const + { + const Real w = mDiagnose.grid().background() / mDiagnose.grid().voxelSize()[0]; + if (w < halfWidth) { + std::ostringstream ss; + ss << "The background value ("<< mDiagnose.grid().background()<<") is less than " + << halfWidth << " voxel units\n"; + return ss.str(); + } + return ""; + } + + /// @brief Return a nonempty message if the grid has no active tile values. + /// + /// @note Medium run-time overhead + std::string checkTiles() const + { + const bool test = mDiagnose.grid().tree().hasActiveTiles(); + return test ? "Has active tile values\n" : ""; + } + + /// @brief Return a nonempty message if any of the values are not finite. i.e. NaN or inf. + /// + /// @note Medium run-time overhead + std::string checkFinite(bool updateMask = false) + { + CheckFinite c; + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/true); + } + + /// @brief Return a nonempty message if the active voxel values are out-of-range. + /// + /// @note Medium run-time overhead + std::string checkRange(bool updateMask = false) + { + const ValueType& background = mDiagnose.grid().background(); + CheckRange c(-background, background); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/false, /*background*/false); + } + + /// @brief Return a nonempty message if the the inactive values do not have a + /// magnitude equal to the background value. + /// + /// @note Medium run-time overhead + std::string checkInactiveValues(bool updateMask = false) + { + const ValueType& background = mDiagnose.grid().background(); + CheckMagnitude c(background); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/false); + } + + /// @brief Return a nonempty message if the norm of the gradient of the + /// active voxels is out of the range minV to maxV. + /// + /// @note Significant run-time overhead + std::string checkEikonal(bool updateMask = false, ValueType minV = 0.5, ValueType maxV = 1.5) + { + CheckEikonal c(mDiagnose.grid(), minV, maxV); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/false, /*background*/false); + } + + /// @brief Return a nonempty message if an error or issue is detected. Only + /// runs tests with a number lower than or equal to n, where: + /// + /// Fast checks + /// 1: value type is floating point + /// 2: has level set class type + /// 3: has uniform scale + /// 4: background value is positive and n*dx + /// + /// Slower checks + /// 5: no active tiles + /// 6: all the values are finite, i.e not NaN or infinite + /// 7: active values in range between +-background + /// 8: abs of inactive values = background, i.e. assuming a symmetric narrow band! + /// + /// Relatively slow check (however multi-threaded) + /// 9: norm of gradient at zero-crossings is one, i.e. satisfied the Eikonal equation. + std::string check(size_t n=9, bool updateMask = false) + { + std::string str = this->checkValueType(); + if (str.empty() && n>1) str = this->checkClassType(); + if (str.empty() && n>2) str = this->checkTransform(); + if (str.empty() && n>3) str = this->checkBackground(); + if (str.empty() && n>4) str = this->checkTiles(); + if (str.empty() && n>5) str = this->checkFinite(updateMask); + if (str.empty() && n>6) str = this->checkRange(updateMask); + if (str.empty() && n>7) str = this->checkInactiveValues(updateMask); + if (str.empty() && n>8) str = this->checkEikonal(updateMask); + return str; + } + +private: + // disallow copy construction and copy by assignment! + CheckLevelSet(const CheckLevelSet&);// not implemented + CheckLevelSet& operator=(const CheckLevelSet&);// not implemented + + // Member data + Diagnose mDiagnose; +};// CheckLevelSet + +template +std::string +checkLevelSet(const GridType& grid, size_t n) +{ + CheckLevelSet c(grid); + return c.check(n, false); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Class that performs various types of checks on fog volumes. +/// +/// @note The most common usage is to simply call CheckFogVolume::check() +template +class CheckFogVolume +{ +public: + using ValueType = typename GridType::ValueType; + using MaskType = typename GridType::template ValueConverter::Type; + + CheckFogVolume(const GridType& grid) : mDiagnose(grid) {} + + //@{ + /// @brief Return a boolean mask of all the values + /// (i.e. tiles and/or voxels) that have failed one or + /// more checks. + typename MaskType::ConstPtr mask() const { return mDiagnose.mask(); } + typename MaskType::Ptr mask() { return mDiagnose.mask(); } + //@} + + /// @brief Return the number of values (i.e. background, tiles or + /// voxels) that have failed one or more checks. + Index64 valueCount() const { return mDiagnose.valueCount(); } + + /// @brief Return total number of failed checks + /// @note If only one check was performed and the mask was updated + /// failureCount equals valueCount. + Index64 failureCount() const { return mDiagnose.failureCount(); } + + /// @brief Return a const reference to the grid + const GridType& grid() const { return mDiagnose.grid(); } + + /// @brief Clear the mask and error counter + void clear() { mDiagnose.clear(); } + + /// @brief Return a nonempty message if the grid's value type is a floating point. + /// + /// @note No run-time overhead + static std::string checkValueType() + { + static const bool test = std::is_floating_point::value; + return test ? "" : "Value type is not floating point"; + } + + /// @brief Return a nonempty message if the grid's class is a level set. + /// + /// @note Small run-time overhead + std::string checkClassType() const + { + const bool test = mDiagnose.grid().getGridClass() == GRID_FOG_VOLUME; + return test ? "" : "Class type is not \"GRID_LEVEL_SET\""; + } + + /// @brief Return a nonempty message if the background value is not zero. + /// + /// @note Small run-time overhead + std::string checkBackground() const + { + if (!math::isApproxZero(mDiagnose.grid().background())) { + std::ostringstream ss; + ss << "The background value ("<< mDiagnose.grid().background()<<") is not zero"; + return ss.str(); + } + return ""; + } + + /// @brief Return a nonempty message if any of the values are not finite. i.e. NaN or inf. + /// + /// @note Medium run-time overhead + std::string checkFinite(bool updateMask = false) + { + CheckFinite c; + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/true); + } + + /// @brief Return a nonempty message if any of the inactive values are not zero. + /// + /// @note Medium run-time overhead + std::string checkInactiveValues(bool updateMask = false) + { + CheckMagnitude c(0); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/true); + } + + /// @brief Return a nonempty message if the active voxel values + /// are out-of-range, i.e. not in the range [0,1]. + /// + /// @note Medium run-time overhead + std::string checkRange(bool updateMask = false) + { + CheckRange c(0, 1); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/false); + } + + /// @brief Return a nonempty message if an error or issue is detected. Only + /// runs tests with a number lower than or equal to n, where: + /// + /// Fast checks + /// 1: value type is floating point + /// 2: has FOG volume class type + /// 3: background value is zero + /// + /// Slower checks + /// 4: all the values are finite, i.e not NaN or infinite + /// 5: inactive values are zero + /// 6: active values are in the range [0,1] + std::string check(size_t n=6, bool updateMask = false) + { + std::string str = this->checkValueType(); + if (str.empty() && n>1) str = this->checkClassType(); + if (str.empty() && n>2) str = this->checkBackground(); + if (str.empty() && n>3) str = this->checkFinite(updateMask); + if (str.empty() && n>4) str = this->checkInactiveValues(updateMask); + if (str.empty() && n>5) str = this->checkRange(updateMask); + return str; + } + +private: + // disallow copy construction and copy by assignment! + CheckFogVolume(const CheckFogVolume&);// not implemented + CheckFogVolume& operator=(const CheckFogVolume&);// not implemented + + // Member data + Diagnose mDiagnose; +};// CheckFogVolume + +template +std::string +checkFogVolume(const GridType& grid, size_t n) +{ + CheckFogVolume c(grid); + return c.check(n, false); +} + + +//////////////////////////////////////////////////////////////////////////////// + +// Internal utility objects and implementation details + + +namespace diagnostics_internal { + + +template +class InactiveVoxelValues +{ +public: + using LeafArray = tree::LeafManager; + using ValueType = typename TreeType::ValueType; + using SetType = std::set; + + InactiveVoxelValues(LeafArray&, size_t numValues); + + void runParallel(); + void runSerial(); + + void getInactiveValues(SetType&) const; + + inline InactiveVoxelValues(const InactiveVoxelValues&, tbb::split); + inline void operator()(const tbb::blocked_range&); + inline void join(const InactiveVoxelValues&); + +private: + LeafArray& mLeafArray; + SetType mInactiveValues; + size_t mNumValues; +};// InactiveVoxelValues + +template +InactiveVoxelValues::InactiveVoxelValues(LeafArray& leafs, size_t numValues) + : mLeafArray(leafs) + , mInactiveValues() + , mNumValues(numValues) +{ +} + +template +inline +InactiveVoxelValues::InactiveVoxelValues( + const InactiveVoxelValues& rhs, tbb::split) + : mLeafArray(rhs.mLeafArray) + , mInactiveValues() + , mNumValues(rhs.mNumValues) +{ +} + +template +void +InactiveVoxelValues::runParallel() +{ + tbb::parallel_reduce(mLeafArray.getRange(), *this); +} + + +template +void +InactiveVoxelValues::runSerial() +{ + (*this)(mLeafArray.getRange()); +} + + +template +inline void +InactiveVoxelValues::operator()(const tbb::blocked_range& range) +{ + typename TreeType::LeafNodeType::ValueOffCIter iter; + + for (size_t n = range.begin(); n < range.end() && !tbb::task::self().is_cancelled(); ++n) { + for (iter = mLeafArray.leaf(n).cbeginValueOff(); iter; ++iter) { + mInactiveValues.insert(iter.getValue()); + } + + if (mInactiveValues.size() > mNumValues) { + tbb::task::self().cancel_group_execution(); + } + } +} + +template +inline void +InactiveVoxelValues::join(const InactiveVoxelValues& rhs) +{ + mInactiveValues.insert(rhs.mInactiveValues.begin(), rhs.mInactiveValues.end()); +} + +template +inline void +InactiveVoxelValues::getInactiveValues(SetType& values) const +{ + values.insert(mInactiveValues.begin(), mInactiveValues.end()); +} + + +//////////////////////////////////////// + + +template +class InactiveTileValues +{ +public: + using IterRange = tree::IteratorRange; + using ValueType = typename TreeType::ValueType; + using SetType = std::set; + + InactiveTileValues(size_t numValues); + + void runParallel(IterRange&); + void runSerial(IterRange&); + + void getInactiveValues(SetType&) const; + + inline InactiveTileValues(const InactiveTileValues&, tbb::split); + inline void operator()(IterRange&); + inline void join(const InactiveTileValues&); + +private: + SetType mInactiveValues; + size_t mNumValues; +}; + + +template +InactiveTileValues::InactiveTileValues(size_t numValues) + : mInactiveValues() + , mNumValues(numValues) +{ +} + +template +inline +InactiveTileValues::InactiveTileValues( + const InactiveTileValues& rhs, tbb::split) + : mInactiveValues() + , mNumValues(rhs.mNumValues) +{ +} + +template +void +InactiveTileValues::runParallel(IterRange& range) +{ + tbb::parallel_reduce(range, *this); +} + + +template +void +InactiveTileValues::runSerial(IterRange& range) +{ + (*this)(range); +} + + +template +inline void +InactiveTileValues::operator()(IterRange& range) +{ + for (; range && !tbb::task::self().is_cancelled(); ++range) { + typename TreeType::ValueOffCIter iter = range.iterator(); + for (; iter; ++iter) { + mInactiveValues.insert(iter.getValue()); + } + + if (mInactiveValues.size() > mNumValues) { + tbb::task::self().cancel_group_execution(); + } + } +} + +template +inline void +InactiveTileValues::join(const InactiveTileValues& rhs) +{ + mInactiveValues.insert(rhs.mInactiveValues.begin(), rhs.mInactiveValues.end()); +} + +template +inline void +InactiveTileValues::getInactiveValues(SetType& values) const +{ + values.insert(mInactiveValues.begin(), mInactiveValues.end()); +} + +} // namespace diagnostics_internal + + +//////////////////////////////////////// + + +template +bool +uniqueInactiveValues(const GridType& grid, + std::vector& values, size_t numValues) +{ + using TreeType = typename GridType::TreeType; + using ValueType = typename GridType::ValueType; + using SetType = std::set; + + SetType uniqueValues; + + { // Check inactive voxels + TreeType& tree = const_cast(grid.tree()); + tree::LeafManager leafs(tree); + diagnostics_internal::InactiveVoxelValues voxelOp(leafs, numValues); + voxelOp.runParallel(); + voxelOp.getInactiveValues(uniqueValues); + } + + // Check inactive tiles + if (uniqueValues.size() <= numValues) { + typename TreeType::ValueOffCIter iter(grid.tree()); + iter.setMaxDepth(TreeType::ValueAllIter::LEAF_DEPTH - 1); + diagnostics_internal::InactiveTileValues tileOp(numValues); + + tree::IteratorRange range(iter); + tileOp.runParallel(range); + + tileOp.getInactiveValues(uniqueValues); + } + + values.clear(); + values.reserve(uniqueValues.size()); + + typename SetType::iterator it = uniqueValues.begin(); + for ( ; it != uniqueValues.end(); ++it) { + values.push_back(*it); + } + + return values.size() <= numValues; +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Filter.h b/openvdb/tools/Filter.h new file mode 100644 index 00000000..0823685b --- /dev/null +++ b/openvdb/tools/Filter.h @@ -0,0 +1,445 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @author Ken Museth +/// +/// @file tools/Filter.h +/// +/// @brief Filtering of VDB volumes. Note that only the values in the +/// grid are changed, not its topology! All operations can optionally +/// be masked with another grid that acts as an alpha-mask. + +#ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Interpolation.h" +#include // for std::max() +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Volume filtering (e.g., diffusion) with optional alpha masking +/// +/// @note Only the values in the grid are changed, not its topology! +template::Type, + typename InterruptT = util::NullInterrupter> +class Filter +{ +public: + using GridType = GridT; + using MaskType = MaskT; + using TreeType = typename GridType::TreeType; + using LeafType = typename TreeType::LeafNodeType; + using ValueType = typename GridType::ValueType; + using AlphaType = typename MaskType::ValueType; + using LeafManagerType = typename tree::LeafManager; + using RangeType = typename LeafManagerType::LeafRange; + using BufferType = typename LeafManagerType::BufferType; + static_assert(std::is_floating_point::value, + "openvdb::tools::Filter requires a mask grid with floating-point values"); + + /// Constructor + /// @param grid Grid to be filtered. + /// @param interrupt Optional interrupter. + Filter(GridT& grid, InterruptT* interrupt = nullptr) + : mGrid(&grid) + , mTask(nullptr) + , mInterrupter(interrupt) + , mMask(nullptr) + , mGrainSize(1) + , mMinMask(0) + , mMaxMask(1) + , mInvertMask(false) + { + } + + /// @brief Shallow copy constructor called by tbb::parallel_for() + /// threads during filtering. + /// @param other The other Filter from which to copy. + Filter(const Filter& other) + : mGrid(other.mGrid) + , mTask(other.mTask) + , mInterrupter(other.mInterrupter) + , mMask(other.mMask) + , mGrainSize(other.mGrainSize) + , mMinMask(other.mMinMask) + , mMaxMask(other.mMaxMask) + , mInvertMask(other.mInvertMask) + { + } + + /// @return the grain-size used for multi-threading + int getGrainSize() const { return mGrainSize; } + /// @brief Set the grain-size used for multi-threading. + /// @note A grain size of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mGrainSize = grainsize; } + + /// @brief Return the minimum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType minMask() const { return mMinMask; } + /// @brief Return the maximum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType maxMask() const { return mMaxMask; } + /// @brief Define the range for the (optional) scalar mask. + /// @param min Minimum value of the range. + /// @param max Maximum value of the range. + /// @details Mask values outside the range are clamped to zero or one, and + /// values inside the range map smoothly to 0->1 (unless the mask is inverted). + /// @throw ValueError if @a min is not smaller than @a max. + void setMaskRange(AlphaType min, AlphaType max) + { + if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)"); + mMinMask = min; + mMaxMask = max; + } + + /// @brief Return true if the mask is inverted, i.e. min->max in the + /// original mask maps to 1->0 in the inverted alpha mask. + bool isMaskInverted() const { return mInvertMask; } + /// @brief Invert the optional mask, i.e. min->max in the original + /// mask maps to 1->0 in the inverted alpha mask. + void invertMask(bool invert=true) { mInvertMask = invert; } + + /// @brief One iteration of a fast separable mean-value (i.e. box) filter. + /// @param width The width of the mean-value filter is 2*width+1 voxels. + /// @param iterations Number of times the mean-value filter is applied. + /// @param mask Optional alpha mask. + void mean(int width = 1, int iterations = 1, const MaskType* mask = nullptr); + + /// @brief One iteration of a fast separable Gaussian filter. + /// + /// @note This is approximated as 4 iterations of a separable mean filter + /// which typically leads an approximation that's better than 95%! + /// @param width The width of the mean-value filter is 2*width+1 voxels. + /// @param iterations Number of times the mean-value filter is applied. + /// @param mask Optional alpha mask. + void gaussian(int width = 1, int iterations = 1, const MaskType* mask = nullptr); + + /// @brief One iteration of a median-value filter + /// + /// @note This filter is not separable and is hence relatively slow! + /// @param width The width of the mean-value filter is 2*width+1 voxels. + /// @param iterations Number of times the mean-value filter is applied. + /// @param mask Optional alpha mask. + void median(int width = 1, int iterations = 1, const MaskType* mask = nullptr); + + /// Offsets (i.e. adds) a constant value to all active voxels. + /// @param offset Offset in the same units as the grid. + /// @param mask Optional alpha mask. + void offset(ValueType offset, const MaskType* mask = nullptr); + + /// @brief Used internally by tbb::parallel_for() + /// @param range Range of LeafNodes over which to multi-thread. + /// + /// @warning Never call this method directly! + void operator()(const RangeType& range) const + { + if (mTask) mTask(const_cast(this), range); + else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc."); + } + +private: + using LeafT = typename TreeType::LeafNodeType; + using VoxelIterT = typename LeafT::ValueOnIter; + using VoxelCIterT = typename LeafT::ValueOnCIter; + using BufferT = typename tree::LeafManager::BufferType; + using LeafIterT = typename RangeType::Iterator; + using AlphaMaskT = tools::AlphaMask; + + void cook(LeafManagerType& leafs); + + template + struct Avg { + Avg(const GridT* grid, Int32 w): acc(grid->tree()), width(w), frac(1.f/float(2*w+1)) {} + inline ValueType operator()(Coord xyz); + typename GridT::ConstAccessor acc; + const Int32 width; + const float frac; + }; + + // Private filter methods called by tbb::parallel_for threads + template + void doBox( const RangeType& r, Int32 w); + void doBoxX(const RangeType& r, Int32 w) { this->doBox >(r,w); } + void doBoxZ(const RangeType& r, Int32 w) { this->doBox >(r,w); } + void doBoxY(const RangeType& r, Int32 w) { this->doBox >(r,w); } + void doMedian(const RangeType&, int); + void doOffset(const RangeType&, ValueType); + /// @return true if the process was interrupted + bool wasInterrupted(); + + GridType* mGrid; + typename std::function mTask; + InterruptT* mInterrupter; + const MaskType* mMask; + int mGrainSize; + AlphaType mMinMask, mMaxMask; + bool mInvertMask; +}; // end of Filter class + + +//////////////////////////////////////// + + +namespace filter_internal { +// Helper function for Filter::Avg::operator() +template static inline void accum(T& sum, T addend) { sum += addend; } +// Overload for bool ValueType +inline void accum(bool& sum, bool addend) { sum = sum || addend; } +} + + +template +template +inline typename GridT::ValueType +Filter::Avg::operator()(Coord xyz) +{ + ValueType sum = zeroVal(); + Int32 &i = xyz[Axis], j = i + width; + for (i -= width; i <= j; ++i) filter_internal::accum(sum, acc.getValue(xyz)); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + ValueType value = static_cast(sum * frac); + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return value; +} + + +//////////////////////////////////////// + + +template +inline void +Filter::mean(int width, int iterations, const MaskType* mask) +{ + mMask = mask; + + if (mInterrupter) mInterrupter->start("Applying mean filter"); + + const int w = std::max(1, width); + + LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0); + + for (int i=0; iwasInterrupted(); ++i) { + mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w); + this->cook(leafs); + + mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w); + this->cook(leafs); + + mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w); + this->cook(leafs); + } + + if (mInterrupter) mInterrupter->end(); +} + + +template +inline void +Filter::gaussian(int width, int iterations, const MaskType* mask) +{ + mMask = mask; + + if (mInterrupter) mInterrupter->start("Applying Gaussian filter"); + + const int w = std::max(1, width); + + LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0); + + for (int i=0; iwasInterrupted(); ++n) { + mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w); + this->cook(leafs); + + mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w); + this->cook(leafs); + + mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w); + this->cook(leafs); + } + } + + if (mInterrupter) mInterrupter->end(); +} + + +template +inline void +Filter::median(int width, int iterations, const MaskType* mask) +{ + mMask = mask; + + if (mInterrupter) mInterrupter->start("Applying median filter"); + + LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0); + + mTask = std::bind(&Filter::doMedian, + std::placeholders::_1, std::placeholders::_2, std::max(1, width)); + for (int i=0; iwasInterrupted(); ++i) this->cook(leafs); + + if (mInterrupter) mInterrupter->end(); +} + + +template +inline void +Filter::offset(ValueType value, const MaskType* mask) +{ + mMask = mask; + + if (mInterrupter) mInterrupter->start("Applying offset"); + + LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0); + + mTask = std::bind(&Filter::doOffset, std::placeholders::_1, std::placeholders::_2, value); + this->cook(leafs); + + if (mInterrupter) mInterrupter->end(); +} + + +//////////////////////////////////////// + + +/// Private method to perform the task (serial or threaded) and +/// subsequently swap the leaf buffers. +template +inline void +Filter::cook(LeafManagerType& leafs) +{ + if (mGrainSize>0) { + tbb::parallel_for(leafs.leafRange(mGrainSize), *this); + } else { + (*this)(leafs.leafRange()); + } + leafs.swapLeafBuffer(1, mGrainSize==0); +} + + +/// One dimensional convolution of a separable box filter +template +template +inline void +Filter::doBox(const RangeType& range, Int32 w) +{ + this->wasInterrupted(); + AvgT avg(mGrid, w); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + const Coord xyz = iter.getCoord(); + if (alpha(xyz, a, b)) { + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const ValueType value(b*(*iter) + a*avg(xyz)); + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + buffer.setValue(iter.pos(), value); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + buffer.setValue(iter.pos(), avg(iter.getCoord())); + } + } + } +} + + +/// Performs simple but slow median-value diffusion +template +inline void +Filter::doMedian(const RangeType& range, int width) +{ + this->wasInterrupted(); + typename math::DenseStencil stencil(*mGrid, width);//creates local cache! + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + ValueType value(b*(*iter) + a*stencil.median()); + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + buffer.setValue(iter.pos(), value); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer.setValue(iter.pos(), stencil.median()); + } + } + } +} + + +/// Offsets the values by a constant +template +inline void +Filter::doOffset(const RangeType& range, ValueType offset) +{ + this->wasInterrupted(); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + ValueType value(*iter + a*offset); + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + iter.setValue(value); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + iter.setValue(*iter + offset); + } + } + } +} + + +template +inline bool +Filter::wasInterrupted() +{ + if (util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return true; + } + return false; +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/FindActiveValues.h b/openvdb/tools/FindActiveValues.h new file mode 100644 index 00000000..feb06e1f --- /dev/null +++ b/openvdb/tools/FindActiveValues.h @@ -0,0 +1,430 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Ken Museth +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +// +/// @file FindActiveValues.h +/// +/// @brief Finds the active values in a tree which intersects a bounding box. +/// Two methods are provided, one that counts the number of active values +/// and one that simply tests if any active values intersect the bbox. +/// +/// @warning For repeated calls to the free-standing functions defined below +/// consider instead creating an instance of FindActiveValues +/// and then repeatedly call its member methods. This assumes the tree +/// to be constant between calls but is sightly faster. +/// +/// @author Ken Museth + +#ifndef OPENVDB_TOOLS_FINDACTIVEVALUES_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_FINDACTIVEVALUES_HAS_BEEN_INCLUDED + +#include +#include // for OPENVDB_VERSION_NAME +#include +#include + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Returns true if the bounding box intersects any of the active +/// values in a tree, i.e. either active voxels or active tiles. +/// +/// @warning For repeated calls to this method consider instead creating an instance of +/// FindActiveValues and then repeatedly call any(). This assumes the tree +/// to be constant between calls but is slightly faster. +/// +/// @param tree const tree to be tested for active values. +/// @param bbox index bounding box which is intersected against the active values. +template +inline bool +anyActiveValues(const TreeT& tree, const CoordBBox &bbox); + +/// @brief Returns true if the bounding box intersects none of the active +/// values in a tree, i.e. neither active voxels or active tiles. +/// +/// @warning For repeated calls to this method consider instead creating an instance of +/// FindActiveValues and then repeatedly call none(). This assumes the tree +/// to be constant between calls but is slightly faster. +/// +/// @param tree const tree to be tested for active values. +/// @param bbox index bounding box which is intersected against the active values. +template +inline bool +noActiveValues(const TreeT& tree, const CoordBBox &bbox); + +/// @brief Returns the number of active values that intersects a bounding box intersects, +/// i.e. the count includes both active voxels and virtual voxels in active tiles. +/// +/// @warning For repeated calls to this method consider instead creating an instance of +/// FindActiveValues and then repeatedly call count(). This assumes the tree +/// to be constant between calls but is slightly faster. +/// +/// @param tree const tree to be tested for active values. +/// @param bbox index bounding box which is intersected against the active values. +template +inline Index64 +countActiveValues(const TreeT& tree, const CoordBBox &bbox); + +////////////////////////////////////////////////////////////////////////////////////////// + +/// @brief Finds the active values in a tree which intersects a bounding box. +/// +/// @details Two methods are provided, one that count the number of active values +/// and one that simply tests if any active values intersect the bbox. +/// +/// @warning Tree nodes are cached by this class so it's important that the tree is not +/// modified after this class is instantiated and before its methods are called. +template +class FindActiveValues +{ +public: + + /// @brief Constructor from a const tree, which is assumed not to be modified after construction. + FindActiveValues(const TreeT& tree); + + /// @brief Default destructor + ~FindActiveValues(); + + /// @brief Initiate this class with a new (or modified) tree. + void update(const TreeT& tree); + + /// @brief Returns true if the specified bounding box intersects any active values. + /// + /// @warning Using a ValueAccessor (i.e. useAccessor = true) can improve performance for especially + /// small bounding boxes, but at the cost of no thread-safety. So if multiple threads are + /// calling this method concurrently use the default setting, useAccessor = false. + bool any(const CoordBBox &bbox, bool useAccessor = false) const; + + /// @brief Returns true if the specified bounding box does not intersect any active values. + /// + /// @warning Using a ValueAccessor (i.e. useAccessor = true) can improve performance for especially + /// small bounding boxes, but at the cost of no thread-safety. So if multiple threads are + /// calling this method concurrently use the default setting, useAccessor = false. + bool none(const CoordBBox &bbox, bool useAccessor = false) const { return !this->any(bbox, useAccessor); } + + /// @brief Returns the number of active voxels intersected by the specified bounding box. + Index64 count(const CoordBBox &bbox) const; + +private: + + // Cleans up internal data structures + void clear(); + + // builds internal data structures + void init(const TreeT &tree); + + template + typename NodeT::NodeMaskType getBBoxMask(const CoordBBox &bbox, const NodeT* node) const; + + // process leaf node + inline bool any(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const; + + // process leaf node + inline Index64 count(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const; + + // process internal node + template + bool any(const NodeT* node, const CoordBBox &bbox) const; + + // process internal node + template + Index64 count(const NodeT* node, const CoordBBox &bbox) const; + + using AccT = tree::ValueAccessor; + using RootChildT = typename TreeT::RootNodeType::ChildNodeType; + + struct NodePairT; + + AccT mAcc; + std::vector mRootTiles;// cache bbox of child nodes (faster to cache than access RootNode) + std::vector mRootNodes;// cache bbox of acive tiles (faster to cache than access RootNode) + +};// FindActiveValues class + +////////////////////////////////////////////////////////////////////////////////////////// + +template +FindActiveValues::FindActiveValues(const TreeT& tree) : mAcc(tree), mRootTiles(), mRootNodes() +{ + this->init(tree); +} + +template +FindActiveValues::~FindActiveValues() +{ + this->clear(); +} + +template +void FindActiveValues::update(const TreeT& tree) +{ + this->clear(); + mAcc = AccT(tree); + this->init(tree); +} + +template +void FindActiveValues::clear() +{ + mRootNodes.clear(); + mRootTiles.clear(); +} + +template +void FindActiveValues::init(const TreeT& tree) +{ + for (auto i = tree.root().cbeginChildOn(); i; ++i) { + mRootNodes.emplace_back(i.getCoord(), &*i); + } + for (auto i = tree.root().cbeginValueOn(); i; ++i) { + mRootTiles.emplace_back(CoordBBox::createCube(i.getCoord(), RootChildT::DIM)); + } +} + +template +bool FindActiveValues::any(const CoordBBox &bbox, bool useAccessor) const +{ + if (useAccessor) { + if (mAcc.isValueOn( (bbox.min() + bbox.max())>>1 )) return true; + } else { + if (mAcc.tree().isValueOn( (bbox.min() + bbox.max())>>1 )) return true; + } + + for (auto& tile : mRootTiles) { + if (tile.hasOverlap(bbox)) return true; + } + for (auto& node : mRootNodes) { + if (!node.bbox.hasOverlap(bbox)) { + continue; + } else if (node.bbox.isInside(bbox)) { + return this->any(node.child, bbox); + } else if (this->any(node.child, bbox)) { + return true; + } + } + return false; +} + +template +Index64 FindActiveValues::count(const CoordBBox &bbox) const +{ + Index64 count = 0; + for (auto& tile : mRootTiles) {//loop over active tiles only + if (!tile.hasOverlap(bbox)) { + continue;//ignore non-overlapping tiles + } else if (tile.isInside(bbox)) { + return bbox.volume();// bbox is completely inside the active tile + } else if (bbox.isInside(tile)) { + count += RootChildT::NUM_VOXELS; + } else { + auto tmp = tile; + tmp.intersect(bbox); + count += tmp.volume(); + } + } + for (auto &node : mRootNodes) {//loop over child nodes of the root node only + if ( !node.bbox.hasOverlap(bbox) ) { + continue;//ignore non-overlapping child nodes + } else if ( node.bbox.isInside(bbox) ) { + return this->count(node.child, bbox);// bbox is completely inside the child node + } else { + count += this->count(node.child, bbox); + } + } + return count; +} + +template +template +typename NodeT::NodeMaskType FindActiveValues::getBBoxMask(const CoordBBox &bbox, const NodeT* node) const +{ + typename NodeT::NodeMaskType mask; + auto b = node->getNodeBoundingBox(); + assert( bbox.hasOverlap(b) ); + if ( bbox.isInside(b) ) { + mask.setOn();//node is completely inside the bbox so early out + } else { + b.intersect(bbox); + b.min() &= NodeT::DIM-1u; + b.min() >>= NodeT::ChildNodeType::TOTAL; + b.max() &= NodeT::DIM-1u; + b.max() >>= NodeT::ChildNodeType::TOTAL; + assert( b.hasVolume() ); + auto it = b.begin(); + for (const Coord& x = *it; it; ++it) { + mask.setOn(x[2] + (x[1] << NodeT::LOG2DIM) + (x[0] << 2*NodeT::LOG2DIM)); + } + } + return mask; +} + +template +template +bool FindActiveValues::any(const NodeT* node, const CoordBBox &bbox) const +{ + // Generate a bit mask of the bbox coverage + auto mask = this->getBBoxMask(bbox, node); + + // Check active tiles + const auto tmp = mask & node->getValueMask();// prune active the tile mask with the bbox mask + if (!tmp.isOff()) return true; + + // Check child nodes + mask &= node->getChildMask();// prune the child mask with the bbox mask + const auto* table = node->getTable(); + bool test = false; + for (auto i = mask.beginOn(); !test && i; ++i) { + test = this->any(table[i.pos()].getChild(), bbox); + } + return test; +} + +template +inline bool FindActiveValues::any(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const +{ + bool test = leaf->getValueMask().isOn(); + + for (auto i = leaf->cbeginValueOn(); !test && i; ++i) { + test = bbox.isInside(i.getCoord()); + } + return test; +} + +template +inline Index64 FindActiveValues::count(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const +{ + Index64 count = 0; + if (leaf->getValueMask().isOn()) { + auto b = leaf->getNodeBoundingBox(); + b.intersect(bbox); + count = b.volume(); + } else { + for (auto i = leaf->cbeginValueOn(); i; ++i) { + if (bbox.isInside(i.getCoord())) ++count; + } + } + return count; +} + +template +template +Index64 FindActiveValues::count(const NodeT* node, const CoordBBox &bbox) const +{ + Index64 count = 0; + + // Generate a bit masks + auto mask = this->getBBoxMask(bbox, node); + const auto childMask = mask & node->getChildMask();// prune the child mask with the bbox mask + mask &= node->getValueMask();// prune active tile mask with the bbox mask + const auto* table = node->getTable(); + + {// Check child nodes + using ChildT = typename NodeT::ChildNodeType; + using RangeT = tbb::blocked_range::iterator>; + std::vector childNodes(childMask.countOn()); + int j=0; + for (auto i = childMask.beginOn(); i; ++i, ++j) childNodes[j] = table[i.pos()].getChild(); + count += tbb::parallel_reduce( RangeT(childNodes.begin(), childNodes.end()), 0, + [&](const RangeT& r, Index64 sum)->Index64 { + for ( auto i = r.begin(); i != r.end(); ++i ) sum += this->count(*i, bbox); + return sum; + }, []( Index64 a, Index64 b )->Index64 { return a+b; } + ); + } + + {// Check active tiles + std::vector coords(mask.countOn()); + using RangeT = tbb::blocked_range::iterator>; + int j=0; + for (auto i = mask.beginOn(); i; ++i, ++j) coords[j] = node->offsetToGlobalCoord(i.pos()); + count += tbb::parallel_reduce( RangeT(coords.begin(), coords.end()), 0, + [&bbox](const RangeT& r, Index64 sum)->Index64 { + for ( auto i = r.begin(); i != r.end(); ++i ) { + auto b = CoordBBox::createCube(*i, NodeT::ChildNodeType::DIM); + b.intersect(bbox); + sum += b.volume(); + } + return sum; + }, []( Index64 a, Index64 b )->Index64 { return a+b; } + ); + } + + return count; +} + +template +struct FindActiveValues::NodePairT +{ + const RootChildT* child; + const CoordBBox bbox; + NodePairT(const Coord& c = Coord(), const RootChildT* p = nullptr) + : child(p), bbox(CoordBBox::createCube(c, RootChildT::DIM)) + { + } +};// NodePairT struct + +////////////////////////////////////////////////////////////////////////////////////////// + +// Implementation of stand-alone function +template +inline bool +anyActiveValues(const TreeT& tree, const CoordBBox &bbox) +{ + FindActiveValues op(tree); + return op.any(bbox); +} + +// Implementation of stand-alone function +template +inline bool +noActiveValues(const TreeT& tree, const CoordBBox &bbox) +{ + FindActiveValues op(tree); + return op.none(bbox); +} + +// Implementation of stand-alone function +template +inline bool +countActiveValues(const TreeT& tree, const CoordBBox &bbox) +{ + FindActiveValues op(tree); + return op.count(bbox); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_FINDACTIVEVALUES_HAS_BEEN_INCLUDED + +// Copyright (c) Ken Museth +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/openvdb/tools/GridOperators.h b/openvdb/tools/GridOperators.h new file mode 100644 index 00000000..31305a1a --- /dev/null +++ b/openvdb/tools/GridOperators.h @@ -0,0 +1,1083 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file tools/GridOperators.h +/// +/// @brief Apply an operator to an input grid to produce an output grid +/// with the same active voxel topology but a potentially different value type. + +#ifndef OPENVDB_TOOLS_GRID_OPERATORS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_GRID_OPERATORS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include "ValueTransformer.h" // for tools::foreach() +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief VectorToScalarConverter::Type is the type of a grid +/// having the same tree configuration as VectorGridType but a scalar value type, T, +/// where T is the type of the original vector components. +/// @details For example, VectorToScalarConverter::Type is equivalent to DoubleGrid. +template struct VectorToScalarConverter { + typedef typename VectorGridType::ValueType::value_type VecComponentValueT; + typedef typename VectorGridType::template ValueConverter::Type Type; +}; + +/// @brief ScalarToVectorConverter::Type is the type of a grid +/// having the same tree configuration as ScalarGridType but value type Vec3 +/// where T is ScalarGridType::ValueType. +/// @details For example, ScalarToVectorConverter::Type is equivalent to Vec3DGrid. +template struct ScalarToVectorConverter { + typedef math::Vec3 VectorValueT; + typedef typename ScalarGridType::template ValueConverter::Type Type; +}; + + +/// @brief Compute the Closest-Point Transform (CPT) from a distance field. +/// @return a new vector-valued grid with the same numerical precision as the input grid +/// (for example, if the input grid is a DoubleGrid, the output grid will be a Vec3DGrid) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, bool threaded = true) +{ + return cpt(grid, threaded, nullptr); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return cpt(grid, mask, threaded, nullptr); +} + + +/// @brief Compute the curl of the given vector-valued grid. +/// @return a new vector-valued grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename GridType::Ptr +curl(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +curl(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +curl(const GridType& grid, bool threaded = true) +{ + return curl(grid, threaded, nullptr); +} + +template inline +typename GridType::Ptr +curl(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return curl(grid, mask, threaded, nullptr); +} + + +/// @brief Compute the divergence of the given vector-valued grid. +/// @return a new scalar-valued grid with the same numerical precision as the input grid +/// (for example, if the input grid is a Vec3DGrid, the output grid will be a DoubleGrid) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, bool threaded = true) +{ + return divergence(grid, threaded, nullptr); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return divergence(grid, mask, threaded, nullptr); +} + + +/// @brief Compute the gradient of the given scalar grid. +/// @return a new vector-valued grid with the same numerical precision as the input grid +/// (for example, if the input grid is a DoubleGrid, the output grid will be a Vec3DGrid) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, bool threaded = true) +{ + return gradient(grid, threaded, nullptr); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return gradient(grid, mask, threaded, nullptr); +} + + +/// @brief Compute the Laplacian of the given scalar grid. +/// @return a new scalar grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename GridType::Ptr +laplacian(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, bool threaded = true) +{ + return laplacian(grid, threaded, nullptr); +} + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, const MaskT mask, bool threaded = true) +{ + return laplacian(grid, mask, threaded, nullptr); +} + + +/// @brief Compute the mean curvature of the given grid. +/// @return a new grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, bool threaded = true) +{ + return meanCurvature(grid, threaded, nullptr); +} + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return meanCurvature(grid, mask, threaded, nullptr); +} + + +/// @brief Compute the magnitudes of the vectors of the given vector-valued grid. +/// @return a new scalar-valued grid with the same numerical precision as the input grid +/// (for example, if the input grid is a Vec3DGrid, the output grid will be a DoubleGrid) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, bool threaded = true) +{ + return magnitude(grid, threaded, nullptr); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return magnitude(grid, mask, threaded, nullptr); +} + + +/// @brief Normalize the vectors of the given vector-valued grid. +/// @return a new vector-valued grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename GridType::Ptr +normalize(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +normalize(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +normalize(const GridType& grid, bool threaded = true) +{ + return normalize(grid, threaded, nullptr); +} + +template inline +typename GridType::Ptr +normalize(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return normalize(grid, mask, threaded, nullptr); +} + + +//////////////////////////////////////// + + +namespace gridop { + +/// @brief ToMaskGrid::Type is the type of a grid having the same +/// tree hierarchy as grid type T but a value equal to its active state. +/// @details For example, ToMaskGrid::Type is equivalent to MaskGrid. +template +struct ToMaskGrid { + typedef Grid::Type> Type; +}; + + +/// @brief Apply an operator to an input grid to produce an output grid +/// with the same active voxel topology but a potentially different value type. +/// @details To facilitate inlining, this class is also templated on a Map type. +/// +/// @note This is a helper class and should never be used directly. +template< + typename InGridT, + typename MaskGridType, + typename OutGridT, + typename MapT, + typename OperatorT, + typename InterruptT = util::NullInterrupter> +class GridOperator +{ +public: + typedef typename OutGridT::TreeType OutTreeT; + typedef typename OutTreeT::LeafNodeType OutLeafT; + typedef typename tree::LeafManager LeafManagerT; + + GridOperator(const InGridT& grid, const MaskGridType* mask, const MapT& map, + InterruptT* interrupt = nullptr, bool densify = true) + : mAcc(grid.getConstAccessor()) + , mMap(map) + , mInterrupt(interrupt) + , mMask(mask) + , mDensify(densify) ///< @todo consider adding a "NeedsDensification" operator trait + { + } + GridOperator(const GridOperator&) = default; + GridOperator& operator=(const GridOperator&) = default; + virtual ~GridOperator() = default; + + typename OutGridT::Ptr process(bool threaded = true) + { + if (mInterrupt) mInterrupt->start("Processing grid"); + + // Derive background value of the output grid + typename InGridT::TreeType tmp(mAcc.tree().background()); + typename OutGridT::ValueType backg = OperatorT::result(mMap, tmp, math::Coord(0)); + + // The output tree is topology copy, optionally densified, of the input tree. + // (Densification is necessary for some operators because applying the operator to + // a constant tile produces distinct output values, particularly along tile borders.) + /// @todo Can tiles be handled correctly without densification, or by densifying + /// only to the width of the operator stencil? + typename OutTreeT::Ptr tree(new OutTreeT(mAcc.tree(), backg, TopologyCopy())); + if (mDensify) tree->voxelizeActiveTiles(); + + // create grid with output tree and unit transform + typename OutGridT::Ptr result(new OutGridT(tree)); + + // Modify the solution area if a mask was supplied. + if (mMask) { + result->topologyIntersection(*mMask); + } + + // transform of output grid = transform of input grid + result->setTransform(math::Transform::Ptr(new math::Transform( mMap.copy() ))); + + LeafManagerT leafManager(*tree); + + if (threaded) { + tbb::parallel_for(leafManager.leafRange(), *this); + } else { + (*this)(leafManager.leafRange()); + } + + // If the tree wasn't densified, it might have active tiles that need to be processed. + if (!mDensify) { + using TileIter = typename OutTreeT::ValueOnIter; + + TileIter tileIter = tree->beginValueOn(); + tileIter.setMaxDepth(tileIter.getLeafDepth() - 1); // skip leaf values (i.e., voxels) + + AccessorT inAcc = mAcc; // each thread needs its own accessor, captured by value + auto tileOp = [this, inAcc](const TileIter& it) { + // Apply the operator to the input grid's tile value at the iterator's + // current coordinates, and set the output tile's value to the result. + it.setValue(OperatorT::result(this->mMap, inAcc, it.getCoord())); + }; + + // Apply the operator to tile values, optionally in parallel. + // (But don't share the functor; each thread needs its own accessor.) + tools::foreach(tileIter, tileOp, threaded, /*shareFunctor=*/false); + } + + if (mDensify) tree->prune(); + + if (mInterrupt) mInterrupt->end(); + return result; + } + + /// @brief Iterate sequentially over LeafNodes and voxels in the output + /// grid and apply the operator using a value accessor for the input grid. + /// + /// @note Never call this public method directly - it is called by + /// TBB threads only! + void operator()(const typename LeafManagerT::LeafRange& range) const + { + if (util::wasInterrupted(mInterrupt)) tbb::task::self().cancel_group_execution(); + + for (typename LeafManagerT::LeafRange::Iterator leaf=range.begin(); leaf; ++leaf) { + for (typename OutLeafT::ValueOnIter value=leaf->beginValueOn(); value; ++value) { + value.setValue(OperatorT::result(mMap, mAcc, value.getCoord())); + } + } + } + +protected: + typedef typename InGridT::ConstAccessor AccessorT; + mutable AccessorT mAcc; + const MapT& mMap; + InterruptT* mInterrupt; + const MaskGridType* mMask; + const bool mDensify; +}; // end of GridOperator class + +} // namespace gridop + + +//////////////////////////////////////// + + +/// @brief Compute the closest-point transform of a scalar grid. +template< + typename InGridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Cpt +{ +public: + typedef InGridT InGridType; + typedef typename ScalarToVectorConverter::Type OutGridType; + + Cpt(const InGridType& grid, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(nullptr) + { + } + + Cpt(const InGridType& grid, const MaskGridType& mask, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename OutGridType::Ptr process(bool threaded = true, bool useWorldTransform = true) + { + Functor functor(mInputGrid, mMask, threaded, useWorldTransform, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_CONTRAVARIANT_ABSOLUTE); + return functor.mOutputGrid; + } + +private: + struct IsOpT + { + template + static typename OutGridType::ValueType + result(const MapT& map, const AccT& acc, const Coord& xyz) + { + return math::CPT::result(map, acc, xyz); + } + }; + struct WsOpT + { + template + static typename OutGridType::ValueType + result(const MapT& map, const AccT& acc, const Coord& xyz) + { + return math::CPT_RANGE::result(map, acc, xyz); + } + }; + struct Functor + { + Functor(const InGridType& grid, const MaskGridType* mask, + bool threaded, bool worldspace, InterruptT* interrupt) + : mThreaded(threaded) + , mWorldSpace(worldspace) + , mInputGrid(grid) + , mInterrupt(interrupt) + , mMask(mask) + {} + + template + void operator()(const MapT& map) + { + if (mWorldSpace) { + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt, /*densify=*/false); + mOutputGrid = op.process(mThreaded); // cache the result + } else { + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt, /*densify=*/false); + mOutputGrid = op.process(mThreaded); // cache the result + } + } + const bool mThreaded; + const bool mWorldSpace; + const InGridType& mInputGrid; + typename OutGridType::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; + const InGridType& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Cpt class + + +//////////////////////////////////////// + + +/// @brief Compute the curl of a vector grid. +template< + typename GridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Curl +{ +public: + typedef GridT InGridType; + typedef GridT OutGridType; + + Curl(const GridT& grid, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(nullptr) + { + } + + Curl(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename GridT::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); + return functor.mOutputGrid; + } + +private: + struct Functor + { + Functor(const GridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::Curl OpT; + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const GridT& mInputGrid; + typename GridT::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Curl class + + +//////////////////////////////////////// + + +/// @brief Compute the divergence of a vector grid. +template< + typename InGridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Divergence +{ +public: + typedef InGridT InGridType; + typedef typename VectorToScalarConverter::Type OutGridType; + + Divergence(const InGridT& grid, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(nullptr) + { + } + + Divergence(const InGridT& grid, const MaskGridType& mask, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename OutGridType::Ptr process(bool threaded = true) + { + if (mInputGrid.getGridClass() == GRID_STAGGERED) { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + return functor.mOutputGrid; + } else { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + return functor.mOutputGrid; + } + } + +protected: + template + struct Functor + { + Functor(const InGridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::Divergence OpT; + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const InGridType& mInputGrid; + typename OutGridType::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const InGridType& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Divergence class + + +//////////////////////////////////////// + + +/// @brief Compute the gradient of a scalar grid. +template< + typename InGridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Gradient +{ +public: + typedef InGridT InGridType; + typedef typename ScalarToVectorConverter::Type OutGridType; + + Gradient(const InGridT& grid, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(nullptr) + { + } + + Gradient(const InGridT& grid, const MaskGridType& mask, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename OutGridType::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); + return functor.mOutputGrid; + } + +protected: + struct Functor + { + Functor(const InGridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::Gradient OpT; + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const InGridT& mInputGrid; + typename OutGridType::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const InGridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Gradient class + + +//////////////////////////////////////// + + +template< + typename GridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Laplacian +{ +public: + typedef GridT InGridType; + typedef GridT OutGridType; + + Laplacian(const GridT& grid, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(nullptr) + { + } + + Laplacian(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename GridT::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); + return functor.mOutputGrid; + } + +protected: + struct Functor + { + Functor(const GridT& grid, const MaskGridType* mask, bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::Laplacian OpT; + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const GridT& mInputGrid; + typename GridT::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Laplacian class + + +//////////////////////////////////////// + + +template< + typename GridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class MeanCurvature +{ +public: + typedef GridT InGridType; + typedef GridT OutGridType; + + MeanCurvature(const GridT& grid, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(nullptr) + { + } + + MeanCurvature(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename GridT::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); + return functor.mOutputGrid; + } + +protected: + struct Functor + { + Functor(const GridT& grid, const MaskGridType* mask, bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::MeanCurvature OpT; + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const GridT& mInputGrid; + typename GridT::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of MeanCurvature class + + +//////////////////////////////////////// + + +template< + typename InGridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Magnitude +{ +public: + typedef InGridT InGridType; + typedef typename VectorToScalarConverter::Type OutGridType; + + Magnitude(const InGridType& grid, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(nullptr) + { + } + + Magnitude(const InGridType& grid, const MaskGridType& mask, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename OutGridType::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + return functor.mOutputGrid; + } + +protected: + struct OpT + { + template + static typename OutGridType::ValueType + result(const MapT&, const AccT& acc, const Coord& xyz) { return acc.getValue(xyz).length();} + }; + struct Functor + { + Functor(const InGridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt, /*densify=*/false); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const InGridType& mInputGrid; + typename OutGridType::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const InGridType& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Magnitude class + + +//////////////////////////////////////// + + +template< + typename GridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Normalize +{ +public: + typedef GridT InGridType; + typedef GridT OutGridType; + + Normalize(const GridT& grid, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(nullptr) + { + } + + Normalize(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = nullptr): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename GridT::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (typename GridT::Ptr outGrid = functor.mOutputGrid) { + const VecType vecType = mInputGrid.getVectorType(); + if (vecType == VEC_COVARIANT) { + outGrid->setVectorType(VEC_COVARIANT_NORMALIZE); + } else { + outGrid->setVectorType(vecType); + } + } + return functor.mOutputGrid; + } + +protected: + struct OpT + { + template + static typename OutGridType::ValueType + result(const MapT&, const AccT& acc, const Coord& xyz) + { + typename OutGridType::ValueType vec = acc.getValue(xyz); + if ( !vec.normalize() ) vec.setZero(); + return vec; + } + }; + struct Functor + { + Functor(const GridT& grid, const MaskGridType* mask, bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt, /*densify=*/false); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const GridT& mInputGrid; + typename GridT::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Normalize class + + +//////////////////////////////////////// + + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Cpt::Type, InterruptT> op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Cpt op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +curl(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Curl::Type, InterruptT> op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +curl(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Curl op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Divergence::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Divergence op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Gradient::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Gradient op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Laplacian::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Laplacian op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + MeanCurvature::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + MeanCurvature op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Magnitude::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Magnitude op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +normalize(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Normalize::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +normalize(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Normalize op(grid, mask, interrupt); + return op.process(threaded); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_GRID_OPERATORS_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/GridTransformer.h b/openvdb/tools/GridTransformer.h new file mode 100644 index 00000000..cf49b262 --- /dev/null +++ b/openvdb/tools/GridTransformer.h @@ -0,0 +1,1016 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file GridTransformer.h +/// @author Peter Cucka + +#ifndef OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED + +#include +#include +#include // for isApproxEqual() +#include +#include "ChangeBackground.h" +#include "Interpolation.h" +#include "LevelSetRebuild.h" // for doLevelSetRebuild() +#include "SignedFloodFill.h" // for signedFloodFill +#include "Prune.h" // for pruneLevelSet +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Resample an input grid into an output grid of the same type such that, +/// after resampling, the input and output grids coincide (apart from sampling +/// artifacts), but the output grid's transform is unchanged. +/// @details Specifically, this function resamples the input grid into the output +/// grid's index space, using a sampling kernel like PointSampler, BoxSampler, +/// or QuadraticSampler. +/// @param inGrid the grid to be resampled +/// @param outGrid the grid into which to write the resampled voxel data +/// @param interrupter an object adhering to the util::NullInterrupter interface +/// @par Example: +/// @code +/// // Create an input grid with the default identity transform +/// // and populate it with a level-set sphere. +/// FloatGrid::ConstPtr src = tools::makeSphere(...); +/// // Create an output grid and give it a uniform-scale transform. +/// FloatGrid::Ptr dest = FloatGrid::create(); +/// const float voxelSize = 0.5; +/// dest->setTransform(math::Transform::createLinearTransform(voxelSize)); +/// // Resample the input grid into the output grid, reproducing +/// // the level-set sphere at a smaller voxel size. +/// MyInterrupter interrupter = ...; +/// tools::resampleToMatch(*src, *dest, interrupter); +/// @endcode +template +inline void +resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter); + +/// @brief Resample an input grid into an output grid of the same type such that, +/// after resampling, the input and output grids coincide (apart from sampling +/// artifacts), but the output grid's transform is unchanged. +/// @details Specifically, this function resamples the input grid into the output +/// grid's index space, using a sampling kernel like PointSampler, BoxSampler, +/// or QuadraticSampler. +/// @param inGrid the grid to be resampled +/// @param outGrid the grid into which to write the resampled voxel data +/// @par Example: +/// @code +/// // Create an input grid with the default identity transform +/// // and populate it with a level-set sphere. +/// FloatGrid::ConstPtr src = tools::makeSphere(...); +/// // Create an output grid and give it a uniform-scale transform. +/// FloatGrid::Ptr dest = FloatGrid::create(); +/// const float voxelSize = 0.5; +/// dest->setTransform(math::Transform::createLinearTransform(voxelSize)); +/// // Resample the input grid into the output grid, reproducing +/// // the level-set sphere at a smaller voxel size. +/// tools::resampleToMatch(*src, *dest); +/// @endcode +template +inline void +resampleToMatch(const GridType& inGrid, GridType& outGrid); + + +//////////////////////////////////////// + + +namespace internal { + +/// @brief A TileSampler wraps a grid sampler of another type (BoxSampler, +/// QuadraticSampler, etc.), and for samples that fall within a given tile +/// of the grid, it returns a cached tile value instead of accessing the grid. +template +class TileSampler: public Sampler +{ +public: + using ValueT = typename TreeT::ValueType; + + /// @param b the index-space bounding box of a particular grid tile + /// @param tileVal the tile's value + /// @param on the tile's active state + TileSampler(const CoordBBox& b, const ValueT& tileVal, bool on): + mBBox(b.min().asVec3d(), b.max().asVec3d()), mVal(tileVal), mActive(on), mEmpty(false) + { + mBBox.expand(-this->radius()); // shrink the bounding box by the sample radius + mEmpty = mBBox.empty(); + } + + bool sample(const TreeT& inTree, const Vec3R& inCoord, ValueT& result) const + { + if (!mEmpty && mBBox.isInside(inCoord)) { result = mVal; return mActive; } + return Sampler::sample(inTree, inCoord, result); + } + +protected: + BBoxd mBBox; + ValueT mVal; + bool mActive, mEmpty; +}; + + +/// @brief For point sampling, tree traversal is less expensive than testing +/// bounding box membership. +template +class TileSampler: public PointSampler { +public: + TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {} +}; + +/// @brief For point sampling, tree traversal is less expensive than testing +/// bounding box membership. +template +class TileSampler: public StaggeredPointSampler { +public: + TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {} +}; + +} // namespace internal + + +//////////////////////////////////////// + + +/// A GridResampler applies a geometric transformation to an +/// input grid using one of several sampling schemes, and stores +/// the result in an output grid. +/// +/// Usage: +/// @code +/// GridResampler resampler(); +/// resampler.transformGrid(xform, inGrid, outGrid); +/// @endcode +/// where @c xform is a functor that implements the following methods: +/// @code +/// bool isAffine() const +/// openvdb::Vec3d transform(const openvdb::Vec3d&) const +/// openvdb::Vec3d invTransform(const openvdb::Vec3d&) const +/// @endcode +/// @note When the transform is affine and can be expressed as a 4 x 4 matrix, +/// a GridTransformer is much more efficient than a GridResampler. +class GridResampler +{ +public: + using Ptr = SharedPtr; + using InterruptFunc = std::function; + + GridResampler(): mThreaded(true), mTransformTiles(true) {} + virtual ~GridResampler() {} + + GridResampler(const GridResampler&) = default; + GridResampler& operator=(const GridResampler&) = default; + + /// Enable or disable threading. (Threading is enabled by default.) + void setThreaded(bool b) { mThreaded = b; } + /// Return @c true if threading is enabled. + bool threaded() const { return mThreaded; } + /// Enable or disable processing of tiles. (Enabled by default, except for level set grids.) + void setTransformTiles(bool b) { mTransformTiles = b; } + /// Return @c true if tile processing is enabled. + bool transformTiles() const { return mTransformTiles; } + + /// @brief Allow processing to be aborted by providing an interrupter object. + /// The interrupter will be queried periodically during processing. + /// @see util/NullInterrupter.h for interrupter interface requirements. + template void setInterrupter(InterrupterType&); + + template + void transformGrid(const Transformer&, + const GridT& inGrid, GridT& outGrid) const; + +protected: + template + void applyTransform(const Transformer&, const GridT& inGrid, GridT& outGrid) const; + + bool interrupt() const { return mInterrupt && mInterrupt(); } + +private: + template + static void transformBBox(const Transformer&, const CoordBBox& inBBox, + const InTreeT& inTree, OutTreeT& outTree, const InterruptFunc&, + const Sampler& = Sampler()); + + template + class RangeProcessor; + + bool mThreaded, mTransformTiles; + InterruptFunc mInterrupt; +}; + + +//////////////////////////////////////// + + +/// @brief A GridTransformer applies a geometric transformation to an +/// input grid using one of several sampling schemes, and stores +/// the result in an output grid. +/// +/// @note GridTransformer is optimized for affine transformations. +/// +/// Usage: +/// @code +/// Mat4R xform = ...; +/// GridTransformer transformer(xform); +/// transformer.transformGrid(inGrid, outGrid); +/// @endcode +/// or +/// @code +/// Vec3R pivot = ..., scale = ..., rotate = ..., translate = ...; +/// GridTransformer transformer(pivot, scale, rotate, translate); +/// transformer.transformGrid(inGrid, outGrid); +/// @endcode +class GridTransformer: public GridResampler +{ +public: + using Ptr = SharedPtr; + + GridTransformer(const Mat4R& xform); + GridTransformer( + const Vec3R& pivot, + const Vec3R& scale, + const Vec3R& rotate, + const Vec3R& translate, + const std::string& xformOrder = "tsr", + const std::string& rotationOrder = "zyx"); + ~GridTransformer() override = default; + + GridTransformer(const GridTransformer&) = default; + GridTransformer& operator=(const GridTransformer&) = default; + + const Mat4R& getTransform() const { return mTransform; } + + template + void transformGrid(const GridT& inGrid, GridT& outGrid) const; + +private: + struct MatrixTransform; + + inline void init(const Vec3R& pivot, const Vec3R& scale, + const Vec3R& rotate, const Vec3R& translate, + const std::string& xformOrder, const std::string& rotOrder); + + Vec3R mPivot; + Vec3i mMipLevels; + Mat4R mTransform, mPreScaleTransform, mPostScaleTransform; +}; + + +//////////////////////////////////////// + + +namespace local_util { + +/// @brief Decompose an affine transform into scale, rotation and translation components. +/// @return @c false if the given matrix is not affine or cannot otherwise be decomposed. +template +inline bool +decompose(const math::Mat4& m, math::Vec3& scale, + math::Vec3& rotate, math::Vec3& translate) +{ + if (!math::isAffine(m)) return false; + + // This is the translation in world space + translate = m.getTranslation(); + // Extract translation. + const math::Mat3 xform = m.getMat3(); + + const math::Vec3 unsignedScale( + (math::Vec3(1, 0, 0) * xform).length(), + (math::Vec3(0, 1, 0) * xform).length(), + (math::Vec3(0, 0, 1) * xform).length()); + + const bool hasUniformScale = unsignedScale.eq(math::Vec3(unsignedScale[0])); + + bool hasRotation = false; + bool validDecomposition = false; + + T minAngle = std::numeric_limits::max(); + + // If the transformation matrix contains a reflection, + // test different negative scales to find a decomposition + // that favors the optimal resampling algorithm. + for (size_t n = 0; n < 8; ++n) { + + const math::Vec3 signedScale( + n & 0x1 ? -unsignedScale.x() : unsignedScale.x(), + n & 0x2 ? -unsignedScale.y() : unsignedScale.y(), + n & 0x4 ? -unsignedScale.z() : unsignedScale.z()); + + // Extract scale and potentially reflection. + const math::Mat3 mat = xform * math::scale >(signedScale).inverse(); + if (mat.det() < T(0.0)) continue; // Skip if mat contains a reflection. + + const math::Vec3 tmpAngle = math::eulerAngles(mat, math::XYZ_ROTATION); + + const math::Mat3 rebuild = + math::rotation >(math::Vec3(1, 0, 0), tmpAngle.x()) * + math::rotation >(math::Vec3(0, 1, 0), tmpAngle.y()) * + math::rotation >(math::Vec3(0, 0, 1), tmpAngle.z()) * + math::scale >(signedScale); + + if (xform.eq(rebuild)) { + + const T maxAngle = std::max(std::abs(tmpAngle[0]), + std::max(std::abs(tmpAngle[1]), std::abs(tmpAngle[2]))); + + if (!(minAngle < maxAngle)) { // Update if less or equal. + + minAngle = maxAngle; + rotate = tmpAngle; + scale = signedScale; + + hasRotation = !rotate.eq(math::Vec3::zero()); + validDecomposition = true; + + if (hasUniformScale || !hasRotation) { + // Current decomposition is optimal. + break; + } + } + } + } + + if (!validDecomposition || (hasRotation && !hasUniformScale)) { + // The decomposition is invalid if the transformation matrix contains shear. + // No unique decomposition if scale is nonuniform and rotation is nonzero. + return false; + } + + return true; +} + +} // namespace local_util + + +//////////////////////////////////////// + + +/// This class implements the Transformer functor interface (specifically, +/// the isAffine(), transform() and invTransform() methods) for a transform +/// that is expressed as a 4 x 4 matrix. +struct GridTransformer::MatrixTransform +{ + MatrixTransform(): mat(Mat4R::identity()), invMat(Mat4R::identity()) {} + MatrixTransform(const Mat4R& xform): mat(xform), invMat(xform.inverse()) {} + + bool isAffine() const { return math::isAffine(mat); } + + Vec3R transform(const Vec3R& pos) const { return mat.transformH(pos); } + + Vec3R invTransform(const Vec3R& pos) const { return invMat.transformH(pos); } + + Mat4R mat, invMat; +}; + + +//////////////////////////////////////// + + +/// @brief This class implements the Transformer functor interface (specifically, +/// the isAffine(), transform() and invTransform() methods) for a transform +/// that maps an A grid into a B grid's index space such that, after resampling, +/// A's index space and transform match B's index space and transform. +class ABTransform +{ +public: + /// @param aXform the A grid's transform + /// @param bXform the B grid's transform + ABTransform(const math::Transform& aXform, const math::Transform& bXform): + mAXform(aXform), + mBXform(bXform), + mIsAffine(mAXform.isLinear() && mBXform.isLinear()), + mIsIdentity(mIsAffine && mAXform == mBXform) + {} + + bool isAffine() const { return mIsAffine; } + + bool isIdentity() const { return mIsIdentity; } + + openvdb::Vec3R transform(const openvdb::Vec3R& pos) const + { + return mBXform.worldToIndex(mAXform.indexToWorld(pos)); + } + + openvdb::Vec3R invTransform(const openvdb::Vec3R& pos) const + { + return mAXform.worldToIndex(mBXform.indexToWorld(pos)); + } + + const math::Transform& getA() const { return mAXform; } + const math::Transform& getB() const { return mBXform; } + +private: + const math::Transform &mAXform, &mBXform; + const bool mIsAffine; + const bool mIsIdentity; +}; + + +/// The normal entry points for resampling are the resampleToMatch() functions, +/// which correctly handle level set grids under scaling and shearing. +/// doResampleToMatch() is mainly for internal use but is typically faster +/// for level sets, and correct provided that no scaling or shearing is needed. +/// +/// @warning Do not use this function to scale or shear a level set grid. +template +inline void +doResampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter) +{ + ABTransform xform(inGrid.transform(), outGrid.transform()); + + if (Sampler::consistent() && xform.isIdentity()) { + // If the transforms of the input and output are identical, the + // output tree is simply a deep copy of the input tree. + outGrid.setTree(inGrid.tree().copy()); + } else if (xform.isAffine()) { + // If the input and output transforms are both affine, create an + // input to output transform (in:index-to-world * out:world-to-index) + // and use the fast GridTransformer API. + Mat4R mat = xform.getA().baseMap()->getAffineMap()->getMat4() * + ( xform.getB().baseMap()->getAffineMap()->getMat4().inverse() ); + + GridTransformer transformer(mat); + transformer.setInterrupter(interrupter); + + // Transform the input grid and store the result in the output grid. + transformer.transformGrid(inGrid, outGrid); + } else { + // If either the input or the output transform is non-affine, + // use the slower GridResampler API. + GridResampler resampler; + resampler.setInterrupter(interrupter); + + resampler.transformGrid(xform, inGrid, outGrid); + } +} + + +template +inline void +resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter) +{ + if (inGrid.getGridClass() == GRID_LEVEL_SET) { + // If the input grid is a level set, resample it using the level set rebuild tool. + + if (inGrid.constTransform() == outGrid.constTransform()) { + // If the transforms of the input and output grids are identical, + // the output tree is simply a deep copy of the input tree. + outGrid.setTree(inGrid.tree().copy()); + return; + } + + // If the output grid is a level set, resample the input grid to have the output grid's + // background value. Otherwise, preserve the input grid's background value. + using ValueT = typename GridType::ValueType; + const bool outIsLevelSet = outGrid.getGridClass() == openvdb::GRID_LEVEL_SET; + + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const ValueT halfWidth = outIsLevelSet + ? ValueT(outGrid.background() * (1.0 / outGrid.voxelSize()[0])) + : ValueT(inGrid.background() * (1.0 / inGrid.voxelSize()[0])); + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + + typename GridType::Ptr tempGrid; + try { + tempGrid = doLevelSetRebuild(inGrid, /*iso=*/zeroVal(), + /*exWidth=*/halfWidth, /*inWidth=*/halfWidth, + &outGrid.constTransform(), &interrupter); + } catch (TypeError&) { + // The input grid is classified as a level set, but it has a value type + // that is not supported by the level set rebuild tool. Fall back to + // using the generic resampler. + tempGrid.reset(); + } + if (tempGrid) { + outGrid.setTree(tempGrid->treePtr()); + return; + } + } + + // If the input grid is not a level set, use the generic resampler. + doResampleToMatch(inGrid, outGrid, interrupter); +} + + +template +inline void +resampleToMatch(const GridType& inGrid, GridType& outGrid) +{ + util::NullInterrupter interrupter; + resampleToMatch(inGrid, outGrid, interrupter); +} + + +//////////////////////////////////////// + + +inline +GridTransformer::GridTransformer(const Mat4R& xform): + mPivot(0, 0, 0), + mMipLevels(0, 0, 0), + mTransform(xform), + mPreScaleTransform(Mat4R::identity()), + mPostScaleTransform(Mat4R::identity()) +{ + Vec3R scale, rotate, translate; + if (local_util::decompose(mTransform, scale, rotate, translate)) { + // If the transform can be decomposed into affine components, + // use them to set up a mipmapping-like scheme for downsampling. + init(mPivot, scale, rotate, translate, "srt", "zyx"); + } +} + + +inline +GridTransformer::GridTransformer( + const Vec3R& pivot, const Vec3R& scale, + const Vec3R& rotate, const Vec3R& translate, + const std::string& xformOrder, const std::string& rotOrder): + mPivot(0, 0, 0), + mMipLevels(0, 0, 0), + mPreScaleTransform(Mat4R::identity()), + mPostScaleTransform(Mat4R::identity()) +{ + init(pivot, scale, rotate, translate, xformOrder, rotOrder); +} + + +//////////////////////////////////////// + + +inline void +GridTransformer::init( + const Vec3R& pivot, const Vec3R& scale, + const Vec3R& rotate, const Vec3R& translate, + const std::string& xformOrder, const std::string& rotOrder) +{ + if (xformOrder.size() != 3) { + OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")"); + } + if (rotOrder.size() != 3) { + OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")"); + } + + mPivot = pivot; + + // Scaling is handled via a mipmapping-like scheme of successive + // halvings of the tree resolution, until the remaining scale + // factor is greater than or equal to 1/2. + Vec3R scaleRemainder = scale; + for (int i = 0; i < 3; ++i) { + double s = std::fabs(scale(i)); + if (s < 0.5) { + mMipLevels(i) = int(std::floor(-std::log(s)/std::log(2.0))); + scaleRemainder(i) = scale(i) * (1 << mMipLevels(i)); + } + } + + // Build pre-scale and post-scale transform matrices based on + // the user-specified order of operations. + // Note that we iterate over the transform order string in reverse order + // (e.g., "t", "r", "s", given "srt"). This is because math::Mat matrices + // postmultiply row vectors rather than premultiplying column vectors. + mTransform = mPreScaleTransform = mPostScaleTransform = Mat4R::identity(); + Mat4R* remainder = &mPostScaleTransform; + int rpos, spos, tpos; + rpos = spos = tpos = 3; + for (int ix = 2; ix >= 0; --ix) { // reverse iteration + switch (xformOrder[ix]) { + + case 'r': + rpos = ix; + mTransform.preTranslate(pivot); + remainder->preTranslate(pivot); + + int xpos, ypos, zpos; + xpos = ypos = zpos = 3; + for (int ir = 2; ir >= 0; --ir) { + switch (rotOrder[ir]) { + case 'x': + xpos = ir; + mTransform.preRotate(math::X_AXIS, rotate.x()); + remainder->preRotate(math::X_AXIS, rotate.x()); + break; + case 'y': + ypos = ir; + mTransform.preRotate(math::Y_AXIS, rotate.y()); + remainder->preRotate(math::Y_AXIS, rotate.y()); + break; + case 'z': + zpos = ir; + mTransform.preRotate(math::Z_AXIS, rotate.z()); + remainder->preRotate(math::Z_AXIS, rotate.z()); + break; + } + } + // Reject rotation order strings that don't contain exactly one + // instance of "x", "y" and "z". + if (xpos > 2 || ypos > 2 || zpos > 2) { + OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")"); + } + + mTransform.preTranslate(-pivot); + remainder->preTranslate(-pivot); + break; + + case 's': + spos = ix; + mTransform.preTranslate(pivot); + mTransform.preScale(scale); + mTransform.preTranslate(-pivot); + + remainder->preTranslate(pivot); + remainder->preScale(scaleRemainder); + remainder->preTranslate(-pivot); + remainder = &mPreScaleTransform; + break; + + case 't': + tpos = ix; + mTransform.preTranslate(translate); + remainder->preTranslate(translate); + break; + } + } + // Reject transform order strings that don't contain exactly one + // instance of "t", "r" and "s". + if (tpos > 2 || rpos > 2 || spos > 2) { + OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")"); + } +} + + +//////////////////////////////////////// + + +template +void +GridResampler::setInterrupter(InterrupterType& interrupter) +{ + mInterrupt = std::bind(&InterrupterType::wasInterrupted, + /*this=*/&interrupter, /*percent=*/-1); +} + + +template +void +GridResampler::transformGrid(const Transformer& xform, + const GridT& inGrid, GridT& outGrid) const +{ + tools::changeBackground(outGrid.tree(), inGrid.background()); + applyTransform(xform, inGrid, outGrid); +} + + +template +void +GridTransformer::transformGrid(const GridT& inGrid, GridT& outGrid) const +{ + tools::changeBackground(outGrid.tree(), inGrid.background()); + + if (!Sampler::mipmap() || mMipLevels == Vec3i::zero()) { + // Skip the mipmapping step. + const MatrixTransform xform(mTransform); + applyTransform(xform, inGrid, outGrid); + + } else { + bool firstPass = true; + const typename GridT::ValueType background = inGrid.background(); + typename GridT::Ptr tempGrid = GridT::create(background); + + if (!mPreScaleTransform.eq(Mat4R::identity())) { + firstPass = false; + // Apply the pre-scale transform to the input grid + // and store the result in a temporary grid. + const MatrixTransform xform(mPreScaleTransform); + applyTransform(xform, inGrid, *tempGrid); + } + + // While the scale factor along one or more axes is less than 1/2, + // scale the grid by half along those axes. + Vec3i count = mMipLevels; // # of halvings remaining per axis + while (count != Vec3i::zero()) { + MatrixTransform xform; + xform.mat.setTranslation(mPivot); + xform.mat.preScale(Vec3R( + count.x() ? .5 : 1, count.y() ? .5 : 1, count.z() ? .5 : 1)); + xform.mat.preTranslate(-mPivot); + xform.invMat = xform.mat.inverse(); + + if (firstPass) { + firstPass = false; + // Scale the input grid and store the result in a temporary grid. + applyTransform(xform, inGrid, *tempGrid); + } else { + // Scale the temporary grid and store the result in a transient grid, + // then swap the two and discard the transient grid. + typename GridT::Ptr destGrid = GridT::create(background); + applyTransform(xform, *tempGrid, *destGrid); + tempGrid.swap(destGrid); + } + // (3, 2, 1) -> (2, 1, 0) -> (1, 0, 0) -> (0, 0, 0), etc. + count = math::maxComponent(count - 1, Vec3i::zero()); + } + + // Apply the post-scale transform and store the result in the output grid. + if (!mPostScaleTransform.eq(Mat4R::identity())) { + const MatrixTransform xform(mPostScaleTransform); + applyTransform(xform, *tempGrid, outGrid); + } else { + outGrid.setTree(tempGrid->treePtr()); + } + } +} + + +//////////////////////////////////////// + + +template +class GridResampler::RangeProcessor +{ +public: + using LeafIterT = typename TreeT::LeafCIter; + using TileIterT = typename TreeT::ValueAllCIter; + using LeafRange = typename tree::IteratorRange; + using TileRange = typename tree::IteratorRange; + using InTreeAccessor = typename tree::ValueAccessor; + using OutTreeAccessor = typename tree::ValueAccessor; + + RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inT, TreeT& outT): + mIsRoot(true), mXform(xform), mBBox(b), + mInTree(inT), mOutTree(&outT), mInAcc(mInTree), mOutAcc(*mOutTree) + {} + + RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inTree): + mIsRoot(false), mXform(xform), mBBox(b), + mInTree(inTree), mOutTree(new TreeT(inTree.background())), + mInAcc(mInTree), mOutAcc(*mOutTree) + {} + + ~RangeProcessor() { if (!mIsRoot) delete mOutTree; } + + /// Splitting constructor: don't copy the original processor's output tree + RangeProcessor(RangeProcessor& other, tbb::split): + mIsRoot(false), + mXform(other.mXform), + mBBox(other.mBBox), + mInTree(other.mInTree), + mOutTree(new TreeT(mInTree.background())), + mInAcc(mInTree), + mOutAcc(*mOutTree), + mInterrupt(other.mInterrupt) + {} + + void setInterrupt(const InterruptFunc& f) { mInterrupt = f; } + + /// Transform each leaf node in the given range. + void operator()(LeafRange& r) + { + for ( ; r; ++r) { + if (interrupt()) break; + LeafIterT i = r.iterator(); + CoordBBox bbox(i->origin(), i->origin() + Coord(i->dim())); + if (!mBBox.empty()) { + // Intersect the leaf node's bounding box with mBBox. + bbox = CoordBBox( + Coord::maxComponent(bbox.min(), mBBox.min()), + Coord::minComponent(bbox.max(), mBBox.max())); + } + if (!bbox.empty()) { + transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt); + } + } + } + + /// Transform each non-background tile in the given range. + void operator()(TileRange& r) + { + for ( ; r; ++r) { + if (interrupt()) break; + + TileIterT i = r.iterator(); + // Skip voxels and background tiles. + if (!i.isTileValue()) continue; + if (!i.isValueOn() && math::isApproxEqual(*i, mOutTree->background())) continue; + + CoordBBox bbox; + i.getBoundingBox(bbox); + if (!mBBox.empty()) { + // Intersect the tile's bounding box with mBBox. + bbox = CoordBBox( + Coord::maxComponent(bbox.min(), mBBox.min()), + Coord::minComponent(bbox.max(), mBBox.max())); + } + if (!bbox.empty()) { + /// @todo This samples the tile voxel-by-voxel, which is much too slow. + /// Instead, compute the largest axis-aligned bounding box that is + /// contained in the transformed tile (adjusted for the sampler radius) + /// and fill it with the tile value. Then transform the remaining voxels. + internal::TileSampler + sampler(bbox, i.getValue(), i.isValueOn()); + transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt, sampler); + } + } + } + + /// Merge another processor's output tree into this processor's tree. + void join(RangeProcessor& other) + { + if (!interrupt()) mOutTree->merge(*other.mOutTree); + } + +private: + bool interrupt() const { return mInterrupt && mInterrupt(); } + + const bool mIsRoot; // true if mOutTree is the top-level tree + Transformer mXform; + CoordBBox mBBox; + const TreeT& mInTree; + TreeT* mOutTree; + InTreeAccessor mInAcc; + OutTreeAccessor mOutAcc; + InterruptFunc mInterrupt; +}; + + +//////////////////////////////////////// + + +template +void +GridResampler::applyTransform(const Transformer& xform, + const GridT& inGrid, GridT& outGrid) const +{ + using TreeT = typename GridT::TreeType; + const TreeT& inTree = inGrid.tree(); + TreeT& outTree = outGrid.tree(); + + using RangeProc = RangeProcessor; + + const GridClass gridClass = inGrid.getGridClass(); + + if (gridClass != GRID_LEVEL_SET && mTransformTiles) { + // Independently transform the tiles of the input grid. + // Note: Tiles in level sets can only be background tiles, and they + // are handled more efficiently with a signed flood fill (see below). + + RangeProc proc(xform, CoordBBox(), inTree, outTree); + proc.setInterrupt(mInterrupt); + + typename RangeProc::TileIterT tileIter = inTree.cbeginValueAll(); + tileIter.setMaxDepth(tileIter.getLeafDepth() - 1); // skip leaf nodes + typename RangeProc::TileRange tileRange(tileIter); + + if (mThreaded) { + tbb::parallel_reduce(tileRange, proc); + } else { + proc(tileRange); + } + } + + CoordBBox clipBBox; + if (gridClass == GRID_LEVEL_SET) { + // Inactive voxels in level sets can only be background voxels, and they + // are handled more efficiently with a signed flood fill (see below). + clipBBox = inGrid.evalActiveVoxelBoundingBox(); + } + + // Independently transform the leaf nodes of the input grid. + + RangeProc proc(xform, clipBBox, inTree, outTree); + proc.setInterrupt(mInterrupt); + + typename RangeProc::LeafRange leafRange(inTree.cbeginLeaf()); + + if (mThreaded) { + tbb::parallel_reduce(leafRange, proc); + } else { + proc(leafRange); + } + + // If the grid is a level set, mark inactive voxels as inside or outside. + if (gridClass == GRID_LEVEL_SET) { + tools::pruneLevelSet(outTree); + tools::signedFloodFill(outTree); + } +} + + +//////////////////////////////////////// + + +//static +template +void +GridResampler::transformBBox( + const Transformer& xform, + const CoordBBox& bbox, + const InTreeT& inTree, + OutTreeT& outTree, + const InterruptFunc& interrupt, + const Sampler& sampler) +{ + using ValueT = typename OutTreeT::ValueType; + + // Transform the corners of the input tree's bounding box + // and compute the enclosing bounding box in the output tree. + Vec3R + inRMin(bbox.min().x(), bbox.min().y(), bbox.min().z()), + inRMax(bbox.max().x()+1, bbox.max().y()+1, bbox.max().z()+1), + outRMin = math::minComponent(xform.transform(inRMin), xform.transform(inRMax)), + outRMax = math::maxComponent(xform.transform(inRMin), xform.transform(inRMax)); + for (int i = 0; i < 8; ++i) { + Vec3R corner( + i & 1 ? inRMax.x() : inRMin.x(), + i & 2 ? inRMax.y() : inRMin.y(), + i & 4 ? inRMax.z() : inRMin.z()); + outRMin = math::minComponent(outRMin, xform.transform(corner)); + outRMax = math::maxComponent(outRMax, xform.transform(corner)); + } + Vec3i + outMin = local_util::floorVec3(outRMin) - Sampler::radius(), + outMax = local_util::ceilVec3(outRMax) + Sampler::radius(); + + if (!xform.isAffine()) { + // If the transform is not affine, back-project each output voxel + // into the input tree. + Vec3R xyz, inXYZ; + Coord outXYZ; + int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z(); + for (x = outMin.x(); x <= outMax.x(); ++x) { + if (interrupt && interrupt()) break; + xyz.x() = x; + for (y = outMin.y(); y <= outMax.y(); ++y) { + if (interrupt && interrupt()) break; + xyz.y() = y; + for (z = outMin.z(); z <= outMax.z(); ++z) { + xyz.z() = z; + inXYZ = xform.invTransform(xyz); + ValueT result; + if (sampler.sample(inTree, inXYZ, result)) { + outTree.setValueOn(outXYZ, result); + } else { + // Note: Don't overwrite existing active values with inactive values. + if (!outTree.isValueOn(outXYZ)) { + outTree.setValueOff(outXYZ, result); + } + } + } + } + } + } else { // affine + // Compute step sizes in the input tree that correspond to + // unit steps in x, y and z in the output tree. + const Vec3R + translation = xform.invTransform(Vec3R(0, 0, 0)), + deltaX = xform.invTransform(Vec3R(1, 0, 0)) - translation, + deltaY = xform.invTransform(Vec3R(0, 1, 0)) - translation, + deltaZ = xform.invTransform(Vec3R(0, 0, 1)) - translation; + +#if defined(__ICC) + /// @todo The following line is a workaround for bad code generation + /// in opt-icc11.1_64 (but not debug or gcc) builds. It should be + /// removed once the problem has been addressed at its source. + const Vec3R dummy = deltaX; +#endif + + // Step by whole voxels through the output tree, sampling the + // corresponding fractional voxels of the input tree. + Vec3R inStartX = xform.invTransform(Vec3R(outMin)); + Coord outXYZ; + int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z(); + for (x = outMin.x(); x <= outMax.x(); ++x, inStartX += deltaX) { + if (interrupt && interrupt()) break; + Vec3R inStartY = inStartX; + for (y = outMin.y(); y <= outMax.y(); ++y, inStartY += deltaY) { + if (interrupt && interrupt()) break; + Vec3R inXYZ = inStartY; + for (z = outMin.z(); z <= outMax.z(); ++z, inXYZ += deltaZ) { + ValueT result; + if (sampler.sample(inTree, inXYZ, result)) { + outTree.setValueOn(outXYZ, result); + } else { + // Note: Don't overwrite existing active values with inactive values. + if (!outTree.isValueOn(outXYZ)) { + outTree.setValueOff(outXYZ, result); + } + } + } + } + } + } +} // GridResampler::transformBBox() + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Interpolation.h b/openvdb/tools/Interpolation.h new file mode 100644 index 00000000..989ab196 --- /dev/null +++ b/openvdb/tools/Interpolation.h @@ -0,0 +1,1017 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file Interpolation.h +/// +/// Sampler classes such as PointSampler and BoxSampler that are intended for use +/// with tools::GridTransformer should operate in voxel space and must adhere to +/// the interface described in the example below: +/// @code +/// struct MySampler +/// { +/// // Return a short name that can be used to identify this sampler +/// // in error messages and elsewhere. +/// const char* name() { return "mysampler"; } +/// +/// // Return the radius of the sampling kernel in voxels, not including +/// // the center voxel. This is the number of voxels of padding that +/// // are added to all sides of a volume as a result of resampling. +/// int radius() { return 2; } +/// +/// // Return true if scaling by a factor smaller than 0.5 (along any axis) +/// // should be handled via a mipmapping-like scheme of successive halvings +/// // of a grid's resolution, until the remaining scale factor is +/// // greater than or equal to 1/2. Set this to false only when high-quality +/// // scaling is not required. +/// bool mipmap() { return true; } +/// +/// // Specify if sampling at a location that is collocated with a grid point +/// // is guaranteed to return the exact value at that grid point. +/// // For most sampling kernels, this should be false. +/// bool consistent() { return false; } +/// +/// // Sample the tree at the given coordinates and return the result in val. +/// // Return true if the sampled value is active. +/// template +/// bool sample(const TreeT& tree, const Vec3R& coord, typename TreeT::ValueType& val); +/// }; +/// @endcode + +#ifndef OPENVDB_TOOLS_INTERPOLATION_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_INTERPOLATION_HAS_BEEN_INCLUDED + +#include // for OPENVDB_VERSION_NAME +#include // for round() +#include // for SmoothUnitStep +#include // for Transform +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Provises a unified interface for sampling, i.e. interpolation. +/// @details Order = 0: closest point +/// Order = 1: tri-linear +/// Order = 2: tri-quadratic +/// Staggered: Set to true for MAC grids +template +struct Sampler +{ + static_assert(Order < 3, "Samplers of order higher than 2 are not supported"); + static const char* name(); + static int radius(); + static bool mipmap(); + static bool consistent(); + static bool staggered(); + static size_t order(); + + /// @brief Sample @a inTree at the floating-point index coordinate @a inCoord + /// and store the result in @a result. + /// + /// @return @c true if the sampled value is active. + template + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Sample @a inTree at the floating-point index coordinate @a inCoord. + /// + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + +//////////////////////////////////////// Non-Staggered Samplers + +// The following samplers operate in voxel space. +// When the samplers are applied to grids holding vector or other non-scalar data, +// the data is assumed to be collocated. For example, using the BoxSampler on a grid +// with ValueType Vec3f assumes that all three elements in a vector can be assigned +// the same physical location. Consider using the GridSampler below instead. + +struct PointSampler +{ + static const char* name() { return "point"; } + static int radius() { return 0; } + static bool mipmap() { return false; } + static bool consistent() { return true; } + static bool staggered() { return false; } + static size_t order() { return 0; } + + /// @brief Sample @a inTree at the nearest neighbor to @a inCoord + /// and store the result in @a result. + /// @return @c true if the sampled value is active. + template + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Sample @a inTree at the nearest neighbor to @a inCoord + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + + +struct BoxSampler +{ + static const char* name() { return "box"; } + static int radius() { return 1; } + static bool mipmap() { return true; } + static bool consistent() { return true; } + static bool staggered() { return false; } + static size_t order() { return 1; } + + /// @brief Trilinearly reconstruct @a inTree at @a inCoord + /// and store the result in @a result. + /// @return @c true if any one of the sampled values is active. + template + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Trilinearly reconstruct @a inTree at @a inCoord. + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); + + /// @brief Import all eight values from @a inTree to support + /// tri-linear interpolation. + template + static inline void getValues(ValueT (&data)[N][N][N], const TreeT& inTree, Coord ijk); + + /// @brief Import all eight values from @a inTree to support + /// tri-linear interpolation. + /// @return @c true if any of the eight values are active + template + static inline bool probeValues(ValueT (&data)[N][N][N], const TreeT& inTree, Coord ijk); + + /// @brief Find the minimum and maximum values of the eight cell + /// values in @ data. + template + static inline void extrema(ValueT (&data)[N][N][N], ValueT& vMin, ValueT& vMax); + + /// @return the tri-linear interpolation with the unit cell coordinates @a uvw + template + static inline ValueT trilinearInterpolation(ValueT (&data)[N][N][N], const Vec3R& uvw); +}; + + +struct QuadraticSampler +{ + static const char* name() { return "quadratic"; } + static int radius() { return 1; } + static bool mipmap() { return true; } + static bool consistent() { return false; } + static bool staggered() { return false; } + static size_t order() { return 2; } + + /// @brief Triquadratically reconstruct @a inTree at @a inCoord + /// and store the result in @a result. + /// @return @c true if any one of the sampled values is active. + template + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Triquadratically reconstruct @a inTree at to @a inCoord. + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); + + template + static inline ValueT triquadraticInterpolation(ValueT (&data)[N][N][N], const Vec3R& uvw); +}; + + +//////////////////////////////////////// Staggered Samplers + + +// The following samplers operate in voxel space and are designed for Vec3 +// staggered grid data (e.g., fluid simulations using the Marker-and-Cell approach +// associate elements of the velocity vector with different physical locations: +// the faces of a cube). + +struct StaggeredPointSampler +{ + static const char* name() { return "point"; } + static int radius() { return 0; } + static bool mipmap() { return false; } + static bool consistent() { return false; } + static bool staggered() { return true; } + static size_t order() { return 0; } + + /// @brief Sample @a inTree at the nearest neighbor to @a inCoord + /// and store the result in @a result. + /// @return true if the sampled value is active. + template + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Sample @a inTree at the nearest neighbor to @a inCoord + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + + +struct StaggeredBoxSampler +{ + static const char* name() { return "box"; } + static int radius() { return 1; } + static bool mipmap() { return true; } + static bool consistent() { return false; } + static bool staggered() { return true; } + static size_t order() { return 1; } + + /// @brief Trilinearly reconstruct @a inTree at @a inCoord + /// and store the result in @a result. + /// @return true if any one of the sampled value is active. + template + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Trilinearly reconstruct @a inTree at @a inCoord. + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + + +struct StaggeredQuadraticSampler +{ + static const char* name() { return "quadratic"; } + static int radius() { return 1; } + static bool mipmap() { return true; } + static bool consistent() { return false; } + static bool staggered() { return true; } + static size_t order() { return 2; } + + /// @brief Triquadratically reconstruct @a inTree at @a inCoord + /// and store the result in @a result. + /// @return true if any one of the sampled values is active. + template + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Triquadratically reconstruct @a inTree at to @a inCoord. + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + + +//////////////////////////////////////// GridSampler + + +/// @brief Class that provides the interface for continuous sampling +/// of values in a tree. +/// +/// @details Since trees support only discrete voxel sampling, TreeSampler +/// must be used to sample arbitrary continuous points in (world or +/// index) space. +/// +/// @warning This implementation of the GridSampler stores a pointer +/// to a Tree for value access. While this is thread-safe it is +/// uncached and hence slow compared to using a +/// ValueAccessor. Consequently it is normally advisable to use the +/// template specialization below that employs a +/// ValueAccessor. However, care must be taken when dealing with +/// multi-threading (see warning below). +template +class GridSampler +{ +public: + using Ptr = SharedPtr; + using ValueType = typename GridOrTreeType::ValueType; + using GridType = typename TreeAdapter::GridType; + using TreeType = typename TreeAdapter::TreeType; + using AccessorType = typename TreeAdapter::AccessorType; + + /// @param grid a grid to be sampled + explicit GridSampler(const GridType& grid) + : mTree(&(grid.tree())), mTransform(&(grid.transform())) {} + + /// @param tree a tree to be sampled, or a ValueAccessor for the tree + /// @param transform is used when sampling world space locations. + GridSampler(const TreeType& tree, const math::Transform& transform) + : mTree(&tree), mTransform(&transform) {} + + const math::Transform& transform() const { return *mTransform; } + + /// @brief Sample a point in index space in the grid. + /// @param x Fractional x-coordinate of point in index-coordinates of grid + /// @param y Fractional y-coordinate of point in index-coordinates of grid + /// @param z Fractional z-coordinate of point in index-coordinates of grid + template + ValueType sampleVoxel(const RealType& x, const RealType& y, const RealType& z) const + { + return this->isSample(Vec3d(x,y,z)); + } + + /// @brief Sample value in integer index space + /// @param i Integer x-coordinate in index space + /// @param j Integer y-coordinate in index space + /// @param k Integer x-coordinate in index space + ValueType sampleVoxel(typename Coord::ValueType i, + typename Coord::ValueType j, + typename Coord::ValueType k) const + { + return this->isSample(Coord(i,j,k)); + } + + /// @brief Sample value in integer index space + /// @param ijk the location in index space + ValueType isSample(const Coord& ijk) const { return mTree->getValue(ijk); } + + /// @brief Sample in fractional index space + /// @param ispoint the location in index space + ValueType isSample(const Vec3d& ispoint) const + { + ValueType result = zeroVal(); + SamplerType::sample(*mTree, ispoint, result); + return result; + } + + /// @brief Sample in world space + /// @param wspoint the location in world space + ValueType wsSample(const Vec3d& wspoint) const + { + ValueType result = zeroVal(); + SamplerType::sample(*mTree, mTransform->worldToIndex(wspoint), result); + return result; + } + +private: + const TreeType* mTree; + const math::Transform* mTransform; +}; // class GridSampler + + +/// @brief Specialization of GridSampler for construction from a ValueAccessor type +/// +/// @note This version should normally be favored over the one above +/// that takes a Grid or Tree. The reason is this version uses a +/// ValueAccessor that performs fast (cached) access where the +/// tree-based flavor performs slower (uncached) access. +/// +/// @warning Since this version stores a pointer to an (externally +/// allocated) value accessor it is not threadsafe. Hence each thread +/// should have its own instance of a GridSampler constructed from a +/// local ValueAccessor. Alternatively the Grid/Tree-based GridSampler +/// is threadsafe, but also slower. +template +class GridSampler, SamplerType> +{ +public: + using Ptr = SharedPtr; + using ValueType = typename TreeT::ValueType; + using TreeType = TreeT; + using GridType = Grid; + using AccessorType = typename tree::ValueAccessor; + + /// @param acc a ValueAccessor to be sampled + /// @param transform is used when sampling world space locations. + GridSampler(const AccessorType& acc, + const math::Transform& transform) + : mAccessor(&acc), mTransform(&transform) {} + + const math::Transform& transform() const { return *mTransform; } + + /// @brief Sample a point in index space in the grid. + /// @param x Fractional x-coordinate of point in index-coordinates of grid + /// @param y Fractional y-coordinate of point in index-coordinates of grid + /// @param z Fractional z-coordinate of point in index-coordinates of grid + template + ValueType sampleVoxel(const RealType& x, const RealType& y, const RealType& z) const + { + return this->isSample(Vec3d(x,y,z)); + } + + /// @brief Sample value in integer index space + /// @param i Integer x-coordinate in index space + /// @param j Integer y-coordinate in index space + /// @param k Integer x-coordinate in index space + ValueType sampleVoxel(typename Coord::ValueType i, + typename Coord::ValueType j, + typename Coord::ValueType k) const + { + return this->isSample(Coord(i,j,k)); + } + + /// @brief Sample value in integer index space + /// @param ijk the location in index space + ValueType isSample(const Coord& ijk) const { return mAccessor->getValue(ijk); } + + /// @brief Sample in fractional index space + /// @param ispoint the location in index space + ValueType isSample(const Vec3d& ispoint) const + { + ValueType result = zeroVal(); + SamplerType::sample(*mAccessor, ispoint, result); + return result; + } + + /// @brief Sample in world space + /// @param wspoint the location in world space + ValueType wsSample(const Vec3d& wspoint) const + { + ValueType result = zeroVal(); + SamplerType::sample(*mAccessor, mTransform->worldToIndex(wspoint), result); + return result; + } + +private: + const AccessorType* mAccessor;//not thread-safe! + const math::Transform* mTransform; +};//Specialization of GridSampler + + +//////////////////////////////////////// DualGridSampler + + +/// @brief This is a simple convenience class that allows for sampling +/// from a source grid into the index space of a target grid. At +/// construction the source and target grids are checked for alignment +/// which potentially renders interpolation unnecessary. Else +/// interpolation is performed according to the templated Sampler +/// type. +/// +/// @warning For performance reasons the check for alignment of the +/// two grids is only performed at construction time! +template +class DualGridSampler +{ +public: + using ValueType = typename GridOrTreeT::ValueType; + using GridType = typename TreeAdapter::GridType; + using TreeType = typename TreeAdapter::TreeType; + using AccessorType = typename TreeAdapter::AccessorType; + + /// @brief Grid and transform constructor. + /// @param sourceGrid Source grid. + /// @param targetXform Transform of the target grid. + DualGridSampler(const GridType& sourceGrid, + const math::Transform& targetXform) + : mSourceTree(&(sourceGrid.tree())) + , mSourceXform(&(sourceGrid.transform())) + , mTargetXform(&targetXform) + , mAligned(targetXform == *mSourceXform) + { + } + /// @brief Tree and transform constructor. + /// @param sourceTree Source tree. + /// @param sourceXform Transform of the source grid. + /// @param targetXform Transform of the target grid. + DualGridSampler(const TreeType& sourceTree, + const math::Transform& sourceXform, + const math::Transform& targetXform) + : mSourceTree(&sourceTree) + , mSourceXform(&sourceXform) + , mTargetXform(&targetXform) + , mAligned(targetXform == sourceXform) + { + } + /// @brief Return the value of the source grid at the index + /// coordinates, ijk, relative to the target grid (or its tranform). + inline ValueType operator()(const Coord& ijk) const + { + if (mAligned) return mSourceTree->getValue(ijk); + const Vec3R world = mTargetXform->indexToWorld(ijk); + return SamplerT::sample(*mSourceTree, mSourceXform->worldToIndex(world)); + } + /// @brief Return true if the two grids are aligned. + inline bool isAligned() const { return mAligned; } +private: + const TreeType* mSourceTree; + const math::Transform* mSourceXform; + const math::Transform* mTargetXform; + const bool mAligned; +};// DualGridSampler + +/// @brief Specialization of DualGridSampler for construction from a ValueAccessor type. +template +class DualGridSampler, SamplerT> +{ + public: + using ValueType = typename TreeT::ValueType; + using TreeType = TreeT; + using GridType = Grid; + using AccessorType = typename tree::ValueAccessor; + + /// @brief ValueAccessor and transform constructor. + /// @param sourceAccessor ValueAccessor into the source grid. + /// @param sourceXform Transform for the source grid. + /// @param targetXform Transform for the target grid. + DualGridSampler(const AccessorType& sourceAccessor, + const math::Transform& sourceXform, + const math::Transform& targetXform) + : mSourceAcc(&sourceAccessor) + , mSourceXform(&sourceXform) + , mTargetXform(&targetXform) + , mAligned(targetXform == sourceXform) + { + } + /// @brief Return the value of the source grid at the index + /// coordinates, ijk, relative to the target grid. + inline ValueType operator()(const Coord& ijk) const + { + if (mAligned) return mSourceAcc->getValue(ijk); + const Vec3R world = mTargetXform->indexToWorld(ijk); + return SamplerT::sample(*mSourceAcc, mSourceXform->worldToIndex(world)); + } + /// @brief Return true if the two grids are aligned. + inline bool isAligned() const { return mAligned; } +private: + const AccessorType* mSourceAcc; + const math::Transform* mSourceXform; + const math::Transform* mTargetXform; + const bool mAligned; +};//Specialization of DualGridSampler + +//////////////////////////////////////// AlphaMask + + +// Class to derive the normalized alpha mask +template +class AlphaMask +{ +public: + static_assert(std::is_floating_point::value, + "AlphaMask requires a floating-point value type"); + using GridType = GridT; + using MaskType = MaskT; + using SamlerType = SamplerT; + using FloatType = FloatT; + + AlphaMask(const GridT& grid, const MaskT& mask, FloatT min, FloatT max, bool invert) + : mAcc(mask.tree()) + , mSampler(mAcc, mask.transform() , grid.transform()) + , mMin(min) + , mInvNorm(1/(max-min)) + , mInvert(invert) + { + assert(min < max); + } + + inline bool operator()(const Coord& xyz, FloatT& a, FloatT& b) const + { + a = math::SmoothUnitStep( (mSampler(xyz) - mMin) * mInvNorm );//smooth mapping to 0->1 + b = 1 - a; + if (mInvert) std::swap(a,b); + return a>0; + } + +protected: + using AccT = typename MaskType::ConstAccessor; + AccT mAcc; + tools::DualGridSampler mSampler; + const FloatT mMin, mInvNorm; + const bool mInvert; +};// AlphaMask + +//////////////////////////////////////// + +namespace local_util { + +inline Vec3i +floorVec3(const Vec3R& v) +{ + return Vec3i(int(std::floor(v(0))), int(std::floor(v(1))), int(std::floor(v(2)))); +} + + +inline Vec3i +ceilVec3(const Vec3R& v) +{ + return Vec3i(int(std::ceil(v(0))), int(std::ceil(v(1))), int(std::ceil(v(2)))); +} + + +inline Vec3i +roundVec3(const Vec3R& v) +{ + return Vec3i(int(::round(v(0))), int(::round(v(1))), int(::round(v(2)))); +} + +} // namespace local_util + + +//////////////////////////////////////// PointSampler + + +template +inline bool +PointSampler::sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result) +{ + return inTree.probeValue(Coord(local_util::roundVec3(inCoord)), result); +} + +template +inline typename TreeT::ValueType +PointSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + return inTree.getValue(Coord(local_util::roundVec3(inCoord))); +} + + +//////////////////////////////////////// BoxSampler + +template +inline void +BoxSampler::getValues(ValueT (&data)[N][N][N], const TreeT& inTree, Coord ijk) +{ + data[0][0][0] = inTree.getValue(ijk); // i, j, k + + ijk[2] += 1; + data[0][0][1] = inTree.getValue(ijk); // i, j, k + 1 + + ijk[1] += 1; + data[0][1][1] = inTree.getValue(ijk); // i, j+1, k + 1 + + ijk[2] -= 1; + data[0][1][0] = inTree.getValue(ijk); // i, j+1, k + + ijk[0] += 1; + ijk[1] -= 1; + data[1][0][0] = inTree.getValue(ijk); // i+1, j, k + + ijk[2] += 1; + data[1][0][1] = inTree.getValue(ijk); // i+1, j, k + 1 + + ijk[1] += 1; + data[1][1][1] = inTree.getValue(ijk); // i+1, j+1, k + 1 + + ijk[2] -= 1; + data[1][1][0] = inTree.getValue(ijk); // i+1, j+1, k +} + +template +inline bool +BoxSampler::probeValues(ValueT (&data)[N][N][N], const TreeT& inTree, Coord ijk) +{ + bool hasActiveValues = false; + hasActiveValues |= inTree.probeValue(ijk, data[0][0][0]); // i, j, k + + ijk[2] += 1; + hasActiveValues |= inTree.probeValue(ijk, data[0][0][1]); // i, j, k + 1 + + ijk[1] += 1; + hasActiveValues |= inTree.probeValue(ijk, data[0][1][1]); // i, j+1, k + 1 + + ijk[2] -= 1; + hasActiveValues |= inTree.probeValue(ijk, data[0][1][0]); // i, j+1, k + + ijk[0] += 1; + ijk[1] -= 1; + hasActiveValues |= inTree.probeValue(ijk, data[1][0][0]); // i+1, j, k + + ijk[2] += 1; + hasActiveValues |= inTree.probeValue(ijk, data[1][0][1]); // i+1, j, k + 1 + + ijk[1] += 1; + hasActiveValues |= inTree.probeValue(ijk, data[1][1][1]); // i+1, j+1, k + 1 + + ijk[2] -= 1; + hasActiveValues |= inTree.probeValue(ijk, data[1][1][0]); // i+1, j+1, k + + return hasActiveValues; +} + +template +inline void +BoxSampler::extrema(ValueT (&data)[N][N][N], ValueT& vMin, ValueT &vMax) +{ + vMin = vMax = data[0][0][0]; + vMin = math::Min(vMin, data[0][0][1]); + vMax = math::Max(vMax, data[0][0][1]); + vMin = math::Min(vMin, data[0][1][0]); + vMax = math::Max(vMax, data[0][1][0]); + vMin = math::Min(vMin, data[0][1][1]); + vMax = math::Max(vMax, data[0][1][1]); + vMin = math::Min(vMin, data[1][0][0]); + vMax = math::Max(vMax, data[1][0][0]); + vMin = math::Min(vMin, data[1][0][1]); + vMax = math::Max(vMax, data[1][0][1]); + vMin = math::Min(vMin, data[1][1][0]); + vMax = math::Max(vMax, data[1][1][0]); + vMin = math::Min(vMin, data[1][1][1]); + vMax = math::Max(vMax, data[1][1][1]); +} + + +template +inline ValueT +BoxSampler::trilinearInterpolation(ValueT (&data)[N][N][N], const Vec3R& uvw) +{ + auto _interpolate = [](const ValueT& a, const ValueT& b, double weight) + { + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto temp = (b - a) * weight; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return static_cast(a + ValueT(temp)); + }; + + // Trilinear interpolation: + // The eight surrounding latice values are used to construct the result. \n + // result(x,y,z) = + // v000 (1-x)(1-y)(1-z) + v001 (1-x)(1-y)z + v010 (1-x)y(1-z) + v011 (1-x)yz + // + v100 x(1-y)(1-z) + v101 x(1-y)z + v110 xy(1-z) + v111 xyz + + return _interpolate( + _interpolate( + _interpolate(data[0][0][0], data[0][0][1], uvw[2]), + _interpolate(data[0][1][0], data[0][1][1], uvw[2]), + uvw[1]), + _interpolate( + _interpolate(data[1][0][0], data[1][0][1], uvw[2]), + _interpolate(data[1][1][0], data[1][1][1], uvw[2]), + uvw[1]), + uvw[0]); +} + + +template +inline bool +BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result) +{ + using ValueT = typename TreeT::ValueType; + + const Vec3i inIdx = local_util::floorVec3(inCoord); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the eight voxels surrounding the + // fractional source coordinates. + ValueT data[2][2][2]; + + const bool hasActiveValues = BoxSampler::probeValues(data, inTree, Coord(inIdx)); + + result = BoxSampler::trilinearInterpolation(data, uvw); + + return hasActiveValues; +} + + +template +inline typename TreeT::ValueType +BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + using ValueT = typename TreeT::ValueType; + + const Vec3i inIdx = local_util::floorVec3(inCoord); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the eight voxels surrounding the + // fractional source coordinates. + ValueT data[2][2][2]; + + BoxSampler::getValues(data, inTree, Coord(inIdx)); + + return BoxSampler::trilinearInterpolation(data, uvw); +} + + +//////////////////////////////////////// QuadraticSampler + +template +inline ValueT +QuadraticSampler::triquadraticInterpolation(ValueT (&data)[N][N][N], const Vec3R& uvw) +{ + auto _interpolate = [](const ValueT* value, double weight) + { + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const ValueT + a = static_cast(0.5 * (value[0] + value[2]) - value[1]), + b = static_cast(0.5 * (value[2] - value[0])), + c = static_cast(value[1]); + const auto temp = weight * (weight * a + b) + c; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return static_cast(temp); + }; + + /// @todo For vector types, interpolate over each component independently. + ValueT vx[3]; + for (int dx = 0; dx < 3; ++dx) { + ValueT vy[3]; + for (int dy = 0; dy < 3; ++dy) { + // Fit a parabola to three contiguous samples in z + // (at z=-1, z=0 and z=1), then evaluate the parabola at z', + // where z' is the fractional part of inCoord.z, i.e., + // inCoord.z - inIdx.z. The coefficients come from solving + // + // | (-1)^2 -1 1 || a | | v0 | + // | 0 0 1 || b | = | v1 | + // | 1^2 1 1 || c | | v2 | + // + // for a, b and c. + const ValueT* vz = &data[dx][dy][0]; + vy[dy] = _interpolate(vz, uvw.z()); + }//loop over y + // Fit a parabola to three interpolated samples in y, then + // evaluate the parabola at y', where y' is the fractional + // part of inCoord.y. + vx[dx] = _interpolate(vy, uvw.y()); + }//loop over x + // Fit a parabola to three interpolated samples in x, then + // evaluate the parabola at the fractional part of inCoord.x. + return _interpolate(vx, uvw.x()); +} + +template +inline bool +QuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result) +{ + using ValueT = typename TreeT::ValueType; + + const Vec3i inIdx = local_util::floorVec3(inCoord), inLoIdx = inIdx - Vec3i(1, 1, 1); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the 27 voxels surrounding the + // fractional source coordinates. + bool active = false; + ValueT data[3][3][3]; + for (int dx = 0, ix = inLoIdx.x(); dx < 3; ++dx, ++ix) { + for (int dy = 0, iy = inLoIdx.y(); dy < 3; ++dy, ++iy) { + for (int dz = 0, iz = inLoIdx.z(); dz < 3; ++dz, ++iz) { + if (inTree.probeValue(Coord(ix, iy, iz), data[dx][dy][dz])) active = true; + } + } + } + + result = QuadraticSampler::triquadraticInterpolation(data, uvw); + + return active; +} + +template +inline typename TreeT::ValueType +QuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + using ValueT = typename TreeT::ValueType; + + const Vec3i inIdx = local_util::floorVec3(inCoord), inLoIdx = inIdx - Vec3i(1, 1, 1); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the 27 voxels surrounding the + // fractional source coordinates. + ValueT data[3][3][3]; + for (int dx = 0, ix = inLoIdx.x(); dx < 3; ++dx, ++ix) { + for (int dy = 0, iy = inLoIdx.y(); dy < 3; ++dy, ++iy) { + for (int dz = 0, iz = inLoIdx.z(); dz < 3; ++dz, ++iz) { + data[dx][dy][dz] = inTree.getValue(Coord(ix, iy, iz)); + } + } + } + + return QuadraticSampler::triquadraticInterpolation(data, uvw); +} + + +//////////////////////////////////////// StaggeredPointSampler + + +template +inline bool +StaggeredPointSampler::sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result) +{ + using ValueType = typename TreeT::ValueType; + + ValueType tempX, tempY, tempZ; + bool active = false; + + active = PointSampler::sample(inTree, inCoord + Vec3R(0.5, 0, 0), tempX) || active; + active = PointSampler::sample(inTree, inCoord + Vec3R(0, 0.5, 0), tempY) || active; + active = PointSampler::sample(inTree, inCoord + Vec3R(0, 0, 0.5), tempZ) || active; + + result.x() = tempX.x(); + result.y() = tempY.y(); + result.z() = tempZ.z(); + + return active; +} + +template +inline typename TreeT::ValueType +StaggeredPointSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + using ValueT = typename TreeT::ValueType; + + const ValueT tempX = PointSampler::sample(inTree, inCoord + Vec3R(0.5, 0.0, 0.0)); + const ValueT tempY = PointSampler::sample(inTree, inCoord + Vec3R(0.0, 0.5, 0.0)); + const ValueT tempZ = PointSampler::sample(inTree, inCoord + Vec3R(0.0, 0.0, 0.5)); + + return ValueT(tempX.x(), tempY.y(), tempZ.z()); +} + + +//////////////////////////////////////// StaggeredBoxSampler + + +template +inline bool +StaggeredBoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result) +{ + using ValueType = typename TreeT::ValueType; + + ValueType tempX, tempY, tempZ; + tempX = tempY = tempZ = zeroVal(); + bool active = false; + + active = BoxSampler::sample(inTree, inCoord + Vec3R(0.5, 0, 0), tempX) || active; + active = BoxSampler::sample(inTree, inCoord + Vec3R(0, 0.5, 0), tempY) || active; + active = BoxSampler::sample(inTree, inCoord + Vec3R(0, 0, 0.5), tempZ) || active; + + result.x() = tempX.x(); + result.y() = tempY.y(); + result.z() = tempZ.z(); + + return active; +} + +template +inline typename TreeT::ValueType +StaggeredBoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + using ValueT = typename TreeT::ValueType; + + const ValueT tempX = BoxSampler::sample(inTree, inCoord + Vec3R(0.5, 0.0, 0.0)); + const ValueT tempY = BoxSampler::sample(inTree, inCoord + Vec3R(0.0, 0.5, 0.0)); + const ValueT tempZ = BoxSampler::sample(inTree, inCoord + Vec3R(0.0, 0.0, 0.5)); + + return ValueT(tempX.x(), tempY.y(), tempZ.z()); +} + + +//////////////////////////////////////// StaggeredQuadraticSampler + + +template +inline bool +StaggeredQuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result) +{ + using ValueType = typename TreeT::ValueType; + + ValueType tempX, tempY, tempZ; + bool active = false; + + active = QuadraticSampler::sample(inTree, inCoord + Vec3R(0.5, 0, 0), tempX) || active; + active = QuadraticSampler::sample(inTree, inCoord + Vec3R(0, 0.5, 0), tempY) || active; + active = QuadraticSampler::sample(inTree, inCoord + Vec3R(0, 0, 0.5), tempZ) || active; + + result.x() = tempX.x(); + result.y() = tempY.y(); + result.z() = tempZ.z(); + + return active; +} + +template +inline typename TreeT::ValueType +StaggeredQuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + using ValueT = typename TreeT::ValueType; + + const ValueT tempX = QuadraticSampler::sample(inTree, inCoord + Vec3R(0.5, 0.0, 0.0)); + const ValueT tempY = QuadraticSampler::sample(inTree, inCoord + Vec3R(0.0, 0.5, 0.0)); + const ValueT tempZ = QuadraticSampler::sample(inTree, inCoord + Vec3R(0.0, 0.0, 0.5)); + + return ValueT(tempX.x(), tempY.y(), tempZ.z()); +} + +//////////////////////////////////////// Sampler + +template <> +struct Sampler<0, false> : public PointSampler {}; + +template <> +struct Sampler<1, false> : public BoxSampler {}; + +template <> +struct Sampler<2, false> : public QuadraticSampler {}; + +template <> +struct Sampler<0, true> : public StaggeredPointSampler {}; + +template <> +struct Sampler<1, true> : public StaggeredBoxSampler {}; + +template <> +struct Sampler<2, true> : public StaggeredQuadraticSampler {}; + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_INTERPOLATION_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetAdvect.h b/openvdb/tools/LevelSetAdvect.h new file mode 100644 index 00000000..2214560b --- /dev/null +++ b/openvdb/tools/LevelSetAdvect.h @@ -0,0 +1,575 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Ken Museth +/// +/// @file tools/LevelSetAdvect.h +/// +/// @brief Hyperbolic advection of narrow-band level sets + +#ifndef OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED + +#include +#include +#include +#include "LevelSetTracker.h" +#include "VelocityFields.h" // for EnrightField +#include +//#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Hyperbolic advection of narrow-band level sets in an +/// external velocity field +/// +/// The @c FieldType template argument below refers to any functor +/// with the following interface (see tools/VelocityFields.h +/// for examples): +/// +/// @code +/// class VelocityField { +/// ... +/// public: +/// openvdb::VectorType operator() (const openvdb::Coord& xyz, ValueType time) const; +/// ... +/// }; +/// @endcode +/// +/// @note The functor method returns the velocity field at coordinate +/// position xyz of the advection grid, and for the specified +/// time. Note that since the velocity is returned in the local +/// coordinate space of the grid that is being advected, the functor +/// typically depends on the transformation of that grid. This design +/// is chosen for performance reasons. Finally we will assume that the +/// functor method is NOT threadsafe (typically uses a ValueAccessor) +/// and that its lightweight enough that we can copy it per thread. +/// +/// The @c InterruptType template argument below refers to any class +/// with the following interface: +/// @code +/// class Interrupter { +/// ... +/// public: +/// void start(const char* name = nullptr) // called when computations begin +/// void end() // called when computations end +/// bool wasInterrupted(int percent=-1) // return true to break computation +///}; +/// @endcode +/// +/// @note If no template argument is provided for this InterruptType +/// the util::NullInterrupter is used which implies that all +/// interrupter calls are no-ops (i.e. incurs no computational overhead). +/// + +template, + typename InterruptT = util::NullInterrupter> +class LevelSetAdvection +{ +public: + using GridType = GridT; + using TrackerT = LevelSetTracker; + using LeafRange = typename TrackerT::LeafRange; + using LeafType = typename TrackerT::LeafType; + using BufferType = typename TrackerT::BufferType; + using ValueType = typename TrackerT::ValueType; + using VectorType = typename FieldT::VectorType; + + /// Main constructor + LevelSetAdvection(GridT& grid, const FieldT& field, InterruptT* interrupt = nullptr): + mTracker(grid, interrupt), mField(field), + mSpatialScheme(math::HJWENO5_BIAS), + mTemporalScheme(math::TVD_RK2) {} + + virtual ~LevelSetAdvection() {} + + /// @brief Return the spatial finite difference scheme + math::BiasedGradientScheme getSpatialScheme() const { return mSpatialScheme; } + /// @brief Set the spatial finite difference scheme + void setSpatialScheme(math::BiasedGradientScheme scheme) { mSpatialScheme = scheme; } + + /// @brief Return the temporal integration scheme + math::TemporalIntegrationScheme getTemporalScheme() const { return mTemporalScheme; } + /// @brief Set the spatial finite difference scheme + void setTemporalScheme(math::TemporalIntegrationScheme scheme) { mTemporalScheme = scheme; } + + /// @brief Return the spatial finite difference scheme + math::BiasedGradientScheme getTrackerSpatialScheme() const { + return mTracker.getSpatialScheme(); + } + /// @brief Set the spatial finite difference scheme + void setTrackerSpatialScheme(math::BiasedGradientScheme scheme) { + mTracker.setSpatialScheme(scheme); + } + /// @brief Return the temporal integration scheme + math::TemporalIntegrationScheme getTrackerTemporalScheme() const { + return mTracker.getTemporalScheme(); + } + /// @brief Set the spatial finite difference scheme + void setTrackerTemporalScheme(math::TemporalIntegrationScheme scheme) { + mTracker.setTemporalScheme(scheme); + } + + /// @brief Return The number of normalizations performed per track or + /// normalize call. + int getNormCount() const { return mTracker.getNormCount(); } + /// @brief Set the number of normalizations performed per track or + /// normalize call. + void setNormCount(int n) { mTracker.setNormCount(n); } + + /// @brief Return the grain-size used for multi-threading + int getGrainSize() const { return mTracker.getGrainSize(); } + /// @brief Set the grain-size used for multi-threading. + /// @note A grain size of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mTracker.setGrainSize(grainsize); } + + /// Advect the level set from its current time, time0, to its + /// final time, time1. If time0>time1 backward advection is performed. + /// + /// @return number of CFL iterations used to advect from time0 to time1 + size_t advect(ValueType time0, ValueType time1); + +private: + // disallow copy construction and copy by assinment! + LevelSetAdvection(const LevelSetAdvection&);// not implemented + LevelSetAdvection& operator=(const LevelSetAdvection&);// not implemented + + // This templated private struct implements all the level set magic. + template + struct Advect + { + /// Main constructor + Advect(LevelSetAdvection& parent); + /// Shallow copy constructor called by tbb::parallel_for() threads + Advect(const Advect& other); + /// Destructor + virtual ~Advect() { if (mIsMaster) this->clearField(); } + /// Advect the level set from its current time, time0, to its final time, time1. + /// @return number of CFL iterations + size_t advect(ValueType time0, ValueType time1); + /// Used internally by tbb::parallel_for() + void operator()(const LeafRange& r) const + { + if (mTask) mTask(const_cast(this), r); + else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); + } + /// method calling tbb + void cook(const char* msg, size_t swapBuffer = 0); + /// Sample field and return the CFL time step + typename GridT::ValueType sampleField(ValueType time0, ValueType time1); + template void sample(const LeafRange& r, ValueType t0, ValueType t1); + inline void sampleXformed(const LeafRange& r, ValueType t0, ValueType t1) + { + this->sample(r, t0, t1); + } + inline void sampleAligned(const LeafRange& r, ValueType t0, ValueType t1) + { + this->sample(r, t0, t1); + } + void clearField(); + // Convex combination of Phi and a forward Euler advection steps: + // Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|); + template + void euler(const LeafRange&, ValueType, Index, Index); + inline void euler01(const LeafRange& r, ValueType t) {this->euler<0,1>(r, t, 0, 1);} + inline void euler12(const LeafRange& r, ValueType t) {this->euler<1,2>(r, t, 1, 1);} + inline void euler34(const LeafRange& r, ValueType t) {this->euler<3,4>(r, t, 1, 2);} + inline void euler13(const LeafRange& r, ValueType t) {this->euler<1,3>(r, t, 1, 2);} + + LevelSetAdvection& mParent; + VectorType* mVelocity; + size_t* mOffsets; + const MapT* mMap; + typename std::function mTask; + const bool mIsMaster; + }; // end of private Advect struct + + template + size_t advect1(ValueType time0, ValueType time1); + + template + size_t advect2(ValueType time0, ValueType time1); + + template + size_t advect3(ValueType time0, ValueType time1); + + TrackerT mTracker; + //each thread needs a deep copy of the field since it might contain a ValueAccessor + const FieldT mField; + math::BiasedGradientScheme mSpatialScheme; + math::TemporalIntegrationScheme mTemporalScheme; + +};//end of LevelSetAdvection + + +template +inline size_t +LevelSetAdvection::advect(ValueType time0, ValueType time1) +{ + switch (mSpatialScheme) { + case math::FIRST_BIAS: + return this->advect1(time0, time1); + case math::SECOND_BIAS: + return this->advect1(time0, time1); + case math::THIRD_BIAS: + return this->advect1(time0, time1); + case math::WENO5_BIAS: + return this->advect1(time0, time1); + case math::HJWENO5_BIAS: + return this->advect1(time0, time1); + default: + OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); + } + return 0; +} + + +template +template +inline size_t +LevelSetAdvection::advect1(ValueType time0, ValueType time1) +{ + switch (mTemporalScheme) { + case math::TVD_RK1: + return this->advect2(time0, time1); + case math::TVD_RK2: + return this->advect2(time0, time1); + case math::TVD_RK3: + return this->advect2(time0, time1); + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + } + return 0; +} + + +template +template +inline size_t +LevelSetAdvection::advect2(ValueType time0, ValueType time1) +{ + const math::Transform& trans = mTracker.grid().transform(); + if (trans.mapType() == math::UniformScaleMap::mapType()) { + return this->advect3(time0, time1); + } else if (trans.mapType() == math::UniformScaleTranslateMap::mapType()) { + return this->advect3( + time0, time1); + } else if (trans.mapType() == math::UnitaryMap::mapType()) { + return this->advect3(time0, time1); + } else if (trans.mapType() == math::TranslationMap::mapType()) { + return this->advect3(time0, time1); + } else { + OPENVDB_THROW(ValueError, "MapType not supported!"); + } + return 0; +} + + +template +template< + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme, + typename MapT> +inline size_t +LevelSetAdvection::advect3(ValueType time0, ValueType time1) +{ + Advect tmp(*this); + return tmp.advect(time0, time1); +} + + +/////////////////////////////////////////////////////////////////////// + + +template +template< + typename MapT, + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline +LevelSetAdvection:: +Advect:: +Advect(LevelSetAdvection& parent) + : mParent(parent) + , mVelocity(nullptr) + , mOffsets(nullptr) + , mMap(parent.mTracker.grid().transform().template constMap().get()) + , mTask(0) + , mIsMaster(true) +{ +} + + +template +template< + typename MapT, + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline +LevelSetAdvection:: +Advect:: +Advect(const Advect& other) + : mParent(other.mParent) + , mVelocity(other.mVelocity) + , mOffsets(other.mOffsets) + , mMap(other.mMap) + , mTask(other.mTask) + , mIsMaster(false) +{ +} + + +template +template< + typename MapT, + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline size_t +LevelSetAdvection:: +Advect:: +advect(ValueType time0, ValueType time1) +{ + namespace ph = std::placeholders; + + //util::CpuTimer timer; + size_t countCFL = 0; + if ( math::isZero(time0 - time1) ) return countCFL; + const bool isForward = time0 < time1; + while ((isForward ? time0time1) && mParent.mTracker.checkInterrupter()) { + /// Make sure we have enough temporal auxiliary buffers + //timer.start( "\nallocate buffers" ); + mParent.mTracker.leafs().rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1); + //timer.stop(); + + const ValueType dt = this->sampleField(time0, time1); + if ( math::isZero(dt) ) break;//V is essentially zero so terminate + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time + switch(TemporalScheme) { + case math::TVD_RK1: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0) + mTask = std::bind(&Advect::euler01, ph::_1, ph::_2, dt); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Advecting level set using TVD_RK1", 1); + break; + case math::TVD_RK2: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0) + mTask = std::bind(&Advect::euler01, ph::_1, ph::_2, dt); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Advecting level set using TVD_RK1 (step 1 of 2)", 1); + + // Convex combine explict Euler step: t2 = t0 + dt + // Phi_t2(1) = 1/2 * Phi_t0(1) + 1/2 * (Phi_t1(0) - dt * V.Grad_t1(0)) + mTask = std::bind(&Advect::euler12, ph::_1, ph::_2, dt); + + // Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1) + this->cook("Advecting level set using TVD_RK1 (step 2 of 2)", 1); + break; + case math::TVD_RK3: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0) + mTask = std::bind(&Advect::euler01, ph::_1, ph::_2, dt); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Advecting level set using TVD_RK3 (step 1 of 3)", 1); + + // Convex combine explict Euler step: t2 = t0 + dt/2 + // Phi_t2(2) = 3/4 * Phi_t0(1) + 1/4 * (Phi_t1(0) - dt * V.Grad_t1(0)) + mTask = std::bind(&Advect::euler34, ph::_1, ph::_2, dt); + + // Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2) + this->cook("Advecting level set using TVD_RK3 (step 2 of 3)", 2); + + // Convex combine explict Euler step: t3 = t0 + dt + // Phi_t3(2) = 1/3 * Phi_t0(1) + 2/3 * (Phi_t2(0) - dt * V.Grad_t2(0) + mTask = std::bind(&Advect::euler13, ph::_1, ph::_2, dt); + + // Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2) + this->cook("Advecting level set using TVD_RK3 (step 3 of 3)", 2); + break; + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + }//end of compile-time resolved switch + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + + time0 += isForward ? dt : -dt; + ++countCFL; + mParent.mTracker.leafs().removeAuxBuffers(); + this->clearField(); + /// Track the narrow band + mParent.mTracker.track(); + }//end wile-loop over time + return countCFL;//number of CLF propagation steps +} + + +template +template< + typename MapT, + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline typename GridT::ValueType +LevelSetAdvection:: +Advect:: +sampleField(ValueType time0, ValueType time1) +{ + namespace ph = std::placeholders; + + const int grainSize = mParent.mTracker.getGrainSize(); + const size_t leafCount = mParent.mTracker.leafs().leafCount(); + if (leafCount==0) return ValueType(0.0); + + // Compute the prefix sum of offsets to active voxels + size_t size=0, voxelCount=mParent.mTracker.leafs().getPrefixSum(mOffsets, size, grainSize); + + // Sample the velocity field + if (mParent.mField.transform() == mParent.mTracker.grid().transform()) { + mTask = std::bind(&Advect::sampleAligned, ph::_1, ph::_2, time0, time1); + } else { + mTask = std::bind(&Advect::sampleXformed, ph::_1, ph::_2, time0, time1); + } + assert(voxelCount == mParent.mTracker.grid().activeVoxelCount()); + mVelocity = new VectorType[ voxelCount ]; + this->cook("Sampling advection field"); + + // Find the extrema of the magnitude of the velocities + ValueType maxAbsV = 0; + VectorType* v = mVelocity; + for (size_t i = 0; i < voxelCount; ++i, ++v) { + maxAbsV = math::Max(maxAbsV, ValueType(v->lengthSqr())); + } + + // Compute the CFL number + if (math::isApproxZero(maxAbsV, math::Delta::value())) return ValueType(0); + static const ValueType CFL = (TemporalScheme == math::TVD_RK1 ? ValueType(0.3) : + TemporalScheme == math::TVD_RK2 ? ValueType(0.9) : + ValueType(1.0))/math::Sqrt(ValueType(3.0)); + const ValueType dt = math::Abs(time1 - time0), dx = mParent.mTracker.voxelSize(); + return math::Min(dt, ValueType(CFL*dx/math::Sqrt(maxAbsV))); +} + + +template +template< + typename MapT, + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +template +inline void +LevelSetAdvection:: +Advect:: +sample(const LeafRange& range, ValueType time0, ValueType time1) +{ + const bool isForward = time0 < time1; + using VoxelIterT = typename LeafType::ValueOnCIter; + const MapT& map = *mMap; + const FieldT field( mParent.mField ); + mParent.mTracker.checkInterrupter(); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + VectorType* vel = mVelocity + mOffsets[ leafIter.pos() ]; + for (VoxelIterT iter = leafIter->cbeginValueOn(); iter; ++iter, ++vel) { + const VectorType v = Aligned ? field(iter.getCoord(), time0) ://resolved at compile time + field(map.applyMap(iter.getCoord().asVec3d()), time0); + *vel = isForward ? v : -v; + } + } +} + + +template +template< + typename MapT, + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline void +LevelSetAdvection:: +Advect:: +clearField() +{ + delete [] mOffsets; + delete [] mVelocity; + mOffsets = nullptr; + mVelocity = nullptr; +} + + +template +template< + typename MapT, + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +inline void +LevelSetAdvection:: +Advect:: +cook(const char* msg, size_t swapBuffer) +{ + mParent.mTracker.startInterrupter( msg ); + + const int grainSize = mParent.mTracker.getGrainSize(); + const LeafRange range = mParent.mTracker.leafs().leafRange(grainSize); + + grainSize == 0 ? (*this)(range) : tbb::parallel_for(range, *this); + + mParent.mTracker.leafs().swapLeafBuffer(swapBuffer, grainSize == 0); + + mParent.mTracker.endInterrupter(); +} + + +// Convex combination of Phi and a forward Euler advection steps: +// Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * V.Grad(0)); +template +template< + typename MapT, + math::BiasedGradientScheme SpatialScheme, + math::TemporalIntegrationScheme TemporalScheme> +template +inline void +LevelSetAdvection:: +Advect:: +euler(const LeafRange& range, ValueType dt, Index phiBuffer, Index resultBuffer) +{ + using SchemeT = math::BIAS_SCHEME; + using StencilT = typename SchemeT::template ISStencil::StencilType; + using VoxelIterT = typename LeafType::ValueOnCIter; + using GradT = math::GradientBiased; + + static const ValueType Alpha = ValueType(Nominator)/ValueType(Denominator); + static const ValueType Beta = ValueType(1) - Alpha; + + mParent.mTracker.checkInterrupter(); + const MapT& map = *mMap; + StencilT stencil(mParent.mTracker.grid()); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + const VectorType* vel = mVelocity + mOffsets[ leafIter.pos() ]; + const ValueType* phi = leafIter.buffer(phiBuffer).data(); + ValueType* result = leafIter.buffer(resultBuffer).data(); + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter, ++vel) { + const Index i = voxelIter.pos(); + stencil.moveTo(voxelIter); + const ValueType a = + stencil.getValue() - dt * vel->dot(GradT::result(map, stencil, *vel)); + result[i] = Nominator ? Alpha * phi[i] + Beta * a : a; + }//loop over active voxels in the leaf of the mask + }//loop over leafs of the level set +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetFilter.h b/openvdb/tools/LevelSetFilter.h new file mode 100644 index 00000000..71ce06e2 --- /dev/null +++ b/openvdb/tools/LevelSetFilter.h @@ -0,0 +1,510 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @author Ken Museth +/// +/// @file tools/LevelSetFilter.h +/// +/// @brief Performs various types of level set deformations with +/// interface tracking. These unrestricted deformations include +/// surface smoothing (e.g., Laplacian flow), filtering (e.g., mean +/// value) and morphological operations (e.g., morphological opening). +/// All these operations can optionally be masked with another grid that +/// acts as an alpha-mask. + +#ifndef OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED + +#include "LevelSetTracker.h" +#include "Interpolation.h" +#include // for std::max() +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Filtering (e.g. diffusion) of narrow-band level sets. An +/// optional scalar field can be used to produce a (smooth) alpha mask +/// for the filtering. +/// +/// @note This class performs proper interface tracking which allows +/// for unrestricted surface deformations +template::Type, + typename InterruptT = util::NullInterrupter> +class LevelSetFilter : public LevelSetTracker +{ +public: + using BaseType = LevelSetTracker; + using GridType = GridT; + using MaskType = MaskT; + using TreeType = typename GridType::TreeType; + using ValueType = typename TreeType::ValueType; + using AlphaType = typename MaskType::ValueType; + static_assert(std::is_floating_point::value, + "LevelSetFilter requires a mask grid with floating-point values"); + + /// @brief Main constructor from a grid + /// @param grid The level set to be filtered. + /// @param interrupt Optional interrupter. + LevelSetFilter(GridType& grid, InterruptT* interrupt = nullptr) + : BaseType(grid, interrupt) + , mMinMask(0) + , mMaxMask(1) + , mInvertMask(false) + { + } + /// @brief Default destructor + ~LevelSetFilter() override {} + + /// @brief Return the minimum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType minMask() const { return mMinMask; } + /// @brief Return the maximum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType maxMask() const { return mMaxMask; } + /// @brief Define the range for the (optional) scalar mask. + /// @param min Minimum value of the range. + /// @param max Maximum value of the range. + /// @details Mask values outside the range maps to alpha values of + /// respectfully zero and one, and values inside the range maps + /// smoothly to 0->1 (unless of course the mask is inverted). + /// @throw ValueError if @a min is not smaller than @a max. + void setMaskRange(AlphaType min, AlphaType max) + { + if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)"); + mMinMask = min; + mMaxMask = max; + } + + /// @brief Return true if the mask is inverted, i.e. min->max in the + /// original mask maps to 1->0 in the inverted alpha mask. + bool isMaskInverted() const { return mInvertMask; } + /// @brief Invert the optional mask, i.e. min->max in the original + /// mask maps to 1->0 in the inverted alpha mask. + void invertMask(bool invert=true) { mInvertMask = invert; } + + /// @brief One iteration of mean-curvature flow of the level set. + /// @param mask Optional alpha mask. + void meanCurvature(const MaskType* mask = nullptr) + { + Filter f(this, mask); f.meanCurvature(); + } + + /// @brief One iteration of Laplacian flow of the level set. + /// @param mask Optional alpha mask. + void laplacian(const MaskType* mask = nullptr) + { + Filter f(this, mask); f.laplacian(); + } + + /// @brief One iteration of a fast separable Gaussian filter. + /// @param width Width of the Gaussian kernel in voxel units. + /// @param mask Optional alpha mask. + /// + /// @note This is approximated as 4 iterations of a separable mean filter + /// which typically leads an approximation that's better than 95%! + void gaussian(int width = 1, const MaskType* mask = nullptr) + { + Filter f(this, mask); f.gaussian(width); + } + + /// @brief Offset the level set by the specified (world) distance. + /// @param offset Value of the offset. + /// @param mask Optional alpha mask. + void offset(ValueType offset, const MaskType* mask = nullptr) + { + Filter f(this, mask); f.offset(offset); + } + + /// @brief One iteration of median-value flow of the level set. + /// @param width Width of the median-value kernel in voxel units. + /// @param mask Optional alpha mask. + /// + /// @warning This filter is not separable and is hence relatively + /// slow! + void median(int width = 1, const MaskType* mask = nullptr) + { + Filter f(this, mask); f.median(width); + } + + /// @brief One iteration of mean-value flow of the level set. + /// @param width Width of the mean-value kernel in voxel units. + /// @param mask Optional alpha mask. + /// + /// @note This filter is separable so it's fast! + void mean(int width = 1, const MaskType* mask = nullptr) + { + Filter f(this, mask); f.mean(width); + } + +private: + // disallow copy construction and copy by assignment! + LevelSetFilter(const LevelSetFilter&);// not implemented + LevelSetFilter& operator=(const LevelSetFilter&);// not implemented + + // Private struct that implements all the filtering. + struct Filter + { + using LeafT = typename TreeType::LeafNodeType; + using VoxelIterT = typename LeafT::ValueOnIter; + using VoxelCIterT = typename LeafT::ValueOnCIter; + using BufferT = typename tree::LeafManager::BufferType; + using LeafRange = typename tree::LeafManager::LeafRange; + using LeafIterT = typename LeafRange::Iterator; + using AlphaMaskT = tools::AlphaMask; + + Filter(LevelSetFilter* parent, const MaskType* mask) : mParent(parent), mMask(mask) {} + Filter(const Filter&) = default; + virtual ~Filter() {} + + void box(int width); + void median(int width); + void mean(int width); + void gaussian(int width); + void laplacian(); + void meanCurvature(); + void offset(ValueType value); + void operator()(const LeafRange& r) const + { + if (mTask) mTask(const_cast(this), r); + else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); + } + void cook(bool swap) + { + const int n = mParent->getGrainSize(); + if (n>0) { + tbb::parallel_for(mParent->leafs().leafRange(n), *this); + } else { + (*this)(mParent->leafs().leafRange()); + } + if (swap) mParent->leafs().swapLeafBuffer(1, n==0); + } + + template + struct Avg { + Avg(const GridT& grid, Int32 w) : + acc(grid.tree()), width(w), frac(1/ValueType(2*w+1)) {} + inline ValueType operator()(Coord xyz) + { + ValueType sum = zeroVal(); + Int32& i = xyz[Axis], j = i + width; + for (i -= width; i <= j; ++i) sum += acc.getValue(xyz); + return sum*frac; + } + typename GridT::ConstAccessor acc; + const Int32 width; + const ValueType frac; + }; + + template + void boxImpl(const LeafRange& r, Int32 w); + + void boxXImpl(const LeafRange& r, Int32 w) { this->boxImpl >(r,w); } + void boxZImpl(const LeafRange& r, Int32 w) { this->boxImpl >(r,w); } + void boxYImpl(const LeafRange& r, Int32 w) { this->boxImpl >(r,w); } + + void medianImpl(const LeafRange&, int); + void meanCurvatureImpl(const LeafRange&); + void laplacianImpl(const LeafRange&); + void offsetImpl(const LeafRange&, ValueType); + + LevelSetFilter* mParent; + const MaskType* mMask; + typename std::function mTask; + }; // end of private Filter struct + + AlphaType mMinMask, mMaxMask; + bool mInvertMask; + +}; // end of LevelSetFilter class + + +//////////////////////////////////////// + +template +inline void +LevelSetFilter::Filter::median(int width) +{ + mParent->startInterrupter("Median-value flow of level set"); + + mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0); + + mTask = std::bind(&Filter::medianImpl, + std::placeholders::_1, std::placeholders::_2, std::max(1, width)); + this->cook(true); + + mParent->track(); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter::Filter::mean(int width) +{ + mParent->startInterrupter("Mean-value flow of level set"); + + this->box(width); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter::Filter::gaussian(int width) +{ + mParent->startInterrupter("Gaussian flow of level set"); + + for (int n=0; n<4; ++n) this->box(width); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter::Filter::box(int width) +{ + mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0); + + width = std::max(1, width); + + mTask = std::bind(&Filter::boxXImpl, std::placeholders::_1, std::placeholders::_2, width); + this->cook(true); + + mTask = std::bind(&Filter::boxYImpl, std::placeholders::_1, std::placeholders::_2, width); + this->cook(true); + + mTask = std::bind(&Filter::boxZImpl, std::placeholders::_1, std::placeholders::_2, width); + this->cook(true); + + mParent->track(); +} + +template +inline void +LevelSetFilter::Filter::meanCurvature() +{ + mParent->startInterrupter("Mean-curvature flow of level set"); + + mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0); + + mTask = std::bind(&Filter::meanCurvatureImpl, std::placeholders::_1, std::placeholders::_2); + this->cook(true); + + mParent->track(); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter::Filter::laplacian() +{ + mParent->startInterrupter("Laplacian flow of level set"); + + mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0); + + mTask = std::bind(&Filter::laplacianImpl, std::placeholders::_1, std::placeholders::_2); + this->cook(true); + + mParent->track(); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter::Filter::offset(ValueType value) +{ + mParent->startInterrupter("Offsetting level set"); + + mParent->leafs().removeAuxBuffers();// no auxiliary buffers required + + const ValueType CFL = ValueType(0.5) * mParent->voxelSize(), offset = openvdb::math::Abs(value); + ValueType dist = 0.0; + while (offset-dist > ValueType(0.001)*CFL && mParent->checkInterrupter()) { + const ValueType delta = openvdb::math::Min(offset-dist, CFL); + dist += delta; + + mTask = std::bind(&Filter::offsetImpl, + std::placeholders::_1, std::placeholders::_2, copysign(delta, value)); + this->cook(false); + + mParent->track(); + } + + mParent->endInterrupter(); +} + + +///////////////////////// PRIVATE METHODS ////////////////////// + +/// Performs parabolic mean-curvature diffusion +template +inline void +LevelSetFilter::Filter::meanCurvatureImpl(const LeafRange& range) +{ + mParent->checkInterrupter(); + //const float CFL = 0.9f, dt = CFL * mDx * mDx / 6.0f; + const ValueType dx = mParent->voxelSize(), dt = math::Pow2(dx) / ValueType(3.0); + math::CurvatureStencil stencil(mParent->grid(), dx); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.meanCurvatureNormGrad(); + buffer[iter.pos()] = b * phi0 + a * phi1; + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer[iter.pos()] = *iter + dt*stencil.meanCurvatureNormGrad(); + } + } + } +} + +/// Performs Laplacian diffusion. Note if the grids contains a true +/// signed distance field (e.g. a solution to the Eikonal equation) +/// Laplacian diffusions (e.g. geometric heat equation) is actually +/// identical to mean curvature diffusion, yet less computationally +/// expensive! In other words if you're performing renormalization +/// anyway (e.g. rebuilding the narrow-band) you should consider +/// performing Laplacian diffusion over mean curvature flow! +template +inline void +LevelSetFilter::Filter::laplacianImpl(const LeafRange& range) +{ + mParent->checkInterrupter(); + //const float CFL = 0.9f, half_dt = CFL * mDx * mDx / 12.0f; + const ValueType dx = mParent->voxelSize(), dt = math::Pow2(dx) / ValueType(6.0); + math::GradStencil stencil(mParent->grid(), dx); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.laplacian(); + buffer[iter.pos()] = b * phi0 + a * phi1; + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer[iter.pos()] = *iter + dt*stencil.laplacian(); + } + } + } +} + +/// Offsets the values by a constant +template +inline void +LevelSetFilter::Filter::offsetImpl( + const LeafRange& range, ValueType offset) +{ + mParent->checkInterrupter(); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) iter.setValue(*iter + a*offset); + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + iter.setValue(*iter + offset); + } + } + } +} + +/// Performs simple but slow median-value diffusion +template +inline void +LevelSetFilter::Filter::medianImpl(const LeafRange& range, int width) +{ + mParent->checkInterrupter(); + typename math::DenseStencil stencil(mParent->grid(), width);//creates local cache! + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + buffer[iter.pos()] = b * (*iter) + a * stencil.median(); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer[iter.pos()] = stencil.median(); + } + } + } +} + +/// One dimensional convolution of a separable box filter +template +template +inline void +LevelSetFilter::Filter::boxImpl(const LeafRange& range, Int32 w) +{ + mParent->checkInterrupter(); + AvgT avg(mParent->grid(), w); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + const Coord xyz = iter.getCoord(); + if (alpha(xyz, a, b)) buffer[iter.pos()] = b * (*iter)+ a * avg(xyz); + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + buffer[iter.pos()] = avg(iter.getCoord()); + } + } + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetFracture.h b/openvdb/tools/LevelSetFracture.h new file mode 100644 index 00000000..86ca41ea --- /dev/null +++ b/openvdb/tools/LevelSetFracture.h @@ -0,0 +1,317 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file tools/LevelSetFracture.h +/// +/// @brief Divide volumes represented by level set grids into multiple, +/// disjoint pieces by intersecting them with one or more "cutter" volumes, +/// also represented by level sets. + +#ifndef OPENVDB_TOOLS_LEVELSETFRACTURE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETFRACTURE_HAS_BEEN_INCLUDED + +#include +#include +#include + +#include "Composite.h" // for csgIntersectionCopy() and csgDifferenceCopy() +#include "GridTransformer.h" // for resampleToMatch() +#include "LevelSetUtil.h" // for sdfSegmentation() + +#include // for std::max(), std::min() +#include +#include +#include + +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Level set fracturing +template +class LevelSetFracture +{ +public: + using Vec3sList = std::vector; + using QuatsList = std::vector; + using GridPtrList = std::list; + using GridPtrListIter = typename GridPtrList::iterator; + + + /// @brief Default constructor + /// + /// @param interrupter optional interrupter object + explicit LevelSetFracture(InterruptType* interrupter = nullptr); + + /// @brief Divide volumes represented by level set grids into multiple, + /// disjoint pieces by intersecting them with one or more "cutter" volumes, + /// also represented by level sets. + /// @details If desired, the process can be applied iteratively, so that + /// fragments created with one cutter are subdivided by other cutters. + /// + /// @note The incoming @a grids and the @a cutter are required to have matching + /// transforms and narrow band widths! + /// + /// @param grids list of grids to fracture. The residuals of the + /// fractured grids will remain in this list + /// @param cutter a level set grid to use as the cutter object + /// @param segment toggle to split disjoint fragments into their own grids + /// @param points optional list of world space points at which to instance the + /// cutter object (if null, use the cutter's current position only) + /// @param rotations optional list of custom rotations for each cutter instance + /// @param cutterOverlap toggle to allow consecutive cutter instances to fracture + /// previously generated fragments + void fracture(GridPtrList& grids, const GridType& cutter, bool segment = false, + const Vec3sList* points = nullptr, const QuatsList* rotations = nullptr, + bool cutterOverlap = true); + + /// Return a list of new fragments, not including the residuals from the input grids. + GridPtrList& fragments() { return mFragments; } + + /// Remove all elements from the fragment list. + void clear() { mFragments.clear(); } + +private: + // disallow copy by assignment + void operator=(const LevelSetFracture&) {} + + bool wasInterrupted(int percent = -1) const { + return mInterrupter && mInterrupter->wasInterrupted(percent); + } + + bool isValidFragment(GridType&) const; + void segmentFragments(GridPtrList&) const; + void process(GridPtrList&, const GridType& cutter); + + InterruptType* mInterrupter; + GridPtrList mFragments; +}; + + +//////////////////////////////////////// + + +// Internal utility objects and implementation details + +namespace level_set_fracture_internal { + + +template +struct FindMinMaxVoxelValue { + + using ValueType = typename LeafNodeType::ValueType; + + FindMinMaxVoxelValue(const std::vector& nodes) + : minValue(std::numeric_limits::max()) + , maxValue(-minValue) + , mNodes(nodes.empty() ? nullptr : &nodes.front()) + { + } + + FindMinMaxVoxelValue(FindMinMaxVoxelValue& rhs, tbb::split) + : minValue(std::numeric_limits::max()) + , maxValue(-minValue) + , mNodes(rhs.mNodes) + { + } + + void operator()(const tbb::blocked_range& range) { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + const ValueType* data = mNodes[n]->buffer().data(); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + minValue = std::min(minValue, data[i]); + maxValue = std::max(maxValue, data[i]); + } + } + } + + void join(FindMinMaxVoxelValue& rhs) { + minValue = std::min(minValue, rhs.minValue); + maxValue = std::max(maxValue, rhs.maxValue); + } + + ValueType minValue, maxValue; + + LeafNodeType const * const * const mNodes; +}; // struct FindMinMaxVoxelValue + + +} // namespace level_set_fracture_internal + + +//////////////////////////////////////// + + +template +LevelSetFracture::LevelSetFracture(InterruptType* interrupter) + : mInterrupter(interrupter) + , mFragments() +{ +} + + +template +void +LevelSetFracture::fracture(GridPtrList& grids, const GridType& cutter, + bool segmentation, const Vec3sList* points, const QuatsList* rotations, bool cutterOverlap) +{ + // We can process all incoming grids with the same cutter instance, + // this optimization is enabled by the requirement of having matching + // transforms between all incoming grids and the cutter object. + if (points && points->size() != 0) { + + + math::Transform::Ptr originalCutterTransform = cutter.transform().copy(); + GridType cutterGrid(*const_cast(&cutter), ShallowCopy()); + + const bool hasInstanceRotations = + points && rotations && points->size() == rotations->size(); + + // for each instance point.. + for (size_t p = 0, P = points->size(); p < P; ++p) { + int percent = int((float(p) / float(P)) * 100.0); + if (wasInterrupted(percent)) break; + + GridType instCutterGrid; + instCutterGrid.setTransform(originalCutterTransform->copy()); + math::Transform::Ptr xform = originalCutterTransform->copy(); + + if (hasInstanceRotations) { + const Vec3s& rot = (*rotations)[p].eulerAngles(math::XYZ_ROTATION); + xform->preRotate(rot[0], math::X_AXIS); + xform->preRotate(rot[1], math::Y_AXIS); + xform->preRotate(rot[2], math::Z_AXIS); + xform->postTranslate((*points)[p]); + } else { + xform->postTranslate((*points)[p]); + } + + cutterGrid.setTransform(xform); + + // Since there is no scaling, use the generic resampler instead of + // the more expensive level set rebuild tool. + if (mInterrupter != nullptr) { + + if (hasInstanceRotations) { + doResampleToMatch(cutterGrid, instCutterGrid, *mInterrupter); + } else { + doResampleToMatch(cutterGrid, instCutterGrid, *mInterrupter); + } + } else { + util::NullInterrupter interrupter; + if (hasInstanceRotations) { + doResampleToMatch(cutterGrid, instCutterGrid, interrupter); + } else { + doResampleToMatch(cutterGrid, instCutterGrid, interrupter); + } + } + + if (wasInterrupted(percent)) break; + + if (cutterOverlap && !mFragments.empty()) process(mFragments, instCutterGrid); + process(grids, instCutterGrid); + } + + } else { + // use cutter in place + if (cutterOverlap && !mFragments.empty()) process(mFragments, cutter); + process(grids, cutter); + } + + if (segmentation) { + segmentFragments(mFragments); + segmentFragments(grids); + } +} + + +template +bool +LevelSetFracture::isValidFragment(GridType& grid) const +{ + using LeafNodeType = typename GridType::TreeType::LeafNodeType; + + if (grid.tree().leafCount() < 9) { + + std::vector nodes; + grid.tree().getNodes(nodes); + + Index64 activeVoxelCount = 0; + + for (size_t n = 0, N = nodes.size(); n < N; ++n) { + activeVoxelCount += nodes[n]->onVoxelCount(); + } + + if (activeVoxelCount < 27) return false; + + level_set_fracture_internal::FindMinMaxVoxelValue op(nodes); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), op); + + if ((op.minValue < 0) == (op.maxValue < 0)) return false; + } + + return true; +} + + +template +void +LevelSetFracture::segmentFragments(GridPtrList& grids) const +{ + GridPtrList newFragments; + + for (GridPtrListIter it = grids.begin(); it != grids.end(); ++it) { + + std::vector segments; + segmentSDF(*(*it), segments); + + for (size_t n = 0, N = segments.size(); n < N; ++n) { + newFragments.push_back(segments[n]); + } + } + + grids.swap(newFragments); +} + + +template +void +LevelSetFracture::process( + GridPtrList& grids, const GridType& cutter) +{ + using GridPtr = typename GridType::Ptr; + GridPtrList newFragments; + + for (GridPtrListIter it = grids.begin(); it != grids.end(); ++it) { + + if (wasInterrupted()) break; + + GridPtr& grid = *it; + + GridPtr fragment = csgIntersectionCopy(*grid, cutter); + if (!isValidFragment(*fragment)) continue; + + GridPtr residual = csgDifferenceCopy(*grid, cutter); + if (!isValidFragment(*residual)) continue; + + newFragments.push_back(fragment); + + grid->tree().clear(); + grid->tree().merge(residual->tree()); + } + + if (!newFragments.empty()) { + mFragments.splice(mFragments.end(), newFragments); + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETFRACTURE_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetMeasure.h b/openvdb/tools/LevelSetMeasure.h new file mode 100644 index 00000000..5534e6c6 --- /dev/null +++ b/openvdb/tools/LevelSetMeasure.h @@ -0,0 +1,563 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @author Ken Museth +/// +/// @file LevelSetMeasure.h + +#ifndef OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //for Pi +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Return the surface area of a narrow-band level set. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed level set surfaces +/// @param useWorldSpace if true the area is computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set or empty. +template +inline Real +levelSetArea(const GridType& grid, bool useWorldSpace = true); + +/// @brief Return the volume of a narrow-band level set surface. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed level set surfaces +/// @param useWorldSpace if true the volume is computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set or empty. +template +inline Real +levelSetVolume(const GridType& grid, bool useWorldSpace = true); + +/// @brief Return the Euler Characteristics of a narrow-band level set surface (possibly disconnected). +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed level set surfaces +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set or empty. +template +inline int +levelSetEulerCharacteristic(const GridType& grid); + +/// @brief Return the genus of a narrow-band level set surface. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed level set surfaces +/// @warning The genus is only well defined for a single connected surface +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set or empty. +template +inline int +levelSetGenus(const GridType& grid); + +//////////////////////////////////////////////////////////////////////////////////////// + +/// @brief Smeared-out and continuous Dirac Delta function. +template +class DiracDelta +{ +public: + // eps is the half-width of the dirac delta function in units of phi + DiracDelta(RealT eps) : mC(0.5/eps), mD(2*boost::math::constants::pi()*mC), mE(eps) {} + // values of the dirac delta function are in units of one over the units of phi + inline RealT operator()(RealT phi) const { return math::Abs(phi) > mE ? 0 : mC*(1+cos(mD*phi)); } +private: + const RealT mC, mD, mE; +};// DiracDelta functor + + +/// @brief Multi-threaded computation of surface area, volume and +/// average mean-curvature for narrow band level sets. +/// +/// @details To reduce the risk of round-off errors (primarily due to +/// catastrophic cancellation) and guarantee determinism during +/// multi-threading this class is implemented using parallel_for, and +/// delayed reduction of a sorted list. +template +class LevelSetMeasure +{ +public: + using GridType = GridT; + using TreeType = typename GridType::TreeType; + using ValueType = typename TreeType::ValueType; + using ManagerType = typename tree::LeafManager; + + static_assert(std::is_floating_point::value, + "level set measure is supported only for scalar, floating-point grids"); + + /// @brief Main constructor from a grid + /// @param grid The level set to be measured. + /// @param interrupt Optional interrupter. + /// @throw RuntimeError if the grid is not a level set or if it's empty. + LevelSetMeasure(const GridType& grid, InterruptT* interrupt = nullptr); + + /// @brief Re-initialize using the specified grid. + /// @param grid The level set to be measured. + /// @throw RuntimeError if the grid is not a level set or if it's empty. + void init(const GridType& grid); + + /// @brief Destructor + virtual ~LevelSetMeasure() {} + + /// @return the grain-size used for multi-threading + int getGrainSize() const { return mGrainSize; } + + /// @brief Set the grain-size used for multi-threading. + /// @note A grain size of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mGrainSize = grainsize; } + + /// @brief Compute the surface area of the level set. + /// @param useWorldUnits Specifies if the result is in world or voxel units. + /// @note Performs internal caching so only the initial call incurs actual computation. + Real area(bool useWorldUnits = true); + + /// @brief Compute the volume of the level set surface. + /// @param useWorldUnits Specifies if the result is in world or voxel units. + /// @note Performs internal caching so only the initial call incurs actual computation. + Real volume(bool useWorldUnits = true); + + /// @brief Compute the total mean curvature of the level set surface. + /// @param useWorldUnits Specifies if the result is in world or voxel units. + /// @note Performs internal caching so only the initial call incurs actual computation. + Real totMeanCurvature(bool useWorldUnits = true); + + /// @brief Compute the total gaussian curvature of the level set surface. + /// @param useWorldUnits Specifies if the result is in world or voxel units. + /// @note Performs internal caching so only the initial call incurs actual computation. + Real totGaussianCurvature(bool useWorldUnits = true); + + /// @brief Compute the average mean curvature of the level set surface. + /// @param useWorldUnits Specifies if the result is in world or voxel units. + /// @note Performs internal caching so only the initial call incurs actual computation. + Real avgMeanCurvature(bool useWorldUnits = true) {return this->totMeanCurvature(useWorldUnits) / this->area(useWorldUnits);} + + /// @brief Compute the average gaussian curvature of the level set surface. + /// @param useWorldUnits Specifies if the result is in world or voxel units. + /// @note Performs internal caching so only the initial call incurs actual computation. + Real avgGaussianCurvature(bool useWorldUnits = true) {return this->totGaussianCurvature(useWorldUnits) / this->area(useWorldUnits); } + + /// @brief Compute the Euler characteristic of the level set surface. + /// @note Performs internal caching so only the initial call incurs actual computation. + int eulerCharacteristic(); + + /// @brief Compute the genus of the level set surface. + /// @warning The genus is only well defined for a single connected surface. + /// @note Performs internal caching so only the initial call incurs actual computation. + int genus() { return 1 - this->eulerCharacteristic()/2;} + +private: + + using LeafT = typename TreeType::LeafNodeType; + using VoxelCIterT = typename LeafT::ValueOnCIter; + using LeafRange = typename ManagerType::LeafRange; + using LeafIterT = typename LeafRange::Iterator; + using ManagerPtr = std::unique_ptr; + using BufferPtr = std::unique_ptr; + + // disallow copy construction and copy by assignment! + LevelSetMeasure(const LevelSetMeasure&);// not implemented + LevelSetMeasure& operator=(const LevelSetMeasure&);// not implemented + + const GridType *mGrid; + ManagerPtr mLeafs; + BufferPtr mBuffer; + InterruptT *mInterrupter; + double mDx, mArea, mVolume, mTotMeanCurvature, mTotGausCurvature; + int mGrainSize; + bool mUpdateArea, mUpdateCurvature; + + // @brief Return false if the process was interrupted + bool checkInterrupter(); + + struct MeasureArea + { + MeasureArea(LevelSetMeasure* parent) : mParent(parent), mStencil(*mParent->mGrid) + { + if (parent->mInterrupter) parent->mInterrupter->start("Measuring area and volume of level set"); + if (parent->mGrainSize>0) { + tbb::parallel_for(parent->mLeafs->leafRange(parent->mGrainSize), *this); + } else { + (*this)(parent->mLeafs->leafRange()); + } + tbb::parallel_invoke([&](){parent->mArea = parent->reduce(0);}, + [&](){parent->mVolume = parent->reduce(1)/3.0;}); + parent->mUpdateArea = false; + if (parent->mInterrupter) parent->mInterrupter->end(); + } + MeasureArea(const MeasureArea& other) : mParent(other.mParent), mStencil(*mParent->mGrid) {} + void operator()(const LeafRange& range) const; + LevelSetMeasure* mParent; + mutable math::GradStencil mStencil; + };// MeasureArea + + struct MeasureCurvatures + { + MeasureCurvatures(LevelSetMeasure* parent) : mParent(parent), mStencil(*mParent->mGrid) + { + if (parent->mInterrupter) parent->mInterrupter->start("Measuring curvatures of level set"); + if (parent->mGrainSize>0) { + tbb::parallel_for(parent->mLeafs->leafRange(parent->mGrainSize), *this); + } else { + (*this)(parent->mLeafs->leafRange()); + } + tbb::parallel_invoke([&](){parent->mTotMeanCurvature = parent->reduce(0);}, + [&](){parent->mTotGausCurvature = parent->reduce(1);}); + parent->mUpdateCurvature = false; + if (parent->mInterrupter) parent->mInterrupter->end(); + } + MeasureCurvatures(const MeasureCurvatures& other) : mParent(other.mParent), mStencil(*mParent->mGrid) {} + void operator()(const LeafRange& range) const; + LevelSetMeasure* mParent; + mutable math::CurvatureStencil mStencil; + };// MeasureCurvatures + + double reduce(int offset) + { + double *first = mBuffer.get() + offset*mLeafs->leafCount(), *last = first + mLeafs->leafCount(); + tbb::parallel_sort(first, last);// mitigates catastrophic cancellation + Real sum = 0.0; + while(first != last) sum += *first++; + return sum; + } + +}; // end of LevelSetMeasure class + + +template +inline +LevelSetMeasure::LevelSetMeasure(const GridType& grid, InterruptT* interrupt) + : mInterrupter(interrupt) + , mGrainSize(1) +{ + this->init(grid); +} + +template +inline void +LevelSetMeasure::init(const GridType& grid) +{ + if (!grid.hasUniformVoxels()) { + OPENVDB_THROW(RuntimeError, + "The transform must have uniform scale for the LevelSetMeasure to function"); + } + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "LevelSetMeasure only supports level sets;" + " try setting the grid class to \"level set\""); + } + if (grid.empty()) { + OPENVDB_THROW(RuntimeError, + "LevelSetMeasure does not support empty grids;"); + } + mGrid = &grid; + mDx = grid.voxelSize()[0]; + mLeafs = std::make_unique(mGrid->tree()); + mBuffer = std::make_unique(2*mLeafs->leafCount()); + mUpdateArea = mUpdateCurvature = true; +} + +template +inline Real +LevelSetMeasure::area(bool useWorldUnits) +{ + if (mUpdateArea) {MeasureArea m(this);}; + double area = mArea; + if (useWorldUnits) area *= math::Pow2(mDx); + return area; +} + +template +inline Real +LevelSetMeasure::volume(bool useWorldUnits) +{ + if (mUpdateArea) {MeasureArea m(this);}; + double volume = mVolume; + if (useWorldUnits) volume *= math::Pow3(mDx) ; + return volume; +} + +template +inline Real +LevelSetMeasure::totMeanCurvature(bool useWorldUnits) +{ + if (mUpdateCurvature) {MeasureCurvatures m(this);}; + return mTotMeanCurvature * (useWorldUnits ? mDx : 1); +} + +template +inline Real +LevelSetMeasure::totGaussianCurvature(bool) +{ + if (mUpdateCurvature) {MeasureCurvatures m(this);}; + return mTotGausCurvature; +} + +template +inline int +LevelSetMeasure::eulerCharacteristic() +{ + const Real x = this->totGaussianCurvature(true) / (2.0*boost::math::constants::pi()); + return int(math::Round( x )); +} + +///////////////////////// PRIVATE METHODS ////////////////////// + +template +inline bool +LevelSetMeasure::checkInterrupter() +{ + if (util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return false; + } + return true; +} + +template +inline void +LevelSetMeasure:: +MeasureArea::operator()(const LeafRange& range) const +{ + using Vec3T = math::Vec3; + // computations are performed in index space where dV = 1 + mParent->checkInterrupter(); + const Real invDx = 1.0/mParent->mDx; + const DiracDelta DD(1.5);// dirac delta function is 3 voxel units wide + const size_t leafCount = mParent->mLeafs->leafCount(); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + Real sumA = 0, sumV = 0;//reduce risk of catastrophic cancellation + for (VoxelCIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Real dd = DD(invDx * (*voxelIter)); + if (dd > 0.0) { + mStencil.moveTo(voxelIter); + const Coord& p = mStencil.getCenterCoord();// in voxel units + const Vec3T g = mStencil.gradient();// in world units + sumA += dd*g.length();// \delta(\phi)*|\nabla\phi| + sumV += dd*(g[0]*Real(p[0]) + g[1]*Real(p[1]) + g[2]*Real(p[2]));// \delta(\phi)\vec{x}\cdot\nabla\phi + } + } + double* ptr = mParent->mBuffer.get() + leafIter.pos(); + *ptr = sumA; + ptr += leafCount; + *ptr = sumV; + } +} + +template +inline void +LevelSetMeasure:: +MeasureCurvatures::operator()(const LeafRange& range) const +{ + using Vec3T = math::Vec3; + // computations are performed in index space where dV = 1 + mParent->checkInterrupter(); + const Real dx = mParent->mDx, dx2=dx*dx, invDx = 1.0/dx; + const DiracDelta DD(1.5);// dirac delta function is 3 voxel units wide + ValueType mean, gauss; + const size_t leafCount = mParent->mLeafs->leafCount(); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + Real sumM = 0, sumG = 0;//reduce risk of catastrophic cancellation + for (VoxelCIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Real dd = DD(invDx * (*voxelIter)); + if (dd > 0.0) { + mStencil.moveTo(voxelIter); + const Vec3T g = mStencil.gradient(); + const Real dA = dd*g.length();// \delta(\phi)*\delta(\phi) + mStencil.curvatures(mean, gauss); + sumM += dA*mean*dx;// \delta(\phi)*\delta(\phi)*MeanCurvature + sumG += dA*gauss*dx2;// \delta(\phi)*\delta(\phi)*GaussCurvature + } + } + double* ptr = mParent->mBuffer.get() + leafIter.pos(); + *ptr = sumM; + ptr += leafCount; + *ptr = sumG; + } +} + +//////////////////////////////////////// + +//{ +/// @cond OPENVDB_LEVEL_SET_MEASURE_INTERNAL + +template +inline +typename std::enable_if::value, Real>::type +doLevelSetArea(const GridT& grid, bool useWorldUnits) +{ + LevelSetMeasure m(grid); + return m.area(useWorldUnits); +} + +template +inline +typename std::enable_if::value, Real>::type +doLevelSetArea(const GridT&, bool) +{ + OPENVDB_THROW(TypeError, + "level set area is supported only for scalar, floating-point grids"); +} + +/// @endcond +//} + +template +inline Real +levelSetArea(const GridT& grid, bool useWorldUnits) +{ + return doLevelSetArea(grid, useWorldUnits); +} + +//////////////////////////////////////// + +//{ +/// @cond OPENVDB_LEVEL_SET_MEASURE_INTERNAL + +template +inline +typename std::enable_if::value, Real>::type +doLevelSetVolume(const GridT& grid, bool useWorldUnits) +{ + LevelSetMeasure m(grid); + return m.volume(useWorldUnits); +} + +template +inline +typename std::enable_if::value, Real>::type +doLevelSetVolume(const GridT&, bool) +{ + OPENVDB_THROW(TypeError, + "level set volume is supported only for scalar, floating-point grids"); +} + +/// @endcond +//} + +template +inline Real +levelSetVolume(const GridT& grid, bool useWorldUnits) +{ + return doLevelSetVolume(grid, useWorldUnits); +} + +//////////////////////////////////////// + +//{ +/// @cond OPENVDB_LEVEL_SET_MEASURE_INTERNAL + +template +inline +typename std::enable_if::value, int>::type +doLevelSetEulerCharacteristic(const GridT& grid) +{ + LevelSetMeasure m(grid); + return m.eulerCharacteristic(); +} + +template +inline +typename std::enable_if::value, int>::type +doLevelSetEulerCharacteristic(const GridT&) +{ + OPENVDB_THROW(TypeError, + "level set euler characteristic is supported only for scalar, floating-point grids"); +} + +/// @endcond +//} + + +template +inline int +levelSetEulerCharacteristic(const GridT& grid) +{ + return doLevelSetEulerCharacteristic(grid); +} + +//////////////////////////////////////// + +//{ +/// @cond OPENVDB_LEVEL_SET_MEASURE_INTERNAL + +template +inline +typename std::enable_if::value, int>::type +doLevelSetEuler(const GridT& grid) +{ + LevelSetMeasure m(grid); + return m.eulerCharacteristics(); + +} + +template +inline +typename std::enable_if::value, int>::type +doLevelSetGenus(const GridT& grid) +{ + LevelSetMeasure m(grid); + return m.genus(); +} + +template +inline +typename std::enable_if::value, int>::type +doLevelSetGenus(const GridT&) +{ + OPENVDB_THROW(TypeError, + "level set genus is supported only for scalar, floating-point grids"); +} + +/// @endcond +//} + +template +inline int +levelSetGenus(const GridT& grid) +{ + return doLevelSetGenus(grid); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +/// @deprecated Use the @a LevelSetMeasure class instead. +template +OPENVDB_DEPRECATED +inline void +levelSetMeasure(const GridT& grid, Real& area, Real& volume, Real& avgCurvature, + bool useWorldUnits = true) +{ + LevelSetMeasure m(grid); + area = m.area(useWorldUnits); + volume = m.volume(useWorldUnits); + avgCurvature = m.avgMeanCurvature(useWorldUnits); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetMorph.h b/openvdb/tools/LevelSetMorph.h new file mode 100644 index 00000000..0f7a25d6 --- /dev/null +++ b/openvdb/tools/LevelSetMorph.h @@ -0,0 +1,644 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Ken Museth +/// +/// @file tools/LevelSetMorph.h +/// +/// @brief Shape morphology of level sets. Morphing from a source +/// narrow-band level sets to a target narrow-band level set. + +#ifndef OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED + +#include "LevelSetTracker.h" +#include "Interpolation.h" // for BoxSampler, etc. +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Shape morphology of level sets. Morphing from a source +/// narrow-band level sets to a target narrow-band level set. +/// +/// @details +/// The @c InterruptType template argument below refers to any class +/// with the following interface: +/// @code +/// class Interrupter { +/// ... +/// public: +/// void start(const char* name = nullptr) // called when computations begin +/// void end() // called when computations end +/// bool wasInterrupted(int percent=-1) // return true to break computation +/// }; +/// @endcode +/// +/// @note If no template argument is provided for this InterruptType, +/// the util::NullInterrupter is used, which implies that all interrupter +/// calls are no-ops (i.e., they incur no computational overhead). +template +class LevelSetMorphing +{ +public: + using GridType = GridT; + using TreeType = typename GridT::TreeType; + using TrackerT = LevelSetTracker; + using LeafRange = typename TrackerT::LeafRange; + using LeafType = typename TrackerT::LeafType; + using BufferType = typename TrackerT::BufferType; + using ValueType = typename TrackerT::ValueType; + + /// Main constructor + LevelSetMorphing(GridT& sourceGrid, const GridT& targetGrid, InterruptT* interrupt = nullptr) + : mTracker(sourceGrid, interrupt) + , mTarget(&targetGrid) + , mMask(nullptr) + , mSpatialScheme(math::HJWENO5_BIAS) + , mTemporalScheme(math::TVD_RK2) + , mMinMask(0) + , mDeltaMask(1) + , mInvertMask(false) + { + } + + virtual ~LevelSetMorphing() {} + + /// Redefine the target level set + void setTarget(const GridT& targetGrid) { mTarget = &targetGrid; } + + /// Define the alpha mask + void setAlphaMask(const GridT& maskGrid) { mMask = &maskGrid; } + + /// Return the spatial finite-difference scheme + math::BiasedGradientScheme getSpatialScheme() const { return mSpatialScheme; } + /// Set the spatial finite-difference scheme + void setSpatialScheme(math::BiasedGradientScheme scheme) { mSpatialScheme = scheme; } + + /// Return the temporal integration scheme + math::TemporalIntegrationScheme getTemporalScheme() const { return mTemporalScheme; } + /// Set the temporal integration scheme + void setTemporalScheme(math::TemporalIntegrationScheme scheme) { mTemporalScheme = scheme; } + + /// Return the spatial finite-difference scheme + math::BiasedGradientScheme getTrackerSpatialScheme() const + { + return mTracker.getSpatialScheme(); + } + /// Set the spatial finite-difference scheme + void setTrackerSpatialScheme(math::BiasedGradientScheme scheme) + { + mTracker.setSpatialScheme(scheme); + } + /// Return the temporal integration scheme + math::TemporalIntegrationScheme getTrackerTemporalScheme() const + { + return mTracker.getTemporalScheme(); + } + /// Set the temporal integration scheme + void setTrackerTemporalScheme(math::TemporalIntegrationScheme scheme) + { + mTracker.setTemporalScheme(scheme); + } + /// Return the number of normalizations performed per track or normalize call. + int getNormCount() const { return mTracker.getNormCount(); } + /// Set the number of normalizations performed per track or normalize call. + void setNormCount(int n) { mTracker.setNormCount(n); } + + /// Return the grain size used for multithreading + int getGrainSize() const { return mTracker.getGrainSize(); } + /// @brief Set the grain size used for multithreading. + /// @note A grain size of 0 or less disables multithreading! + void setGrainSize(int grainsize) { mTracker.setGrainSize(grainsize); } + + /// @brief Return the minimum value of the mask to be used for the + /// derivation of a smooth alpha value. + ValueType minMask() const { return mMinMask; } + + /// @brief Return the maximum value of the mask to be used for the + /// derivation of a smooth alpha value. + ValueType maxMask() const { return mDeltaMask + mMinMask; } + + /// @brief Define the range for the (optional) scalar mask. + /// @param min Minimum value of the range. + /// @param max Maximum value of the range. + /// @details Mask values outside the range maps to alpha values of + /// respectfully zero and one, and values inside the range maps + /// smoothly to 0->1 (unless of course the mask is inverted). + /// @throw ValueError if @a min is not smaller than @a max. + void setMaskRange(ValueType min, ValueType max) + { + if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)"); + mMinMask = min; + mDeltaMask = max-min; + } + + /// @brief Return true if the mask is inverted, i.e. min->max in the + /// original mask maps to 1->0 in the inverted alpha mask. + bool isMaskInverted() const { return mInvertMask; } + /// @brief Invert the optional mask, i.e. min->max in the original + /// mask maps to 1->0 in the inverted alpha mask. + void invertMask(bool invert=true) { mInvertMask = invert; } + + /// @brief Advect the level set from its current time, @a time0, to its + /// final time, @a time1. If @a time0 > @a time1, perform backward advection. + /// + /// @return the number of CFL iterations used to advect from @a time0 to @a time1 + size_t advect(ValueType time0, ValueType time1); + +private: + + // disallow copy construction and copy by assignment! + LevelSetMorphing(const LevelSetMorphing&);// not implemented + LevelSetMorphing& operator=(const LevelSetMorphing&);// not implemented + + template + size_t advect1(ValueType time0, ValueType time1); + + template + size_t advect2(ValueType time0, ValueType time1); + + template + size_t advect3(ValueType time0, ValueType time1); + + TrackerT mTracker; + const GridT *mTarget, *mMask; + math::BiasedGradientScheme mSpatialScheme; + math::TemporalIntegrationScheme mTemporalScheme; + ValueType mMinMask, mDeltaMask; + bool mInvertMask; + + // This templated private class implements all the level set magic. + template + struct Morph + { + /// Main constructor + Morph(LevelSetMorphing& parent); + /// Shallow copy constructor called by tbb::parallel_for() threads + Morph(const Morph& other); + /// Shallow copy constructor called by tbb::parallel_reduce() threads + Morph(Morph& other, tbb::split); + /// destructor + virtual ~Morph() {} + /// Advect the level set from its current time, time0, to its final time, time1. + /// @return number of CFL iterations + size_t advect(ValueType time0, ValueType time1); + /// Used internally by tbb::parallel_for() + void operator()(const LeafRange& r) const + { + if (mTask) mTask(const_cast(this), r); + else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); + } + /// Used internally by tbb::parallel_reduce() + void operator()(const LeafRange& r) + { + if (mTask) mTask(this, r); + else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); + } + /// This is only called by tbb::parallel_reduce() threads + void join(const Morph& other) { mMaxAbsS = math::Max(mMaxAbsS, other.mMaxAbsS); } + + /// Enum to define the type of multithreading + enum ThreadingMode { PARALLEL_FOR, PARALLEL_REDUCE }; // for internal use + // method calling tbb + void cook(ThreadingMode mode, size_t swapBuffer = 0); + + /// Sample field and return the CFT time step + typename GridT::ValueType sampleSpeed(ValueType time0, ValueType time1, Index speedBuffer); + void sampleXformedSpeed(const LeafRange& r, Index speedBuffer); + void sampleAlignedSpeed(const LeafRange& r, Index speedBuffer); + + // Convex combination of Phi and a forward Euler advection steps: + // Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|); + template + void euler(const LeafRange&, ValueType, Index, Index, Index); + inline void euler01(const LeafRange& r, ValueType t, Index s) {this->euler<0,1>(r,t,0,1,s);} + inline void euler12(const LeafRange& r, ValueType t) {this->euler<1,2>(r, t, 1, 1, 2);} + inline void euler34(const LeafRange& r, ValueType t) {this->euler<3,4>(r, t, 1, 2, 3);} + inline void euler13(const LeafRange& r, ValueType t) {this->euler<1,3>(r, t, 1, 2, 3);} + + using FuncType = typename std::function; + LevelSetMorphing* mParent; + ValueType mMinAbsS, mMaxAbsS; + const MapT* mMap; + FuncType mTask; + }; // end of private Morph struct + +};//end of LevelSetMorphing + +template +inline size_t +LevelSetMorphing::advect(ValueType time0, ValueType time1) +{ + switch (mSpatialScheme) { + case math::FIRST_BIAS: + return this->advect1(time0, time1); + //case math::SECOND_BIAS: + //return this->advect1(time0, time1); + //case math::THIRD_BIAS: + //return this->advect1(time0, time1); + //case math::WENO5_BIAS: + //return this->advect1(time0, time1); + case math::HJWENO5_BIAS: + return this->advect1(time0, time1); + case math::SECOND_BIAS: + case math::THIRD_BIAS: + case math::WENO5_BIAS: + case math::UNKNOWN_BIAS: + default: + OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); + } + return 0; +} + +template +template +inline size_t +LevelSetMorphing::advect1(ValueType time0, ValueType time1) +{ + switch (mTemporalScheme) { + case math::TVD_RK1: + return this->advect2(time0, time1); + case math::TVD_RK2: + return this->advect2(time0, time1); + case math::TVD_RK3: + return this->advect2(time0, time1); + case math::UNKNOWN_TIS: + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + } + return 0; +} + +template +template +inline size_t +LevelSetMorphing::advect2(ValueType time0, ValueType time1) +{ + const math::Transform& trans = mTracker.grid().transform(); + if (trans.mapType() == math::UniformScaleMap::mapType()) { + return this->advect3(time0, time1); + } else if (trans.mapType() == math::UniformScaleTranslateMap::mapType()) { + return this->advect3( + time0, time1); + } else if (trans.mapType() == math::UnitaryMap::mapType()) { + return this->advect3(time0, time1); + } else if (trans.mapType() == math::TranslationMap::mapType()) { + return this->advect3(time0, time1); + } else { + OPENVDB_THROW(ValueError, "MapType not supported!"); + } + return 0; +} + +template +template +inline size_t +LevelSetMorphing::advect3(ValueType time0, ValueType time1) +{ + Morph tmp(*this); + return tmp.advect(time0, time1); +} + + +/////////////////////////////////////////////////////////////////////// + +template +template +inline +LevelSetMorphing:: +Morph:: +Morph(LevelSetMorphing& parent) + : mParent(&parent) + , mMinAbsS(ValueType(1e-6)) + , mMap(parent.mTracker.grid().transform().template constMap().get()) + , mTask(nullptr) +{ +} + +template +template +inline +LevelSetMorphing:: +Morph:: +Morph(const Morph& other) + : mParent(other.mParent) + , mMinAbsS(other.mMinAbsS) + , mMaxAbsS(other.mMaxAbsS) + , mMap(other.mMap) + , mTask(other.mTask) +{ +} + +template +template +inline +LevelSetMorphing:: +Morph:: +Morph(Morph& other, tbb::split) + : mParent(other.mParent) + , mMinAbsS(other.mMinAbsS) + , mMaxAbsS(other.mMaxAbsS) + , mMap(other.mMap) + , mTask(other.mTask) +{ +} + +template +template +inline size_t +LevelSetMorphing:: +Morph:: +advect(ValueType time0, ValueType time1) +{ + namespace ph = std::placeholders; + + // Make sure we have enough temporal auxiliary buffers for the time + // integration AS WELL AS an extra buffer with the speed function! + static const Index auxBuffers = 1 + (TemporalScheme == math::TVD_RK3 ? 2 : 1); + size_t countCFL = 0; + while (time0 < time1 && mParent->mTracker.checkInterrupter()) { + mParent->mTracker.leafs().rebuildAuxBuffers(auxBuffers); + + const ValueType dt = this->sampleSpeed(time0, time1, auxBuffers); + if ( math::isZero(dt) ) break;//V is essentially zero so terminate + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time + switch(TemporalScheme) { + case math::TVD_RK1: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * Speed(2) * |Grad[Phi(0)]| + mTask = std::bind(&Morph::euler01, ph::_1, ph::_2, dt, /*speed*/2); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook(PARALLEL_FOR, 1); + break; + case math::TVD_RK2: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * Speed(2) * |Grad[Phi(0)]| + mTask = std::bind(&Morph::euler01, ph::_1, ph::_2, dt, /*speed*/2); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook(PARALLEL_FOR, 1); + + // Convex combine explict Euler step: t2 = t0 + dt + // Phi_t2(1) = 1/2 * Phi_t0(1) + 1/2 * (Phi_t1(0) - dt * Speed(2) * |Grad[Phi(0)]|) + mTask = std::bind(&Morph::euler12, ph::_1, ph::_2, dt); + + // Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1) + this->cook(PARALLEL_FOR, 1); + break; + case math::TVD_RK3: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * Speed(3) * |Grad[Phi(0)]| + mTask = std::bind(&Morph::euler01, ph::_1, ph::_2, dt, /*speed*/3); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook(PARALLEL_FOR, 1); + + // Convex combine explict Euler step: t2 = t0 + dt/2 + // Phi_t2(2) = 3/4 * Phi_t0(1) + 1/4 * (Phi_t1(0) - dt * Speed(3) * |Grad[Phi(0)]|) + mTask = std::bind(&Morph::euler34, ph::_1, ph::_2, dt); + + // Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2) + this->cook(PARALLEL_FOR, 2); + + // Convex combine explict Euler step: t3 = t0 + dt + // Phi_t3(2) = 1/3 * Phi_t0(1) + 2/3 * (Phi_t2(0) - dt * Speed(3) * |Grad[Phi(0)]|) + mTask = std::bind(&Morph::euler13, ph::_1, ph::_2, dt); + + // Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2) + this->cook(PARALLEL_FOR, 2); + break; + case math::UNKNOWN_TIS: + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + }//end of compile-time resolved switch + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + + time0 += dt; + ++countCFL; + mParent->mTracker.leafs().removeAuxBuffers(); + + // Track the narrow band + mParent->mTracker.track(); + }//end wile-loop over time + + return countCFL;//number of CLF propagation steps +} + +template +template +inline typename GridT::ValueType +LevelSetMorphing:: +Morph:: +sampleSpeed(ValueType time0, ValueType time1, Index speedBuffer) +{ + namespace ph = std::placeholders; + + mMaxAbsS = mMinAbsS; + const size_t leafCount = mParent->mTracker.leafs().leafCount(); + if (leafCount==0 || time0 >= time1) return ValueType(0); + + const math::Transform& xform = mParent->mTracker.grid().transform(); + if (mParent->mTarget->transform() == xform && + (mParent->mMask == nullptr || mParent->mMask->transform() == xform)) { + mTask = std::bind(&Morph::sampleAlignedSpeed, ph::_1, ph::_2, speedBuffer); + } else { + mTask = std::bind(&Morph::sampleXformedSpeed, ph::_1, ph::_2, speedBuffer); + } + this->cook(PARALLEL_REDUCE); + if (math::isApproxEqual(mMinAbsS, mMaxAbsS)) return ValueType(0);//speed is essentially zero + static const ValueType CFL = (TemporalScheme == math::TVD_RK1 ? ValueType(0.3) : + TemporalScheme == math::TVD_RK2 ? ValueType(0.9) : + ValueType(1.0))/math::Sqrt(ValueType(3.0)); + const ValueType dt = math::Abs(time1 - time0), dx = mParent->mTracker.voxelSize(); + return math::Min(dt, ValueType(CFL*dx/mMaxAbsS)); +} + +template +template +inline void +LevelSetMorphing:: +Morph:: +sampleXformedSpeed(const LeafRange& range, Index speedBuffer) +{ + using VoxelIterT = typename LeafType::ValueOnCIter; + using SamplerT = tools::GridSampler; + + const MapT& map = *mMap; + mParent->mTracker.checkInterrupter(); + + typename GridT::ConstAccessor targetAcc = mParent->mTarget->getAccessor(); + SamplerT target(targetAcc, mParent->mTarget->transform()); + if (mParent->mMask == nullptr) { + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueType* speed = leafIter.buffer(speedBuffer).data(); + bool isZero = true; + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + ValueType& s = speed[voxelIter.pos()]; + s -= target.wsSample(map.applyMap(voxelIter.getCoord().asVec3d())); + if (!math::isApproxZero(s)) isZero = false; + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel + } + } else { + const ValueType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask); + const bool invMask = mParent->isMaskInverted(); + typename GridT::ConstAccessor maskAcc = mParent->mMask->getAccessor(); + SamplerT mask(maskAcc, mParent->mMask->transform()); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueType* speed = leafIter.buffer(speedBuffer).data(); + bool isZero = true; + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Vec3R xyz = map.applyMap(voxelIter.getCoord().asVec3d());//world space + const ValueType a = math::SmoothUnitStep((mask.wsSample(xyz)-min)*invNorm); + ValueType& s = speed[voxelIter.pos()]; + s -= target.wsSample(xyz); + s *= invMask ? 1 - a : a; + if (!math::isApproxZero(s)) isZero = false; + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel + } + } +} + +template +template +inline void +LevelSetMorphing:: +Morph:: +sampleAlignedSpeed(const LeafRange& range, Index speedBuffer) +{ + using VoxelIterT = typename LeafType::ValueOnCIter; + + mParent->mTracker.checkInterrupter(); + + typename GridT::ConstAccessor target = mParent->mTarget->getAccessor(); + + if (mParent->mMask == nullptr) { + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueType* speed = leafIter.buffer(speedBuffer).data(); + bool isZero = true; + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + ValueType& s = speed[voxelIter.pos()]; + s -= target.getValue(voxelIter.getCoord()); + if (!math::isApproxZero(s)) isZero = false; + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel + } + } else { + const ValueType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask); + const bool invMask = mParent->isMaskInverted(); + typename GridT::ConstAccessor mask = mParent->mMask->getAccessor(); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueType* speed = leafIter.buffer(speedBuffer).data(); + bool isZero = true; + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Coord ijk = voxelIter.getCoord();//index space + const ValueType a = math::SmoothUnitStep((mask.getValue(ijk)-min)*invNorm); + ValueType& s = speed[voxelIter.pos()]; + s -= target.getValue(ijk); + s *= invMask ? 1 - a : a; + if (!math::isApproxZero(s)) isZero = false; + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel + } + } +} + +template +template +inline void +LevelSetMorphing:: +Morph:: +cook(ThreadingMode mode, size_t swapBuffer) +{ + mParent->mTracker.startInterrupter("Morphing level set"); + + const int grainSize = mParent->mTracker.getGrainSize(); + const LeafRange range = mParent->mTracker.leafs().leafRange(grainSize); + + if (mParent->mTracker.getGrainSize()==0) { + (*this)(range); + } else if (mode == PARALLEL_FOR) { + tbb::parallel_for(range, *this); + } else if (mode == PARALLEL_REDUCE) { + tbb::parallel_reduce(range, *this); + } else { + OPENVDB_THROW(ValueError, "expected threading mode " << int(PARALLEL_FOR) + << " or " << int(PARALLEL_REDUCE) << ", got " << int(mode)); + } + + mParent->mTracker.leafs().swapLeafBuffer(swapBuffer, grainSize == 0); + + mParent->mTracker.endInterrupter(); +} + +template +template +template +inline void +LevelSetMorphing:: +Morph:: +euler(const LeafRange& range, ValueType dt, + Index phiBuffer, Index resultBuffer, Index speedBuffer) +{ + using SchemeT = math::BIAS_SCHEME; + using StencilT = typename SchemeT::template ISStencil::StencilType; + using VoxelIterT = typename LeafType::ValueOnCIter; + using NumGrad = math::GradientNormSqrd; + + static const ValueType Alpha = ValueType(Nominator)/ValueType(Denominator); + static const ValueType Beta = ValueType(1) - Alpha; + + mParent->mTracker.checkInterrupter(); + const MapT& map = *mMap; + StencilT stencil(mParent->mTracker.grid()); + + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + const ValueType* speed = leafIter.buffer(speedBuffer).data(); + if (math::isExactlyEqual(speed[0], std::numeric_limits::max())) continue; + const ValueType* phi = leafIter.buffer(phiBuffer).data(); + ValueType* result = leafIter.buffer(resultBuffer).data(); + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Index n = voxelIter.pos(); + if (math::isApproxZero(speed[n])) continue; + stencil.moveTo(voxelIter); + const ValueType v = stencil.getValue() - dt * speed[n] * NumGrad::result(map, stencil); + result[n] = Nominator ? Alpha * phi[n] + Beta * v : v; + }//loop over active voxels in the leaf of the mask + }//loop over leafs of the level set +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetPlatonic.h b/openvdb/tools/LevelSetPlatonic.h new file mode 100644 index 00000000..1f801b99 --- /dev/null +++ b/openvdb/tools/LevelSetPlatonic.h @@ -0,0 +1,478 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Ken Museth +/// +/// @file LevelSetPlatonic.h +/// +/// @brief Generate a narrow-band level sets of the five platonic solids. +/// +/// @note By definition a level set has a fixed narrow band width +/// (the half width is defined by LEVEL_SET_HALF_WIDTH in Types.h), +/// whereas an SDF can have a variable narrow band width. + +#ifndef OPENVDB_TOOLS_LEVELSETPLATONIC_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETPLATONIC_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a platonic solid. +/// +/// @param faceCount number of faces of the platonic solid, i.e. 4, 6, 8, 12 or 20 +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// @param interrupt a pointer adhering to the util::NullInterrupter interface +/// +/// @details Faces: TETRAHEDRON=4, CUBE=6, OCTAHEDRON=8, DODECAHEDRON=12, ICOSAHEDRON=20 +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetPlatonic( + int faceCount, // 4, 6, 8, 12 or 20 + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = nullptr); + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a platonic solid. +/// +/// @param faceCount number of faces of the platonic solid, i.e. 4, 6, 8, 12 or 20 +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// +/// @details Faces: TETRAHEDRON=4, CUBE=6, OCTAHEDRON=8, DODECAHEDRON=12, ICOSAHEDRON=20 +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetPlatonic( + int faceCount,// 4, 6, 8, 12 or 20 + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + util::NullInterrupter tmp; + return createLevelSetPlatonic(faceCount, scale, center, voxelSize, halfWidth, &tmp); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a tetrahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// @param interrupt a pointer adhering to the util::NullInterrupter interface +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetTetrahedron( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = nullptr) +{ + return createLevelSetPlatonic( + 4, scale, center, voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a tetrahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetTetrahedron( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + util::NullInterrupter tmp; + return createLevelSetPlatonic(4, scale, center, voxelSize, halfWidth, &tmp); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a cube. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// @param interrupt a pointer adhering to the util::NullInterrupter interface +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetCube( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = nullptr) +{ + return createLevelSetPlatonic(6, scale, center, voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a cube. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetCube( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + util::NullInterrupter tmp; + return createLevelSetPlatonic(6, scale, center, voxelSize, halfWidth, &tmp); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of an octahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// @param interrupt a pointer adhering to the util::NullInterrupter interface +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetOctahedron( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = nullptr) +{ + return createLevelSetPlatonic(8, scale, center, voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of an octahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetOctahedron( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + util::NullInterrupter tmp; + return createLevelSetPlatonic(8, scale, center, voxelSize, halfWidth, &tmp); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a dodecahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// @param interrupt a pointer adhering to the util::NullInterrupter interface +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetDodecahedron( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = nullptr) +{ + return createLevelSetPlatonic(12, scale, center, voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a dodecahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetDodecahedron( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + util::NullInterrupter tmp; + return createLevelSetPlatonic(12, scale, center, voxelSize, halfWidth, &tmp); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of an icosahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// @param interrupt a pointer adhering to the util::NullInterrupter interface +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetIcosahedron( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = nullptr) +{ + return createLevelSetPlatonic(20, scale, center, voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of an icosahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetIcosahedron( + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + util::NullInterrupter tmp; + return createLevelSetPlatonic(20, scale, center, voxelSize, halfWidth, &tmp); +} + +//////////////////////////////////////////////////////////////////////////////// + +template +typename GridType::Ptr +createLevelSetPlatonic(int faceCount,float scale, const Vec3f& center, + float voxelSize, float halfWidth, InterruptT *interrupt) +{ + // GridType::ValueType is required to be a floating-point scalar. + static_assert(std::is_floating_point::value, + "level set grids must have scalar, floating-point value types"); + + const math::Transform::Ptr xform = math::Transform::createLinearTransform( voxelSize ); + + std::vector vtx; + std::vector tri; + std::vector qua; + + if (faceCount == 4) {// Tetrahedron + + vtx.push_back( Vec3f( 0.0f, 1.0f, 0.0f) ); + vtx.push_back( Vec3f(-0.942810297f, -0.333329707f, 0.0f) ); + vtx.push_back( Vec3f( 0.471405149f, -0.333329707f, 0.816497624f) ); + vtx.push_back( Vec3f( 0.471405149f, -0.333329707f, -0.816497624f) ); + + tri.push_back( Vec3I(0, 2, 3) ); + tri.push_back( Vec3I(0, 3, 1) ); + tri.push_back( Vec3I(0, 1, 2) ); + tri.push_back( Vec3I(1, 3, 2) ); + + } else if (faceCount == 6) {// Cube + + vtx.push_back( Vec3f(-0.5f, -0.5f, -0.5f) ); + vtx.push_back( Vec3f( 0.5f, -0.5f, -0.5f) ); + vtx.push_back( Vec3f( 0.5f, -0.5f, 0.5f) ); + vtx.push_back( Vec3f(-0.5f, -0.5f, 0.5f) ); + vtx.push_back( Vec3f(-0.5f, 0.5f, -0.5f) ); + vtx.push_back( Vec3f( 0.5f, 0.5f, -0.5f) ); + vtx.push_back( Vec3f( 0.5f, 0.5f, 0.5f) ); + vtx.push_back( Vec3f(-0.5f, 0.5f, 0.5f) ); + + qua.push_back( Vec4I(1, 0, 4, 5) ); + qua.push_back( Vec4I(2, 1, 5, 6) ); + qua.push_back( Vec4I(3, 2, 6, 7) ); + qua.push_back( Vec4I(0, 3, 7, 4) ); + qua.push_back( Vec4I(2, 3, 0, 1) ); + qua.push_back( Vec4I(5, 4, 7, 6) ); + + } else if (faceCount == 8) {// Octahedron + + vtx.push_back( Vec3f( 0.0f, 0.0f, -1.0f) ); + vtx.push_back( Vec3f( 1.0f, 0.0f, 0.0f) ); + vtx.push_back( Vec3f( 0.0f, 0.0f, 1.0f) ); + vtx.push_back( Vec3f(-1.0f, 0.0f, 0.0f) ); + vtx.push_back( Vec3f( 0.0f,-1.0f, 0.0f) ); + vtx.push_back( Vec3f( 0.0f, 1.0f, 0.0f) ); + + tri.push_back( Vec3I(0, 4, 3) ); + tri.push_back( Vec3I(0, 1, 4) ); + tri.push_back( Vec3I(1, 2, 4) ); + tri.push_back( Vec3I(2, 3, 4) ); + tri.push_back( Vec3I(0, 3, 5) ); + tri.push_back( Vec3I(0, 5, 1) ); + tri.push_back( Vec3I(1, 5, 2) ); + tri.push_back( Vec3I(2, 5, 3) ); + + } else if (faceCount == 12) {// Dodecahedron + + vtx.push_back( Vec3f( 0.354437858f, 0.487842113f, -0.789344311f) ); + vtx.push_back( Vec3f( 0.573492587f, -0.186338872f, -0.78934437f) ); + vtx.push_back( Vec3f( 0.0f, -0.603005826f, -0.78934443f) ); + vtx.push_back( Vec3f(-0.573492587f, -0.186338872f, -0.78934437f) ); + vtx.push_back( Vec3f(-0.354437858f, 0.487842113f, -0.789344311f) ); + vtx.push_back( Vec3f(-0.573492587f, 0.789345026f, -0.186338797f) ); + vtx.push_back( Vec3f(-0.927930415f, -0.301502913f, -0.186338872f) ); + vtx.push_back( Vec3f( 0.0f, -0.975683928f, -0.186338902f) ); + vtx.push_back( Vec3f( 0.927930415f, -0.301502913f, -0.186338872f) ); + vtx.push_back( Vec3f( 0.573492587f, 0.789345026f, -0.186338797f) ); + vtx.push_back( Vec3f( 0.0f, 0.975683868f, 0.186338902f) ); + vtx.push_back( Vec3f(-0.927930415f, 0.301502913f, 0.186338872f) ); + vtx.push_back( Vec3f(-0.573492587f, -0.789345026f, 0.186338797f) ); + vtx.push_back( Vec3f( 0.573492587f, -0.789345026f, 0.186338797f) ); + vtx.push_back( Vec3f( 0.927930415f, 0.301502913f, 0.186338872f) ); + vtx.push_back( Vec3f( 0.0f, 0.603005826f, 0.78934443f) ); + vtx.push_back( Vec3f( 0.573492587f, 0.186338872f, 0.78934437f) ); + vtx.push_back( Vec3f( 0.354437858f, -0.487842113f, 0.789344311f) ); + vtx.push_back( Vec3f(-0.354437858f, -0.487842113f, 0.789344311f) ); + vtx.push_back( Vec3f(-0.573492587f, 0.186338872f, 0.78934437f) ); + + qua.push_back( Vec4I(0, 1, 2, 3) ); + tri.push_back( Vec3I(0, 3, 4) ); + qua.push_back( Vec4I(0, 4, 5, 10) ); + tri.push_back( Vec3I(0, 10, 9) ); + qua.push_back( Vec4I(0, 9, 14, 8) ); + tri.push_back( Vec3I(0, 8, 1) ); + qua.push_back( Vec4I(1, 8, 13, 7) ); + tri.push_back( Vec3I(1, 7, 2) ); + qua.push_back( Vec4I(2, 7, 12, 6) ); + tri.push_back( Vec3I(2, 6, 3) ); + qua.push_back( Vec4I(3, 6, 11, 5) ); + tri.push_back( Vec3I(3, 5, 4) ); + qua.push_back( Vec4I(5, 11, 19, 15) ); + tri.push_back( Vec3I(5, 15, 10) ); + qua.push_back( Vec4I(6, 12, 18, 19) ); + tri.push_back( Vec3I(6, 19, 11) ); + qua.push_back( Vec4I(7, 13, 17, 18) ); + tri.push_back( Vec3I(7, 18, 12) ); + qua.push_back( Vec4I(8, 14, 16, 17) ); + tri.push_back( Vec3I(8, 17, 13) ); + qua.push_back( Vec4I(9, 10, 15, 16) ); + tri.push_back( Vec3I(9, 16, 14) ); + qua.push_back( Vec4I(15, 19, 18, 17) ); + tri.push_back( Vec3I(15, 17, 16) ); + + } else if (faceCount == 20) {// Icosahedron + + vtx.push_back( Vec3f(0.0f, 0.0f, -1.0f) ); + vtx.push_back( Vec3f(0.0f, 0.894427359f, -0.447213143f) ); + vtx.push_back( Vec3f(0.850650847f, 0.276393682f, -0.447213203f) ); + vtx.push_back( Vec3f(0.525731206f, -0.723606944f, -0.447213262f) ); + vtx.push_back( Vec3f(-0.525731206f, -0.723606944f, -0.447213262f) ); + vtx.push_back( Vec3f(-0.850650847f, 0.276393682f, -0.447213203f) ); + vtx.push_back( Vec3f(-0.525731206f, 0.723606944f, 0.447213262f) ); + vtx.push_back( Vec3f(-0.850650847f, -0.276393682f, 0.447213203f) ); + vtx.push_back( Vec3f(0.0f, -0.894427359f, 0.447213143f) ); + vtx.push_back( Vec3f(0.850650847f, -0.276393682f, 0.447213203f) ); + vtx.push_back( Vec3f(0.525731206f, 0.723606944f, 0.447213262f) ); + vtx.push_back( Vec3f(0.0f, 0.0f, 1.0f) ); + + tri.push_back( Vec3I( 2, 0, 1) ); + tri.push_back( Vec3I( 3, 0, 2) ); + tri.push_back( Vec3I( 4, 0, 3) ); + tri.push_back( Vec3I( 5, 0, 4) ); + tri.push_back( Vec3I( 1, 0, 5) ); + tri.push_back( Vec3I( 6, 1, 5) ); + tri.push_back( Vec3I( 7, 5, 4) ); + tri.push_back( Vec3I( 8, 4, 3) ); + tri.push_back( Vec3I( 9, 3, 2) ); + tri.push_back( Vec3I(10, 2, 1) ); + tri.push_back( Vec3I(10, 1, 6) ); + tri.push_back( Vec3I( 6, 5, 7) ); + tri.push_back( Vec3I( 7, 4, 8) ); + tri.push_back( Vec3I( 8, 3, 9) ); + tri.push_back( Vec3I( 9, 2, 10) ); + tri.push_back( Vec3I( 6, 11, 10) ); + tri.push_back( Vec3I(10, 11, 9) ); + tri.push_back( Vec3I( 9, 11, 8) ); + tri.push_back( Vec3I( 8, 11, 7) ); + tri.push_back( Vec3I( 7, 11, 6) ); + + } else { + OPENVDB_THROW(RuntimeError, "Invalid face count"); + } + + // Apply scale and translation to all the vertices + for ( size_t i = 0; i(tmp, *xform, vtx, tri, qua, halfWidth); + } else { + grid = meshToLevelSet(*interrupt, *xform, vtx, tri, qua, halfWidth); + } + + return grid; +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETPLATONIC_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetRebuild.h b/openvdb/tools/LevelSetRebuild.h new file mode 100644 index 00000000..520c8b1e --- /dev/null +++ b/openvdb/tools/LevelSetRebuild.h @@ -0,0 +1,327 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Return a new grid of type @c GridType that contains a narrow-band level set +/// representation of an isosurface of a given grid. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param isovalue the isovalue that defines the implicit surface (defaults to zero, +/// which is typical if the input grid is already a level set or a SDF). +/// @param halfWidth half the width of the narrow band, in voxel units +/// (defaults to 3 voxels, which is required for some level set operations) +/// @param xform optional transform for the output grid +/// (if not provided, the transform of the input @a grid will be matched) +/// +/// @throw TypeError if @a grid is not scalar or not floating-point +/// +/// @note If the input grid contains overlapping isosurfaces, interior edges will be lost. +template +inline typename GridType::Ptr +levelSetRebuild(const GridType& grid, float isovalue = 0, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), const math::Transform* xform = nullptr); + + +/// @brief Return a new grid of type @c GridType that contains a narrow-band level set +/// representation of an isosurface of a given grid. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param isovalue the isovalue that defines the implicit surface +/// @param exBandWidth the exterior narrow-band width in voxel units +/// @param inBandWidth the interior narrow-band width in voxel units +/// @param xform optional transform for the output grid +/// (if not provided, the transform of the input @a grid will be matched) +/// +/// @throw TypeError if @a grid is not scalar or not floating-point +/// +/// @note If the input grid contains overlapping isosurfaces, interior edges will be lost. +template +inline typename GridType::Ptr +levelSetRebuild(const GridType& grid, float isovalue, float exBandWidth, float inBandWidth, + const math::Transform* xform = nullptr); + + +/// @brief Return a new grid of type @c GridType that contains a narrow-band level set +/// representation of an isosurface of a given grid. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param isovalue the isovalue that defines the implicit surface +/// @param exBandWidth the exterior narrow-band width in voxel units +/// @param inBandWidth the interior narrow-band width in voxel units +/// @param xform optional transform for the output grid +/// (if not provided, the transform of the input @a grid will be matched) +/// @param interrupter optional interrupter object +/// +/// @throw TypeError if @a grid is not scalar or not floating-point +/// +/// @note If the input grid contains overlapping isosurfaces, interior edges will be lost. +template +inline typename GridType::Ptr +levelSetRebuild(const GridType& grid, float isovalue, float exBandWidth, float inBandWidth, + const math::Transform* xform = nullptr, InterruptT* interrupter = nullptr); + + +//////////////////////////////////////// + + +// Internal utility objects and implementation details + +namespace internal { + +class PointListTransform +{ +public: + PointListTransform(const PointList& pointsIn, std::vector& pointsOut, + const math::Transform& xform) + : mPointsIn(pointsIn) + , mPointsOut(&pointsOut) + , mXform(xform) + { + } + + void runParallel() + { + tbb::parallel_for(tbb::blocked_range(0, mPointsOut->size()), *this); + } + + void runSerial() + { + (*this)(tbb::blocked_range(0, mPointsOut->size())); + } + + inline void operator()(const tbb::blocked_range& range) const + { + for (size_t n = range.begin(); n < range.end(); ++n) { + (*mPointsOut)[n] = Vec3s(mXform.worldToIndex(mPointsIn[n])); + } + } + +private: + const PointList& mPointsIn; + std::vector * const mPointsOut; + const math::Transform& mXform; +}; + + +class PrimCpy +{ +public: + PrimCpy(const PolygonPoolList& primsIn, const std::vector& indexList, + std::vector& primsOut) + : mPrimsIn(primsIn) + , mIndexList(indexList) + , mPrimsOut(&primsOut) + { + } + + void runParallel() + { + tbb::parallel_for(tbb::blocked_range(0, mIndexList.size()), *this); + } + + void runSerial() + { + (*this)(tbb::blocked_range(0, mIndexList.size())); + } + + inline void operator()(const tbb::blocked_range& range) const + { + openvdb::Vec4I quad; + quad[3] = openvdb::util::INVALID_IDX; + std::vector& primsOut = *mPrimsOut; + + for (size_t n = range.begin(); n < range.end(); ++n) { + size_t index = mIndexList[n]; + PolygonPool& polygons = mPrimsIn[n]; + + // Copy quads + for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) { + primsOut[index++] = polygons.quad(i); + } + polygons.clearQuads(); + + // Copy triangles (adaptive mesh) + for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) { + const openvdb::Vec3I& triangle = polygons.triangle(i); + quad[0] = triangle[0]; + quad[1] = triangle[1]; + quad[2] = triangle[2]; + primsOut[index++] = quad; + } + + polygons.clearTriangles(); + } + } + +private: + const PolygonPoolList& mPrimsIn; + const std::vector& mIndexList; + std::vector * const mPrimsOut; +}; + +} // namespace internal + + +//////////////////////////////////////// + + +//{ +/// @cond OPENVDB_LEVEL_SET_REBUILD_INTERNAL + +/// The normal entry points for level set rebuild are the levelSetRebuild() functions. +/// doLevelSetRebuild() is mainly for internal use, but when the isovalue and half band +/// widths are given in ValueType units (for example, if they are queried from +/// a grid), it might be more convenient to call this function directly. +/// +/// @internal This overload is enabled only for grids with a scalar, floating-point ValueType. +template +inline typename std::enable_if< + std::is_floating_point::value, typename GridType::Ptr>::type +doLevelSetRebuild(const GridType& grid, typename GridType::ValueType iso, + typename GridType::ValueType exWidth, typename GridType::ValueType inWidth, + const math::Transform* xform, InterruptT* interrupter) +{ + const float + isovalue = float(iso), + exBandWidth = float(exWidth), + inBandWidth = float(inWidth); + + tools::VolumeToMesh mesher(isovalue); + mesher(grid); + + math::Transform::Ptr transform = (xform != nullptr) ? xform->copy() : grid.transform().copy(); + + std::vector points(mesher.pointListSize()); + + { // Copy and transform (required for MeshToVolume) points to grid space. + internal::PointListTransform ptnXForm(mesher.pointList(), points, *transform); + ptnXForm.runParallel(); + mesher.pointList().reset(nullptr); + } + + std::vector primitives; + + { // Copy primitives. + PolygonPoolList& polygonPoolList = mesher.polygonPoolList(); + + size_t numPrimitives = 0; + std::vector indexlist(mesher.polygonPoolListSize()); + + for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) { + const openvdb::tools::PolygonPool& polygons = polygonPoolList[n]; + indexlist[n] = numPrimitives; + numPrimitives += polygons.numQuads(); + numPrimitives += polygons.numTriangles(); + } + + primitives.resize(numPrimitives); + internal::PrimCpy primCpy(polygonPoolList, indexlist, primitives); + primCpy.runParallel(); + } + + QuadAndTriangleDataAdapter mesh(points, primitives); + + if (interrupter) { + return meshToVolume(*interrupter, mesh, *transform, exBandWidth, inBandWidth, + DISABLE_RENORMALIZATION, nullptr); + } + + return meshToVolume(mesh, *transform, exBandWidth, inBandWidth, + DISABLE_RENORMALIZATION, nullptr); +} + + +/// @internal This overload is enabled only for grids that do not have a scalar, +/// floating-point ValueType. +template +inline typename std::enable_if< + !std::is_floating_point::value, typename GridType::Ptr>::type +doLevelSetRebuild(const GridType&, typename GridType::ValueType /*isovalue*/, + typename GridType::ValueType /*exWidth*/, typename GridType::ValueType /*inWidth*/, + const math::Transform*, InterruptT*) +{ + OPENVDB_THROW(TypeError, + "level set rebuild is supported only for scalar, floating-point grids"); +} + +/// @endcond +//} + + +//////////////////////////////////////// + + +template +inline typename GridType::Ptr +levelSetRebuild(const GridType& grid, float iso, float exWidth, float inWidth, + const math::Transform* xform, InterruptT* interrupter) +{ + using ValueT = typename GridType::ValueType; + ValueT + isovalue(zeroVal() + ValueT(iso)), + exBandWidth(zeroVal() + ValueT(exWidth)), + inBandWidth(zeroVal() + ValueT(inWidth)); + + return doLevelSetRebuild(grid, isovalue, exBandWidth, inBandWidth, xform, interrupter); +} + + +template +inline typename GridType::Ptr +levelSetRebuild(const GridType& grid, float iso, float exWidth, float inWidth, + const math::Transform* xform) +{ + using ValueT = typename GridType::ValueType; + ValueT + isovalue(zeroVal() + ValueT(iso)), + exBandWidth(zeroVal() + ValueT(exWidth)), + inBandWidth(zeroVal() + ValueT(inWidth)); + + return doLevelSetRebuild( + grid, isovalue, exBandWidth, inBandWidth, xform, nullptr); +} + + +template +inline typename GridType::Ptr +levelSetRebuild(const GridType& grid, float iso, float halfVal, const math::Transform* xform) +{ + using ValueT = typename GridType::ValueType; + ValueT + isovalue(zeroVal() + ValueT(iso)), + halfWidth(zeroVal() + ValueT(halfVal)); + + return doLevelSetRebuild( + grid, isovalue, halfWidth, halfWidth, xform, nullptr); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetSphere.h b/openvdb/tools/LevelSetSphere.h new file mode 100644 index 00000000..3c9d42e6 --- /dev/null +++ b/openvdb/tools/LevelSetSphere.h @@ -0,0 +1,233 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +/// +/// @file LevelSetSphere.h +/// +/// @brief Generate a narrow-band level set of sphere. +/// +/// @note By definition a level set has a fixed narrow band width +/// (the half width is defined by LEVEL_SET_HALF_WIDTH in Types.h), +/// whereas an SDF can have a variable narrow band width. + +#ifndef OPENVDB_TOOLS_LEVELSETSPHERE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETSPHERE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include "SignedFloodFill.h" +#include + +#include +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a sphere. +/// +/// @param radius radius of the sphere in world units +/// @param center center of the sphere in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// @param interrupt a pointer adhering to the util::NullInterrupter interface +/// @param threaded if true multi-threading is enabled (true by default) +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +/// @note The leapfrog algorithm employed in this method is best suited +/// for a single large sphere. For multiple small spheres consider +/// using the faster algorithm in ParticlesToLevelSet.h +template +typename GridType::Ptr +createLevelSetSphere(float radius, const openvdb::Vec3f& center, float voxelSize, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = nullptr, bool threaded = true); + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a sphere. +/// +/// @param radius radius of the sphere in world units +/// @param center center of the sphere in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// @param threaded if true multi-threading is enabled (true by default) +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +/// @note The leapfrog algorithm employed in this method is best suited +/// for a single large sphere. For multiple small spheres consider +/// using the faster algorithm in ParticlesToLevelSet.h +template +typename GridType::Ptr +createLevelSetSphere(float radius, const openvdb::Vec3f& center, float voxelSize, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), bool threaded = true) +{ + return createLevelSetSphere(radius,center,voxelSize,halfWidth,nullptr,threaded); +} + + +//////////////////////////////////////// + + +/// @brief Generates a signed distance field (or narrow band level +/// set) to a single sphere. +/// +/// @note The leapfrog algorithm employed in this class is best +/// suited for a single large sphere. For multiple small spheres consider +/// using the faster algorithm in tools/ParticlesToLevelSet.h +template +class LevelSetSphere +{ +public: + using TreeT = typename GridT::TreeType; + using ValueT = typename GridT::ValueType; + using Vec3T = typename math::Vec3; + static_assert(std::is_floating_point::value, + "level set grids must have scalar, floating-point value types"); + + /// @brief Constructor + /// + /// @param radius radius of the sphere in world units + /// @param center center of the sphere in world units + /// @param interrupt pointer to optional interrupter. Use template + /// argument util::NullInterrupter if no interruption is desired. + /// + /// @note If the radius of the sphere is smaller than + /// 1.5*voxelSize, i.e. the sphere is smaller than the Nyquist + /// frequency of the grid, it is ignored! + LevelSetSphere(ValueT radius, const Vec3T ¢er, InterruptT* interrupt = nullptr) + : mRadius(radius), mCenter(center), mInterrupt(interrupt) + { + if (mRadius<=0) OPENVDB_THROW(ValueError, "radius must be positive"); + } + + /// @return a narrow-band level set of the sphere + /// + /// @param voxelSize Size of voxels in world units + /// @param halfWidth Half-width of narrow-band in voxel units + /// @param threaded If true multi-threading is enabled (true by default) + typename GridT::Ptr getLevelSet(ValueT voxelSize, ValueT halfWidth, bool threaded = true) + { + mGrid = createLevelSet(voxelSize, halfWidth); + this->rasterSphere(voxelSize, halfWidth, threaded); + mGrid->setGridClass(GRID_LEVEL_SET); + return mGrid; + } + +private: + void rasterSphere(ValueT dx, ValueT w, bool threaded) + { + if (!(dx>0.0f)) OPENVDB_THROW(ValueError, "voxel size must be positive"); + if (!(w>1)) OPENVDB_THROW(ValueError, "half-width must be larger than one"); + + // Define radius of sphere and narrow-band in voxel units + const ValueT r0 = mRadius/dx, rmax = r0 + w; + + // Radius below the Nyquist frequency + if (r0 < 1.5f) return; + + // Define center of sphere in voxel units + const Vec3T c(mCenter[0]/dx, mCenter[1]/dx, mCenter[2]/dx); + + // Define bounds of the voxel coordinates + const int imin=math::Floor(c[0]-rmax), imax=math::Ceil(c[0]+rmax); + const int jmin=math::Floor(c[1]-rmax), jmax=math::Ceil(c[1]+rmax); + const int kmin=math::Floor(c[2]-rmax), kmax=math::Ceil(c[2]+rmax); + + // Allocate a ValueAccessor for accelerated random access + typename GridT::Accessor accessor = mGrid->getAccessor(); + + if (mInterrupt) mInterrupt->start("Generating level set of sphere"); + + tbb::enumerable_thread_specific pool(mGrid->tree()); + + auto kernel = [&](const tbb::blocked_range& r) { + openvdb::Coord ijk; + int &i = ijk[0], &j = ijk[1], &k = ijk[2], m=1; + TreeT &tree = pool.local(); + typename GridT::Accessor acc(tree); + // Compute signed distances to sphere using leapfrogging in k + for (i = r.begin(); i <= r.end(); ++i) { + if (util::wasInterrupted(mInterrupt)) return; + const auto x2 = math::Pow2(ValueT(i) - c[0]); + for (j = jmin; j <= jmax; ++j) { + const auto x2y2 = math::Pow2(ValueT(j) - c[1]) + x2; + for (k = kmin; k <= kmax; k += m) { + m = 1; + // Distance in voxel units to sphere + const auto v = math::Sqrt(x2y2 + math::Pow2(ValueT(k)-c[2]))-r0; + const auto d = math::Abs(v); + if (d < w) { // inside narrow band + acc.setValue(ijk, dx*v);// distance in world units + } else { // outside narrow band + m += math::Floor(d-w);// leapfrog + } + }//end leapfrog over k + }//end loop over j + }//end loop over i + };// kernel + + if (threaded) { + // The code blow is making use of a TLS container to minimize the number of concurrent trees + // initially populated by tbb::parallel_for and subsequently merged by tbb::parallel_reduce. + // Experiments have demonstrated this approach to outperform others, including serial reduction + // and a custom concurrent reduction implementation. + tbb::parallel_for(tbb::blocked_range(imin, imax, 128), kernel); + using RangeT = tbb::blocked_range::iterator>; + struct Op { + const bool mDelete; + TreeT *mTree; + Op(TreeT &tree) : mDelete(false), mTree(&tree) {} + Op(const Op& other, tbb::split) : mDelete(true), mTree(new TreeT(other.mTree->background())) {} + ~Op() { if (mDelete) delete mTree; } + void operator()(RangeT &r) { for (auto i=r.begin(); i!=r.end(); ++i) this->merge(*i);} + void join(Op &other) { this->merge(*(other.mTree)); } + void merge(TreeT &tree) { mTree->merge(tree, openvdb::MERGE_ACTIVE_STATES); } + } op( mGrid->tree() ); + tbb::parallel_reduce(RangeT(pool.begin(), pool.end(), 4), op); + } else { + kernel(tbb::blocked_range(imin, imax));//serial + mGrid->tree().merge(*pool.begin(), openvdb::MERGE_ACTIVE_STATES); + } + + // Define consistent signed distances outside the narrow-band + tools::signedFloodFill(mGrid->tree(), threaded); + + if (mInterrupt) mInterrupt->end(); + } + + const ValueT mRadius; + const Vec3T mCenter; + InterruptT* mInterrupt; + typename GridT::Ptr mGrid; +};// LevelSetSphere + + +//////////////////////////////////////// + + +template +typename GridType::Ptr +createLevelSetSphere(float radius, const openvdb::Vec3f& center, float voxelSize, + float halfWidth, InterruptT* interrupt, bool threaded) +{ + // GridType::ValueType is required to be a floating-point scalar. + static_assert(std::is_floating_point::value, + "level set grids must have scalar, floating-point value types"); + + using ValueT = typename GridType::ValueType; + LevelSetSphere factory(ValueT(radius), center, interrupt); + return factory.getLevelSet(ValueT(voxelSize), ValueT(halfWidth), threaded); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETSPHERE_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetTracker.h b/openvdb/tools/LevelSetTracker.h new file mode 100644 index 00000000..f1ea0f5f --- /dev/null +++ b/openvdb/tools/LevelSetTracker.h @@ -0,0 +1,679 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Ken Museth +/// +/// @file tools/LevelSetTracker.h +/// +/// @brief Performs multi-threaded interface tracking of narrow band +/// level sets. This is the building-block for most level set +/// computations that involve dynamic topology, e.g. advection. + +#ifndef OPENVDB_TOOLS_LEVEL_SET_TRACKER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVEL_SET_TRACKER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ChangeBackground.h"// for changeLevelSetBackground +#include "Morphology.h"//for dilateActiveValues +#include "Prune.h"// for pruneLevelSet +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +namespace lstrack { + +/// @brief How to handle voxels that fall outside the narrow band +/// @sa @link LevelSetTracker::trimming() trimming@endlink, +/// @link LevelSetTracker::setTrimming() setTrimming@endlink +enum class TrimMode { + kNone, ///< Leave out-of-band voxels intact + kInterior, ///< Set out-of-band interior voxels to the background value + kExterior, ///< Set out-of-band exterior voxels to the background value + kAll ///< Set all out-of-band voxels to the background value +}; + +} // namespace lstrack + + +/// @brief Performs multi-threaded interface tracking of narrow band level sets +template +class LevelSetTracker +{ +public: + using TrimMode = lstrack::TrimMode; + + using GridType = GridT; + using TreeType = typename GridT::TreeType; + using LeafType = typename TreeType::LeafNodeType; + using ValueType = typename TreeType::ValueType; + using LeafManagerType = typename tree::LeafManager; // leafs + buffers + using LeafRange = typename LeafManagerType::LeafRange; + using BufferType = typename LeafManagerType::BufferType; + using MaskTreeType = typename TreeType::template ValueConverter::Type; + static_assert(std::is_floating_point::value, + "LevelSetTracker requires a level set grid with floating-point values"); + + /// Lightweight struct that stores the state of the LevelSetTracker + struct State { + State(math::BiasedGradientScheme s = math::HJWENO5_BIAS, + math::TemporalIntegrationScheme t = math::TVD_RK1, + int n = static_cast(LEVEL_SET_HALF_WIDTH), int g = 1) + : spatialScheme(s), temporalScheme(t), normCount(n), grainSize(g) {} + math::BiasedGradientScheme spatialScheme; + math::TemporalIntegrationScheme temporalScheme; + int normCount;// Number of iterations of normalization + int grainSize; + }; + + /// @brief Main constructor + /// @throw RuntimeError if the grid is not a level set + LevelSetTracker(GridT& grid, InterruptT* interrupt = nullptr); + + virtual ~LevelSetTracker() { delete mLeafs; } + + /// @brief Iterative normalization, i.e. solving the Eikonal equation + /// @note The mask it optional and by default it is ignored. + template + void normalize(const MaskType* mask); + + /// @brief Iterative normalization, i.e. solving the Eikonal equation + void normalize() { this->normalize(nullptr); } + + /// @brief Track the level set interface, i.e. rebuild and normalize the + /// narrow band of the level set. + void track(); + + /// @brief Set voxels that are outside the narrow band to the background value + /// (if trimming is enabled) and prune the grid. + /// @details Pruning is done automatically as a step in tracking. + /// @sa @link setTrimming() setTrimming@endlink, @link trimming() trimming@endlink + void prune(); + + /// @brief Fast but approximate dilation of the narrow band - one + /// layer at a time. Normally we recommend using the resize method below + /// which internally calls dilate (or erode) with the correct + /// number of @a iterations to achieve the desired half voxel width + /// of the narrow band (3 is recomended for most level set applications). + /// + /// @note Since many level set applications perform + /// interface-tracking, which in turn rebuilds the narrow-band + /// accurately, this dilate method can often be used with a + /// single iterations of low-order re-normalization. This + /// effectively allows very narrow bands to be created from points + /// or polygons (e.g. with a half voxel width of 1), followed by a + /// fast but approximate dilation (typically with a half voxel + /// width of 3). This can be significantly faster than generating + /// the final width of the narrow band from points or polygons. + void dilate(int iterations = 1); + + /// @brief Erodes the width of the narrow-band and update the background values + /// @throw ValueError if @a iterations is larger than the current half-width. + void erode(int iterations = 1); + + /// @brief Resize the width of the narrow band, i.e. perform + /// dilation and renormalization or erosion as required. + bool resize(Index halfWidth = static_cast(LEVEL_SET_HALF_WIDTH)); + + /// @brief Return the half width of the narrow band in floating-point voxel units. + ValueType getHalfWidth() const { return mGrid->background()/mDx; } + + /// @brief Return the state of the tracker (see struct defined above) + State getState() const { return mState; } + + /// @brief Set the state of the tracker (see struct defined above) + void setState(const State& s) { mState = s; } + + /// @return the spatial finite difference scheme + math::BiasedGradientScheme getSpatialScheme() const { return mState.spatialScheme; } + + /// @brief Set the spatial finite difference scheme + void setSpatialScheme(math::BiasedGradientScheme s) { mState.spatialScheme = s; } + + /// @return the temporal integration scheme + math::TemporalIntegrationScheme getTemporalScheme() const { return mState.temporalScheme; } + + /// @brief Set the spatial finite difference scheme + void setTemporalScheme(math::TemporalIntegrationScheme s) { mState.temporalScheme = s;} + + /// @return The number of normalizations performed per track or + /// normalize call. + int getNormCount() const { return mState.normCount; } + + /// @brief Set the number of normalizations performed per track or + /// normalize call. + void setNormCount(int n) { mState.normCount = n; } + + /// @return the grain-size used for multi-threading + int getGrainSize() const { return mState.grainSize; } + + /// @brief Set the grain-size used for multi-threading. + /// @note A grainsize of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mState.grainSize = grainsize; } + + /// @brief Return the trimming mode for voxels outside the narrow band. + /// @details Trimming is enabled by default and is applied automatically prior to pruning. + /// @sa @link setTrimming() setTrimming@endlink, @link prune() prune@endlink + TrimMode trimming() const { return mTrimMode; } + /// @brief Specify whether to trim voxels outside the narrow band prior to pruning. + /// @sa @link trimming() trimming@endlink, @link prune() prune@endlink + void setTrimming(TrimMode mode) { mTrimMode = mode; } + + ValueType voxelSize() const { return mDx; } + + void startInterrupter(const char* msg); + + void endInterrupter(); + + /// @return false if the process was interrupted + bool checkInterrupter(); + + const GridType& grid() const { return *mGrid; } + + LeafManagerType& leafs() { return *mLeafs; } + + const LeafManagerType& leafs() const { return *mLeafs; } + +private: + // disallow copy construction and copy by assignment! + LevelSetTracker(const LevelSetTracker&);// not implemented + LevelSetTracker& operator=(const LevelSetTracker&);// not implemented + + // Private class to perform multi-threaded trimming of + // voxels that are too far away from the zero-crossing. + template + struct Trim + { + Trim(LevelSetTracker& tracker) : mTracker(tracker) {} + void trim(); + void operator()(const LeafRange& r) const; + LevelSetTracker& mTracker; + };// Trim + + // Private struct to perform multi-threaded normalization + template + struct Normalizer + { + using SchemeT = math::BIAS_SCHEME; + using StencilT = typename SchemeT::template ISStencil::StencilType; + using MaskLeafT = typename MaskT::LeafNodeType; + using MaskIterT = typename MaskLeafT::ValueOnCIter; + using VoxelIterT = typename LeafType::ValueOnCIter; + + Normalizer(LevelSetTracker& tracker, const MaskT* mask); + void normalize(); + void operator()(const LeafRange& r) const {mTask(const_cast(this), r);} + void cook(const char* msg, int swapBuffer=0); + template + void euler(const LeafRange& range, Index phiBuffer, Index resultBuffer); + inline void euler01(const LeafRange& r) {this->euler<0,1>(r, 0, 1);} + inline void euler12(const LeafRange& r) {this->euler<1,2>(r, 1, 1);} + inline void euler34(const LeafRange& r) {this->euler<3,4>(r, 1, 2);} + inline void euler13(const LeafRange& r) {this->euler<1,3>(r, 1, 2);} + template + void eval(StencilT& stencil, const ValueType* phi, ValueType* result, Index n) const; + LevelSetTracker& mTracker; + const MaskT* mMask; + const ValueType mDt, mInvDx; + typename std::function mTask; + }; // Normalizer struct + + template + void normalize1(const MaskT* mask); + + template + void normalize2(const MaskT* mask); + + // Throughout the methods below mLeafs is always assumed to contain + // a list of the current LeafNodes! The auxiliary buffers on the + // other hand always have to be allocated locally, since some + // methods need them and others don't! + GridType* mGrid; + LeafManagerType* mLeafs; + InterruptT* mInterrupter; + const ValueType mDx; + State mState; + TrimMode mTrimMode = TrimMode::kAll; +}; // end of LevelSetTracker class + +template +LevelSetTracker:: +LevelSetTracker(GridT& grid, InterruptT* interrupt): + mGrid(&grid), + mLeafs(new LeafManagerType(grid.tree())), + mInterrupter(interrupt), + mDx(static_cast(grid.voxelSize()[0])), + mState() +{ + if ( !grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "The transform must have uniform scale for the LevelSetTracker to function"); + } + if ( grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "LevelSetTracker expected a level set, got a grid of class \"" + + grid.gridClassToString(grid.getGridClass()) + + "\" [hint: Grid::setGridClass(openvdb::GRID_LEVEL_SET)]"); + } +} + +template +inline void +LevelSetTracker:: +prune() +{ + this->startInterrupter("Pruning Level Set"); + + // Set voxels that are too far away from the zero crossing to the background value. + switch (mTrimMode) { + case TrimMode::kNone: break; + case TrimMode::kInterior: Trim(*this).trim(); break; + case TrimMode::kExterior: Trim(*this).trim(); break; + case TrimMode::kAll: Trim(*this).trim(); break; + } + + // Remove inactive nodes from tree + tools::pruneLevelSet(mGrid->tree()); + + // The tree topology has changes so rebuild the list of leafs + mLeafs->rebuildLeafArray(); + this->endInterrupter(); +} + +template +inline void +LevelSetTracker:: +track() +{ + // Dilate narrow-band (this also rebuilds the leaf array!) + tools::dilateActiveValues( *mLeafs, 1, tools::NN_FACE, tools::IGNORE_TILES); + + // Compute signed distances in dilated narrow-band + this->normalize(); + + // Remove voxels that are outside the narrow band + this->prune(); +} + +template +inline void +LevelSetTracker:: +dilate(int iterations) +{ + if (this->getNormCount() == 0) { + for (int i=0; i < iterations; ++i) { + tools::dilateActiveValues( *mLeafs, 1, tools::NN_FACE, tools::IGNORE_TILES); + tools::changeLevelSetBackground(this->leafs(), mDx + mGrid->background()); + } + } else { + for (int i=0; i < iterations; ++i) { + MaskTreeType mask0(mGrid->tree(), false, TopologyCopy()); + tools::dilateActiveValues( *mLeafs, 1, tools::NN_FACE, tools::IGNORE_TILES); + tools::changeLevelSetBackground(this->leafs(), mDx + mGrid->background()); + MaskTreeType mask(mGrid->tree(), false, TopologyCopy()); + mask.topologyDifference(mask0); + this->normalize(&mask); + } + } +} + +template +inline void +LevelSetTracker:: +erode(int iterations) +{ + tools::erodeVoxels(*mLeafs, iterations); + mLeafs->rebuildLeafArray(); + const ValueType background = mGrid->background() - ValueType(iterations) * mDx; + tools::changeLevelSetBackground(this->leafs(), background); +} + +template +inline bool +LevelSetTracker:: +resize(Index halfWidth) +{ + const int wOld = static_cast(math::RoundDown(this->getHalfWidth())); + const int wNew = static_cast(halfWidth); + if (wOld < wNew) { + this->dilate(wNew - wOld); + } else if (wOld > wNew) { + this->erode(wOld - wNew); + } + return wOld != wNew; +} + +template +inline void +LevelSetTracker:: +startInterrupter(const char* msg) +{ + if (mInterrupter) mInterrupter->start(msg); +} + +template +inline void +LevelSetTracker:: +endInterrupter() +{ + if (mInterrupter) mInterrupter->end(); +} + +template +inline bool +LevelSetTracker:: +checkInterrupter() +{ + if (util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return false; + } + return true; +} + +template +template +inline void +LevelSetTracker:: +normalize(const MaskT* mask) +{ + switch (this->getSpatialScheme()) { + case math::FIRST_BIAS: + this->normalize1(mask); break; + case math::SECOND_BIAS: + this->normalize1(mask); break; + case math::THIRD_BIAS: + this->normalize1(mask); break; + case math::WENO5_BIAS: + this->normalize1(mask); break; + case math::HJWENO5_BIAS: + this->normalize1(mask); break; + case math::UNKNOWN_BIAS: + default: + OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); + } +} + +template +template +inline void +LevelSetTracker:: +normalize1(const MaskT* mask) +{ + switch (this->getTemporalScheme()) { + case math::TVD_RK1: + this->normalize2(mask); break; + case math::TVD_RK2: + this->normalize2(mask); break; + case math::TVD_RK3: + this->normalize2(mask); break; + case math::UNKNOWN_TIS: + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + } +} + +template +template +inline void +LevelSetTracker:: +normalize2(const MaskT* mask) +{ + Normalizer tmp(*this, mask); + tmp.normalize(); +} + + +//////////////////////////////////////////////////////////////////////////// + + +template +template +inline void +LevelSetTracker::Trim::trim() +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Trimming != TrimMode::kNone) { + const int grainSize = mTracker.getGrainSize(); + const LeafRange range = mTracker.leafs().leafRange(grainSize); + + if (grainSize>0) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +/// Trim away voxels that have moved outside the narrow band +template +template +inline void +LevelSetTracker::Trim::operator()(const LeafRange& range) const +{ + mTracker.checkInterrupter(); + const ValueType gamma = mTracker.mGrid->background(); + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + for (auto leafIter = range.begin(); leafIter; ++leafIter) { + auto& leaf = *leafIter; + for (auto iter = leaf.beginValueOn(); iter; ++iter) { + const auto val = *iter; + switch (Trimming) { // resolved at compile time + case TrimMode::kNone: + break; + case TrimMode::kInterior: + if (val <= -gamma) { leaf.setValueOff(iter.pos(), -gamma); } + break; + case TrimMode::kExterior: + if (val >= gamma) { leaf.setValueOff(iter.pos(), gamma); } + break; + case TrimMode::kAll: + if (val <= -gamma) { + leaf.setValueOff(iter.pos(), -gamma); + } else if (val >= gamma) { + leaf.setValueOff(iter.pos(), gamma); + } + break; + } + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////////////////////////////////////////// + +template +template +inline +LevelSetTracker:: +Normalizer:: +Normalizer(LevelSetTracker& tracker, const MaskT* mask) + : mTracker(tracker) + , mMask(mask) + , mDt(tracker.voxelSize()*(TemporalScheme == math::TVD_RK1 ? 0.3f : + TemporalScheme == math::TVD_RK2 ? 0.9f : 1.0f)) + , mInvDx(1.0f/tracker.voxelSize()) + , mTask(nullptr) +{ +} + +template +template +inline void +LevelSetTracker:: +Normalizer:: +normalize() +{ + namespace ph = std::placeholders; + + /// Make sure we have enough temporal auxiliary buffers + mTracker.mLeafs->rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1); + + for (int n=0, e=mTracker.getNormCount(); n < e; ++n) { + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + switch(TemporalScheme) {//switch is resolved at compile-time + case math::TVD_RK1: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(0) = Phi_t0(0) - dt * VdotG_t0(1) + mTask = std::bind(&Normalizer::euler01, ph::_1, ph::_2); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Normalizing level set using TVD_RK1", 1); + break; + case math::TVD_RK2: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(1) + mTask = std::bind(&Normalizer::euler01, ph::_1, ph::_2); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Normalizing level set using TVD_RK1 (step 1 of 2)", 1); + + // Convex combine explicit Euler step: t2 = t0 + dt + // Phi_t2(1) = 1/2 * Phi_t0(1) + 1/2 * (Phi_t1(0) - dt * V.Grad_t1(0)) + mTask = std::bind(&Normalizer::euler12, ph::_1, ph::_2); + + // Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1) + this->cook("Normalizing level set using TVD_RK1 (step 2 of 2)", 1); + break; + case math::TVD_RK3: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(1) + mTask = std::bind(&Normalizer::euler01, ph::_1, ph::_2); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Normalizing level set using TVD_RK3 (step 1 of 3)", 1); + + // Convex combine explicit Euler step: t2 = t0 + dt/2 + // Phi_t2(2) = 3/4 * Phi_t0(1) + 1/4 * (Phi_t1(0) - dt * V.Grad_t1(0)) + mTask = std::bind(&Normalizer::euler34, ph::_1, ph::_2); + + // Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2) + this->cook("Normalizing level set using TVD_RK3 (step 2 of 3)", 2); + + // Convex combine explicit Euler step: t3 = t0 + dt + // Phi_t3(2) = 1/3 * Phi_t0(1) + 2/3 * (Phi_t2(0) - dt * V.Grad_t2(0) + mTask = std::bind(&Normalizer::euler13, ph::_1, ph::_2); + + // Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2) + this->cook("Normalizing level set using TVD_RK3 (step 3 of 3)", 2); + break; + case math::UNKNOWN_TIS: + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + mTracker.mLeafs->removeAuxBuffers(); +} + +/// Private method to perform the task (serial or threaded) and +/// subsequently swap the leaf buffers. +template +template +inline void +LevelSetTracker:: +Normalizer:: +cook(const char* msg, int swapBuffer) +{ + mTracker.startInterrupter( msg ); + + const int grainSize = mTracker.getGrainSize(); + const LeafRange range = mTracker.leafs().leafRange(grainSize); + + grainSize>0 ? tbb::parallel_for(range, *this) : (*this)(range); + + mTracker.leafs().swapLeafBuffer(swapBuffer, grainSize==0); + + mTracker.endInterrupter(); +} + +template +template +template +inline void +LevelSetTracker:: +Normalizer:: +eval(StencilT& stencil, const ValueType* phi, ValueType* result, Index n) const +{ + using GradientT = typename math::ISGradientNormSqrd; + static const ValueType alpha = ValueType(Nominator)/ValueType(Denominator); + static const ValueType beta = ValueType(1) - alpha; + + const ValueType normSqGradPhi = GradientT::result(stencil); + const ValueType phi0 = stencil.getValue(); + ValueType v = phi0 / ( math::Sqrt(math::Pow2(phi0) + normSqGradPhi) + + math::Tolerance::value() ); + v = phi0 - mDt * v * (math::Sqrt(normSqGradPhi) * mInvDx - 1.0f); + result[n] = Nominator ? alpha * phi[n] + beta * v : v; +} + +template +template +template +inline void +LevelSetTracker:: +Normalizer:: +euler(const LeafRange& range, Index phiBuffer, Index resultBuffer) +{ + mTracker.checkInterrupter(); + + StencilT stencil(mTracker.grid()); + + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + const ValueType* phi = leafIter.buffer(phiBuffer).data(); + ValueType* result = leafIter.buffer(resultBuffer).data(); + if (mMask == nullptr) { + for (auto iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + this->eval(stencil, phi, result, iter.pos()); + }//loop over active voxels in the leaf of the level set + } else if (const MaskLeafT* mask = mMask->probeLeaf(leafIter->origin())) { + const ValueType* phi0 = leafIter->buffer().data(); + for (MaskIterT iter = mask->cbeginValueOn(); iter; ++iter) { + const Index i = iter.pos(); + stencil.moveTo(iter.getCoord(), phi0[i]); + this->eval(stencil, phi, result, i); + }//loop over active voxels in the leaf of the mask + } + }//loop over leafs of the level set +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVEL_SET_TRACKER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/LevelSetUtil.h b/openvdb/tools/LevelSetUtil.h new file mode 100644 index 00000000..434c6cc7 --- /dev/null +++ b/openvdb/tools/LevelSetUtil.h @@ -0,0 +1,2599 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file tools/LevelSetUtil.h +/// +/// @brief Miscellaneous utility methods that operate primarily +/// or exclusively on level set grids. +/// +/// @author Mihai Alden + +#ifndef OPENVDB_TOOLS_LEVEL_SET_UTIL_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVEL_SET_UTIL_HAS_BEEN_INCLUDED + +#include "MeshToVolume.h" // for traceExteriorBoundaries +#include "SignedFloodFill.h" // for signedFloodFillWithValues + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +// MS Visual C++ requires this extra level of indirection in order to compile +// THIS MUST EXIST IN AN UNNAMED NAMESPACE IN ORDER TO COMPILE ON WINDOWS +namespace { + +template +inline typename GridType::ValueType lsutilGridMax() +{ + return std::numeric_limits::max(); +} + +template +inline typename GridType::ValueType lsutilGridZero() +{ + return zeroVal(); +} + +} // unnamed namespace + + +//////////////////////////////////////// + + +/// @brief Threaded method to convert a sparse level set/SDF into a sparse fog volume +/// +/// @details For a level set, the active and negative-valued interior half of the +/// narrow band becomes a linear ramp from 0 to 1; the inactive interior becomes +/// active with a constant value of 1; and the exterior, including the background +/// and the active exterior half of the narrow band, becomes inactive with a constant +/// value of 0. The interior, though active, remains sparse. +/// @details For a generic SDF, a specified cutoff distance determines the width +/// of the ramp, but otherwise the result is the same as for a level set. +/// +/// @param grid level set/SDF grid to transform +/// @param cutoffDistance optional world space cutoff distance for the ramp +/// (automatically clamped if greater than the interior +/// narrow band width) +template +inline void +sdfToFogVolume( + GridType& grid, + typename GridType::ValueType cutoffDistance = lsutilGridMax()); + + +/// @brief Threaded method to construct a boolean mask that represents interior regions +/// in a signed distance field. +/// +/// @return A shared pointer to either a boolean grid or tree with the same tree +/// configuration and potentially transform as the input @c volume and whose active +/// and @c true values correspond to the interior of the input signed distance field. +/// +/// @param volume Signed distance field / level set volume. +/// @param isovalue Threshold below which values are considered part of the +/// interior region. +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +sdfInteriorMask( + const GridOrTreeType& volume, + typename GridOrTreeType::ValueType isovalue = lsutilGridZero()); + + +/// @brief Extracts the interior regions of a signed distance field and topologically enclosed +/// (watertight) regions of value greater than the @a isovalue (cavities) that can arise +/// as the result of CSG union operations between different shapes where at least one of +/// the shapes has a concavity that is capped. +/// +/// For example the enclosed region of a capped bottle would include the walls and +/// the interior cavity. +/// +/// @return A shared pointer to either a boolean grid or tree with the same tree configuration +/// and potentially transform as the input @c volume and whose active and @c true values +/// correspond to the interior and enclosed regions in the input signed distance field. +/// +/// @param volume Signed distance field / level set volume. +/// @param isovalue Threshold below which values are considered part of the interior region. +/// @param fillMask Optional boolean tree, when provided enclosed cavity regions that are not +/// completely filled by this mask are ignored. +/// +/// For instance if the fill mask does not completely fill the bottle in the +/// previous example only the walls and cap are returned and the interior +/// cavity will be ignored. +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +extractEnclosedRegion( + const GridOrTreeType& volume, + typename GridOrTreeType::ValueType isovalue = lsutilGridZero(), + const typename TreeAdapter::TreeType::template ValueConverter::Type* + fillMask = nullptr); + + +/// @brief Return a mask of the voxels that intersect the implicit surface with +/// the given @a isovalue. +/// +/// @param volume Signed distance field / level set volume. +/// @param isovalue The crossing point that is considered the surface. +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +extractIsosurfaceMask(const GridOrTreeType& volume, typename GridOrTreeType::ValueType isovalue); + + +/// @brief Return a mask for each connected component of the given grid's active voxels. +/// +/// @param volume Input grid or tree +/// @param masks Output set of disjoint active topology masks sorted in descending order +/// based on the active voxel count. +template +inline void +extractActiveVoxelSegmentMasks(const GridOrTreeType& volume, + std::vector::Type::Ptr>& masks); + + +/// @brief Separates disjoint active topology components into distinct grids or trees. +/// +/// @details Supports volumes with active tiles. +/// +/// @param volume Input grid or tree +/// @param segments Output set of disjoint active topology components sorted in +/// descending order based on the active voxel count. +template +inline void +segmentActiveVoxels(const GridOrTreeType& volume, + std::vector& segments); + + +/// @brief Separates disjoint SDF surfaces into distinct grids or trees. +/// +/// @details Supports asymmetric interior / exterior narrowband widths and +/// SDF volumes with dense interior regions. +/// +/// @param volume Input signed distance field / level set volume +/// @param segments Output set of disjoint SDF surfaces found in @a volume sorted in +/// descending order based on the surface intersecting voxel count. +template +inline void +segmentSDF(const GridOrTreeType& volume, std::vector& segments); + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// Internal utility objects and implementation details + + +namespace level_set_util_internal { + + +template +struct MaskInteriorVoxels { + + using ValueType = typename LeafNodeType::ValueType; + using BoolLeafNodeType = tree::LeafNode; + + MaskInteriorVoxels( + ValueType isovalue, const LeafNodeType ** nodes, BoolLeafNodeType ** maskNodes) + : mNodes(nodes), mMaskNodes(maskNodes), mIsovalue(isovalue) + { + } + + void operator()(const tbb::blocked_range& range) const { + + BoolLeafNodeType * maskNodePt = nullptr; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + mMaskNodes[n] = nullptr; + const LeafNodeType& node = *mNodes[n]; + + if (!maskNodePt) { + maskNodePt = new BoolLeafNodeType(node.origin(), false); + } else { + maskNodePt->setOrigin(node.origin()); + } + + const ValueType* values = &node.getValue(0); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + if (values[i] < mIsovalue) maskNodePt->setValueOn(i, true); + } + + if (maskNodePt->onVoxelCount() > 0) { + mMaskNodes[n] = maskNodePt; + maskNodePt = nullptr; + } + } + + if (maskNodePt) delete maskNodePt; + } + + LeafNodeType const * const * const mNodes; + BoolLeafNodeType ** const mMaskNodes; + ValueType const mIsovalue; +}; // MaskInteriorVoxels + + +template +struct MaskInteriorTiles { + + using ValueType = typename TreeType::ValueType; + + MaskInteriorTiles(ValueType isovalue, const TreeType& tree, InternalNodeType ** maskNodes) + : mTree(&tree), mMaskNodes(maskNodes), mIsovalue(isovalue) { } + + void operator()(const tbb::blocked_range& range) const { + tree::ValueAccessor acc(*mTree); + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + typename InternalNodeType::ValueAllIter it = mMaskNodes[n]->beginValueAll(); + for (; it; ++it) { + if (acc.getValue(it.getCoord()) < mIsovalue) { + it.setValue(true); + it.setValueOn(true); + } + } + } + } + + TreeType const * const mTree; + InternalNodeType ** const mMaskNodes; + ValueType const mIsovalue; +}; // MaskInteriorTiles + + +template +struct PopulateTree { + + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + + PopulateTree(TreeType& tree, LeafNodeType** leafnodes, + const size_t * nodexIndexMap, ValueType background) + : mNewTree(background) + , mTreePt(&tree) + , mNodes(leafnodes) + , mNodeIndexMap(nodexIndexMap) + { + } + + PopulateTree(PopulateTree& rhs, tbb::split) + : mNewTree(rhs.mNewTree.background()) + , mTreePt(&mNewTree) + , mNodes(rhs.mNodes) + , mNodeIndexMap(rhs.mNodeIndexMap) + { + } + + void operator()(const tbb::blocked_range& range) { + + tree::ValueAccessor acc(*mTreePt); + + if (mNodeIndexMap) { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + for (size_t i = mNodeIndexMap[n], I = mNodeIndexMap[n + 1]; i < I; ++i) { + if (mNodes[i] != nullptr) acc.addLeaf(mNodes[i]); + } + } + } else { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + acc.addLeaf(mNodes[n]); + } + } + } + + void join(PopulateTree& rhs) { mTreePt->merge(*rhs.mTreePt); } + +private: + TreeType mNewTree; + TreeType * const mTreePt; + LeafNodeType ** const mNodes; + size_t const * const mNodeIndexMap; +}; // PopulateTree + + +/// @brief Negative active values are set @c 0, everything else is set to @c 1. +template +struct LabelBoundaryVoxels { + + using ValueType = typename LeafNodeType::ValueType; + using CharLeafNodeType = tree::LeafNode; + + LabelBoundaryVoxels( + ValueType isovalue, const LeafNodeType ** nodes, CharLeafNodeType ** maskNodes) + : mNodes(nodes), mMaskNodes(maskNodes), mIsovalue(isovalue) + { + } + + void operator()(const tbb::blocked_range& range) const { + + CharLeafNodeType * maskNodePt = nullptr; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + mMaskNodes[n] = nullptr; + const LeafNodeType& node = *mNodes[n]; + + if (!maskNodePt) { + maskNodePt = new CharLeafNodeType(node.origin(), 1); + } else { + maskNodePt->setOrigin(node.origin()); + } + + typename LeafNodeType::ValueOnCIter it; + for (it = node.cbeginValueOn(); it; ++it) { + maskNodePt->setValueOn(it.pos(), ((*it - mIsovalue) < 0.0) ? 0 : 1); + } + + if (maskNodePt->onVoxelCount() > 0) { + mMaskNodes[n] = maskNodePt; + maskNodePt = nullptr; + } + } + + if (maskNodePt) delete maskNodePt; + } + + LeafNodeType const * const * const mNodes; + CharLeafNodeType ** const mMaskNodes; + ValueType const mIsovalue; +}; // LabelBoundaryVoxels + + +template +struct FlipRegionSign { + using ValueType = typename LeafNodeType::ValueType; + + FlipRegionSign(LeafNodeType ** nodes) : mNodes(nodes) { } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + ValueType* values = const_cast(&mNodes[n]->getValue(0)); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + values[i] = values[i] < 0 ? 1 : -1; + } + } + } + + LeafNodeType ** const mNodes; +}; // FlipRegionSign + + +template +struct FindMinVoxelValue { + + using ValueType = typename LeafNodeType::ValueType; + + FindMinVoxelValue(LeafNodeType const * const * const leafnodes) + : minValue(std::numeric_limits::max()) + , mNodes(leafnodes) + { + } + + FindMinVoxelValue(FindMinVoxelValue& rhs, tbb::split) + : minValue(std::numeric_limits::max()) + , mNodes(rhs.mNodes) + { + } + + void operator()(const tbb::blocked_range& range) { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + const ValueType* data = mNodes[n]->buffer().data(); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + minValue = std::min(minValue, data[i]); + } + } + } + + void join(FindMinVoxelValue& rhs) { minValue = std::min(minValue, rhs.minValue); } + + ValueType minValue; + + LeafNodeType const * const * const mNodes; +}; // FindMinVoxelValue + + +template +struct FindMinTileValue { + + using ValueType = typename InternalNodeType::ValueType; + + FindMinTileValue(InternalNodeType const * const * const nodes) + : minValue(std::numeric_limits::max()) + , mNodes(nodes) + { + } + + FindMinTileValue(FindMinTileValue& rhs, tbb::split) + : minValue(std::numeric_limits::max()) + , mNodes(rhs.mNodes) + { + } + + void operator()(const tbb::blocked_range& range) { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + typename InternalNodeType::ValueAllCIter it = mNodes[n]->beginValueAll(); + for (; it; ++it) { + minValue = std::min(minValue, *it); + } + } + } + + void join(FindMinTileValue& rhs) { minValue = std::min(minValue, rhs.minValue); } + + ValueType minValue; + + InternalNodeType const * const * const mNodes; +}; // FindMinTileValue + + +template +struct SDFVoxelsToFogVolume { + + using ValueType = typename LeafNodeType::ValueType; + + SDFVoxelsToFogVolume(LeafNodeType ** nodes, ValueType cutoffDistance) + : mNodes(nodes), mWeight(ValueType(1.0) / cutoffDistance) + { + } + + void operator()(const tbb::blocked_range& range) const { + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& node = *mNodes[n]; + node.setValuesOff(); + + ValueType* values = node.buffer().data(); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + values[i] = values[i] > ValueType(0.0) ? ValueType(0.0) : values[i] * mWeight; + if (values[i] > ValueType(0.0)) node.setValueOn(i); + } + + if (node.onVoxelCount() == 0) { + delete mNodes[n]; + mNodes[n] = nullptr; + } + } + } + + LeafNodeType ** const mNodes; + ValueType const mWeight; +}; // SDFVoxelsToFogVolume + + +template +struct SDFTilesToFogVolume { + + SDFTilesToFogVolume(const TreeType& tree, InternalNodeType ** nodes) + : mTree(&tree), mNodes(nodes) { } + + void operator()(const tbb::blocked_range& range) const { + + using ValueType = typename TreeType::ValueType; + tree::ValueAccessor acc(*mTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + typename InternalNodeType::ValueAllIter it = mNodes[n]->beginValueAll(); + for (; it; ++it) { + if (acc.getValue(it.getCoord()) < ValueType(0.0)) { + it.setValue(ValueType(1.0)); + it.setValueOn(true); + } + } + } + } + + TreeType const * const mTree; + InternalNodeType ** const mNodes; +}; // SDFTilesToFogVolume + + +template +struct FillMaskBoundary { + + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + FillMaskBoundary(const TreeType& tree, ValueType isovalue, const BoolTreeType& fillMask, + const BoolLeafNodeType ** fillNodes, BoolLeafNodeType ** newNodes) + : mTree(&tree) + , mFillMask(&fillMask) + , mFillNodes(fillNodes) + , mNewNodes(newNodes) + , mIsovalue(isovalue) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor maskAcc(*mFillMask); + tree::ValueAccessor distAcc(*mTree); + + std::unique_ptr valueMask(new char[BoolLeafNodeType::SIZE]); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + mNewNodes[n] = nullptr; + const BoolLeafNodeType& node = *mFillNodes[n]; + const Coord& origin = node.origin(); + + const bool denseNode = node.isDense(); + + // possible early out if the fill mask is dense + if (denseNode) { + + int denseNeighbors = 0; + + const BoolLeafNodeType* neighborNode = + maskAcc.probeConstLeaf(origin.offsetBy(-1, 0, 0)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(BoolLeafNodeType::DIM, 0, 0)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(0, -1, 0)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(0, BoolLeafNodeType::DIM, 0)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(0, 0, -1)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(0, 0, BoolLeafNodeType::DIM)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + if (denseNeighbors == 6) continue; + } + + // rest value mask + memset(valueMask.get(), 0, sizeof(char) * BoolLeafNodeType::SIZE); + + const typename TreeType::LeafNodeType* distNode = distAcc.probeConstLeaf(origin); + + // check internal voxel neighbors + + bool earlyTermination = false; + + if (!denseNode) { + if (distNode) { + evalInternalNeighborsP(valueMask.get(), node, *distNode); + evalInternalNeighborsN(valueMask.get(), node, *distNode); + } else if (distAcc.getValue(origin) > mIsovalue) { + earlyTermination = evalInternalNeighborsP(valueMask.get(), node); + if (!earlyTermination) { + earlyTermination = evalInternalNeighborsN(valueMask.get(), node); + } + } + } + + // check external voxel neighbors + + if (!earlyTermination) { + evalExternalNeighborsX(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsX(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsY(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsY(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsZ(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsZ(valueMask.get(), node, maskAcc, distAcc); + } + + // Export marked boundary voxels. + + int numBoundaryValues = 0; + for (Index i = 0, I = BoolLeafNodeType::SIZE; i < I; ++i) { + numBoundaryValues += valueMask[i] == 1; + } + + if (numBoundaryValues > 0) { + mNewNodes[n] = new BoolLeafNodeType(origin, false); + for (Index i = 0, I = BoolLeafNodeType::SIZE; i < I; ++i) { + if (valueMask[i] == 1) mNewNodes[n]->setValueOn(i); + } + } + } + } + +private: + // Check internal voxel neighbors in positive {x, y, z} directions. + void evalInternalNeighborsP(char* valueMask, const BoolLeafNodeType& node, + const LeafNodeType& distNode) const + { + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM - 1; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos + 1) && distNode.getValue(pos + 1) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM - 1; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos + BoolLeafNodeType::DIM) && + distNode.getValue(pos + BoolLeafNodeType::DIM) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM - 1; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos + BoolLeafNodeType::DIM * BoolLeafNodeType::DIM) && + (distNode.getValue(pos + BoolLeafNodeType::DIM * BoolLeafNodeType::DIM) + > mIsovalue)) + { + valueMask[pos] = 1; + } + } + } + } + } + + bool evalInternalNeighborsP(char* valueMask, const BoolLeafNodeType& node) const { + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM - 1; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && !node.isValueOn(pos + 1)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM - 1; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && !node.isValueOn(pos + BoolLeafNodeType::DIM)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM - 1; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && + !node.isValueOn(pos + BoolLeafNodeType::DIM * BoolLeafNodeType::DIM)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + return false; + } + + // Check internal voxel neighbors in negative {x, y, z} directions. + + void evalInternalNeighborsN(char* valueMask, const BoolLeafNodeType& node, + const LeafNodeType& distNode) const + { + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 1; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos - 1) && distNode.getValue(pos - 1) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 1; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos - BoolLeafNodeType::DIM) && + distNode.getValue(pos - BoolLeafNodeType::DIM) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + + for (Index x = 1; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos - BoolLeafNodeType::DIM * BoolLeafNodeType::DIM) && + (distNode.getValue(pos - BoolLeafNodeType::DIM * BoolLeafNodeType::DIM) + > mIsovalue)) + { + valueMask[pos] = 1; + } + } + } + } + } + + + bool evalInternalNeighborsN(char* valueMask, const BoolLeafNodeType& node) const { + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 1; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && !node.isValueOn(pos - 1)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 1; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && !node.isValueOn(pos - BoolLeafNodeType::DIM)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + for (Index x = 1; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && + !node.isValueOn(pos - BoolLeafNodeType::DIM * BoolLeafNodeType::DIM)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + return false; + } + + + // Check external voxel neighbors + + // If UpWind is true check the X+ oriented node face, else the X- oriented face. + template + void evalExternalNeighborsX(char* valueMask, const BoolLeafNodeType& node, + const tree::ValueAccessor& maskAcc, + const tree::ValueAccessor& distAcc) const { + + const Coord& origin = node.origin(); + Coord ijk(0, 0, 0), nijk; + int step = -1; + + if (UpWind) { + step = 1; + ijk[0] = int(BoolLeafNodeType::DIM) - 1; + } + + const Index xPos = ijk[0] << (2 * int(BoolLeafNodeType::LOG2DIM)); + + for (ijk[1] = 0; ijk[1] < int(BoolLeafNodeType::DIM); ++ijk[1]) { + const Index yPos = xPos + (ijk[1] << int(BoolLeafNodeType::LOG2DIM)); + + for (ijk[2] = 0; ijk[2] < int(BoolLeafNodeType::DIM); ++ijk[2]) { + const Index pos = yPos + ijk[2]; + + if (valueMask[pos] == 0 && node.isValueOn(pos)) { + + nijk = origin + ijk.offsetBy(step, 0, 0); + + if (!maskAcc.isValueOn(nijk) && distAcc.getValue(nijk) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + } + + // If UpWind is true check the Y+ oriented node face, else the Y- oriented face. + template + void evalExternalNeighborsY(char* valueMask, const BoolLeafNodeType& node, + const tree::ValueAccessor& maskAcc, + const tree::ValueAccessor& distAcc) const { + + const Coord& origin = node.origin(); + Coord ijk(0, 0, 0), nijk; + int step = -1; + + if (UpWind) { + step = 1; + ijk[1] = int(BoolLeafNodeType::DIM) - 1; + } + + const Index yPos = ijk[1] << int(BoolLeafNodeType::LOG2DIM); + + for (ijk[0] = 0; ijk[0] < int(BoolLeafNodeType::DIM); ++ijk[0]) { + const Index xPos = yPos + (ijk[0] << (2 * int(BoolLeafNodeType::LOG2DIM))); + + for (ijk[2] = 0; ijk[2] < int(BoolLeafNodeType::DIM); ++ijk[2]) { + const Index pos = xPos + ijk[2]; + + if (valueMask[pos] == 0 && node.isValueOn(pos)) { + + nijk = origin + ijk.offsetBy(0, step, 0); + if (!maskAcc.isValueOn(nijk) && distAcc.getValue(nijk) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + } + + // If UpWind is true check the Z+ oriented node face, else the Z- oriented face. + template + void evalExternalNeighborsZ(char* valueMask, const BoolLeafNodeType& node, + const tree::ValueAccessor& maskAcc, + const tree::ValueAccessor& distAcc) const { + + const Coord& origin = node.origin(); + Coord ijk(0, 0, 0), nijk; + int step = -1; + + if (UpWind) { + step = 1; + ijk[2] = int(BoolLeafNodeType::DIM) - 1; + } + + for (ijk[0] = 0; ijk[0] < int(BoolLeafNodeType::DIM); ++ijk[0]) { + const Index xPos = ijk[0] << (2 * int(BoolLeafNodeType::LOG2DIM)); + + for (ijk[1] = 0; ijk[1] < int(BoolLeafNodeType::DIM); ++ijk[1]) { + const Index pos = ijk[2] + xPos + (ijk[1] << int(BoolLeafNodeType::LOG2DIM)); + + if (valueMask[pos] == 0 && node.isValueOn(pos)) { + + nijk = origin + ijk.offsetBy(0, 0, step); + if (!maskAcc.isValueOn(nijk) && distAcc.getValue(nijk) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + } + + ////////// + + TreeType const * const mTree; + BoolTreeType const * const mFillMask; + BoolLeafNodeType const * const * const mFillNodes; + BoolLeafNodeType ** const mNewNodes; + ValueType const mIsovalue; +}; // FillMaskBoundary + + +/// @brief Constructs a memory light char tree that represents the exterior region with @c +1 +/// and the interior regions with @c -1. +template +inline typename TreeType::template ValueConverter::Type::Ptr +computeEnclosedRegionMask(const TreeType& tree, typename TreeType::ValueType isovalue, + const typename TreeType::template ValueConverter::Type* fillMask) +{ + using LeafNodeType = typename TreeType::LeafNodeType; + using RootNodeType = typename TreeType::RootNodeType; + using NodeChainType = typename RootNodeType::NodeChainType; + using InternalNodeType = typename boost::mpl::at>::type; + + using CharTreeType = typename TreeType::template ValueConverter::Type; + using CharLeafNodeType = typename CharTreeType::LeafNodeType; + + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + const TreeType* treePt = &tree; + + size_t numLeafNodes = 0, numInternalNodes = 0; + + std::vector nodes; + std::vector leafnodeCount; + + { + // compute the prefix sum of the leafnode count in each internal node. + std::vector internalNodes; + treePt->getNodes(internalNodes); + + numInternalNodes = internalNodes.size(); + + leafnodeCount.push_back(0); + for (size_t n = 0; n < numInternalNodes; ++n) { + leafnodeCount.push_back(leafnodeCount.back() + internalNodes[n]->leafCount()); + } + + numLeafNodes = leafnodeCount.back(); + + // extract all leafnodes + nodes.reserve(numLeafNodes); + + for (size_t n = 0; n < numInternalNodes; ++n) { + internalNodes[n]->getNodes(nodes); + } + } + + // create mask leafnodes + std::unique_ptr maskNodes(new CharLeafNodeType*[numLeafNodes]); + + tbb::parallel_for(tbb::blocked_range(0, numLeafNodes), + LabelBoundaryVoxels(isovalue, &nodes[0], maskNodes.get())); + + // create mask grid + typename CharTreeType::Ptr maskTree(new CharTreeType(1)); + + PopulateTree populate(*maskTree, maskNodes.get(), &leafnodeCount[0], 1); + tbb::parallel_reduce(tbb::blocked_range(0, numInternalNodes), populate); + + // optionally evaluate the fill mask + + std::vector extraMaskNodes; + + if (fillMask) { + + std::vector fillMaskNodes; + fillMask->getNodes(fillMaskNodes); + + std::unique_ptr boundaryMaskNodes( + new BoolLeafNodeType*[fillMaskNodes.size()]); + + tbb::parallel_for(tbb::blocked_range(0, fillMaskNodes.size()), + FillMaskBoundary(tree, isovalue, *fillMask, &fillMaskNodes[0], + boundaryMaskNodes.get())); + + tree::ValueAccessor maskAcc(*maskTree); + + for (size_t n = 0, N = fillMaskNodes.size(); n < N; ++n) { + + if (boundaryMaskNodes[n] == nullptr) continue; + + const BoolLeafNodeType& boundaryNode = *boundaryMaskNodes[n]; + const Coord& origin = boundaryNode.origin(); + + CharLeafNodeType* maskNodePt = maskAcc.probeLeaf(origin); + + if (!maskNodePt) { + maskNodePt = maskAcc.touchLeaf(origin); + extraMaskNodes.push_back(maskNodePt); + } + + char* data = maskNodePt->buffer().data(); + + typename BoolLeafNodeType::ValueOnCIter it = boundaryNode.cbeginValueOn(); + for (; it; ++it) { + if (data[it.pos()] != 0) data[it.pos()] = -1; + } + + delete boundaryMaskNodes[n]; + } + } + + // eliminate enclosed regions + tools::traceExteriorBoundaries(*maskTree); + + // flip voxel sign to negative inside and positive outside. + tbb::parallel_for(tbb::blocked_range(0, numLeafNodes), + FlipRegionSign(maskNodes.get())); + + if (!extraMaskNodes.empty()) { + tbb::parallel_for(tbb::blocked_range(0, extraMaskNodes.size()), + FlipRegionSign(&extraMaskNodes[0])); + } + + // propagate sign information into tile region + tools::signedFloodFill(*maskTree); + + return maskTree; +} // computeEnclosedRegionMask() + + +template +inline typename TreeType::template ValueConverter::Type::Ptr +computeInteriorMask(const TreeType& tree, typename TreeType::ValueType iso) +{ + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using RootNodeType = typename TreeType::RootNodeType; + using NodeChainType = typename RootNodeType::NodeChainType; + using InternalNodeType = typename boost::mpl::at >::type; + + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + using BoolRootNodeType = typename BoolTreeType::RootNodeType; + using BoolNodeChainType = typename BoolRootNodeType::NodeChainType; + using BoolInternalNodeType = + typename boost::mpl::at>::type; + + ///// + + // Clamp the isovalue to the level set's background value minus epsilon. + // (In a valid narrow-band level set, all voxels, including background voxels, + // have values less than or equal to the background value, so an isovalue + // greater than or equal to the background value would produce a mask with + // effectively infinite extent.) + iso = std::min(iso, + static_cast(tree.background() - math::Tolerance::value())); + + size_t numLeafNodes = 0, numInternalNodes = 0; + + std::vector nodes; + std::vector leafnodeCount; + + { + // compute the prefix sum of the leafnode count in each internal node. + std::vector internalNodes; + tree.getNodes(internalNodes); + + numInternalNodes = internalNodes.size(); + + leafnodeCount.push_back(0); + for (size_t n = 0; n < numInternalNodes; ++n) { + leafnodeCount.push_back(leafnodeCount.back() + internalNodes[n]->leafCount()); + } + + numLeafNodes = leafnodeCount.back(); + + // extract all leafnodes + nodes.reserve(numLeafNodes); + + for (size_t n = 0; n < numInternalNodes; ++n) { + internalNodes[n]->getNodes(nodes); + } + } + + // create mask leafnodes + std::unique_ptr maskNodes(new BoolLeafNodeType*[numLeafNodes]); + + tbb::parallel_for(tbb::blocked_range(0, numLeafNodes), + MaskInteriorVoxels(iso, &nodes[0], maskNodes.get())); + + + // create mask grid + typename BoolTreeType::Ptr maskTree(new BoolTreeType(false)); + + PopulateTree populate(*maskTree, maskNodes.get(), &leafnodeCount[0], false); + tbb::parallel_reduce(tbb::blocked_range(0, numInternalNodes), populate); + + + // evaluate tile values + std::vector internalMaskNodes; + maskTree->getNodes(internalMaskNodes); + + tbb::parallel_for(tbb::blocked_range(0, internalMaskNodes.size()), + MaskInteriorTiles(iso, tree, &internalMaskNodes[0])); + + tree::ValueAccessor acc(tree); + + typename BoolTreeType::ValueAllIter it(*maskTree); + it.setMaxDepth(BoolTreeType::ValueAllIter::LEAF_DEPTH - 2); + + for ( ; it; ++it) { + if (acc.getValue(it.getCoord()) < iso) { + it.setValue(true); + it.setActiveState(true); + } + } + + return maskTree; +} // computeInteriorMask() + + +template +struct MaskIsovalueCrossingVoxels +{ + using InputValueType = typename InputTreeType::ValueType; + using InputLeafNodeType = typename InputTreeType::LeafNodeType; + using BoolTreeType = typename InputTreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + MaskIsovalueCrossingVoxels( + const InputTreeType& inputTree, + const std::vector& inputLeafNodes, + BoolTreeType& maskTree, + InputValueType iso) + : mInputAccessor(inputTree) + , mInputNodes(!inputLeafNodes.empty() ? &inputLeafNodes.front() : nullptr) + , mMaskTree(false) + , mMaskAccessor(maskTree) + , mIsovalue(iso) + { + } + + MaskIsovalueCrossingVoxels(MaskIsovalueCrossingVoxels& rhs, tbb::split) + : mInputAccessor(rhs.mInputAccessor.tree()) + , mInputNodes(rhs.mInputNodes) + , mMaskTree(false) + , mMaskAccessor(mMaskTree) + , mIsovalue(rhs.mIsovalue) + { + } + + void operator()(const tbb::blocked_range& range) { + + const InputValueType iso = mIsovalue; + Coord ijk(0, 0, 0); + + BoolLeafNodeType* maskNodePt = nullptr; + + for (size_t n = range.begin(); mInputNodes && (n != range.end()); ++n) { + + const InputLeafNodeType& node = *mInputNodes[n]; + + if (!maskNodePt) maskNodePt = new BoolLeafNodeType(node.origin(), false); + else maskNodePt->setOrigin(node.origin()); + + bool collectedData = false; + + for (typename InputLeafNodeType::ValueOnCIter it = node.cbeginValueOn(); it; ++it) { + + bool isUnder = *it < iso; + + ijk = it.getCoord(); + + ++ijk[2]; + bool signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // +z edge + --ijk[2]; + + if (!signChange) { + --ijk[2]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // -z edge + ++ijk[2]; + } + + if (!signChange) { + ++ijk[1]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // +y edge + --ijk[1]; + } + + if (!signChange) { + --ijk[1]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // -y edge + ++ijk[1]; + } + + if (!signChange) { + ++ijk[0]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // +x edge + --ijk[0]; + } + + if (!signChange) { + --ijk[0]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // -x edge + ++ijk[0]; + } + + if (signChange) { + collectedData = true; + maskNodePt->setValueOn(it.pos(), true); + } + } + + if (collectedData) { + mMaskAccessor.addLeaf(maskNodePt); + maskNodePt = nullptr; + } + } + + if (maskNodePt) delete maskNodePt; + } + + void join(MaskIsovalueCrossingVoxels& rhs) { + mMaskAccessor.tree().merge(rhs.mMaskAccessor.tree()); + } + +private: + tree::ValueAccessor mInputAccessor; + InputLeafNodeType const * const * const mInputNodes; + + BoolTreeType mMaskTree; + tree::ValueAccessor mMaskAccessor; + + InputValueType mIsovalue; +}; // MaskIsovalueCrossingVoxels + + +//////////////////////////////////////// + + +template +struct NodeMaskSegment +{ + using Ptr = SharedPtr; + using NodeMaskType = typename NodeType::NodeMaskType; + + NodeMaskSegment() : connections(), mask(false), origin(0,0,0), visited(false) {} + + std::vector connections; + NodeMaskType mask; + Coord origin; + bool visited; +}; // struct NodeMaskSegment + + +template +inline void +nodeMaskSegmentation(const NodeType& node, + std::vector::Ptr>& segments) +{ + using NodeMaskType = typename NodeType::NodeMaskType; + using NodeMaskSegmentType = NodeMaskSegment; + using NodeMaskSegmentTypePtr = typename NodeMaskSegmentType::Ptr; + + NodeMaskType nodeMask(node.getValueMask()); + std::deque indexList; + + while (!nodeMask.isOff()) { + + NodeMaskSegmentTypePtr segment(new NodeMaskSegmentType()); + segment->origin = node.origin(); + + NodeMaskType& mask = segment->mask; + + indexList.push_back(nodeMask.findFirstOn()); + nodeMask.setOff(indexList.back()); // mark as visited + Coord ijk(0, 0, 0); + + while (!indexList.empty()) { + + const Index pos = indexList.back(); + indexList.pop_back(); + + if (mask.isOn(pos)) continue; + mask.setOn(pos); + + ijk = NodeType::offsetToLocalCoord(pos); + + Index npos = pos - 1; + if (ijk[2] != 0 && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + 1; + if (ijk[2] != (NodeType::DIM - 1) && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos - NodeType::DIM; + if (ijk[1] != 0 && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + NodeType::DIM; + if (ijk[1] != (NodeType::DIM - 1) && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos - NodeType::DIM * NodeType::DIM; + if (ijk[0] != 0 && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + NodeType::DIM * NodeType::DIM; + if (ijk[0] != (NodeType::DIM - 1) && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + } + + segments.push_back(segment); + } +} + + +template +struct SegmentNodeMask +{ + using NodeMaskSegmentType = NodeMaskSegment; + using NodeMaskSegmentTypePtr = typename NodeMaskSegmentType::Ptr; + using NodeMaskSegmentVector = typename std::vector; + + SegmentNodeMask(std::vector& nodes, NodeMaskSegmentVector* nodeMaskArray) + : mNodes(!nodes.empty() ? &nodes.front() : nullptr) + , mNodeMaskArray(nodeMaskArray) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + NodeType& node = *mNodes[n]; + nodeMaskSegmentation(node, mNodeMaskArray[n]); + + // hack origin data to store array offset + Coord& origin = const_cast(node.origin()); + origin[0] = static_cast(n); + } + } + + NodeType * const * const mNodes; + NodeMaskSegmentVector * const mNodeMaskArray; +}; // struct SegmentNodeMask + + +template +struct ConnectNodeMaskSegments +{ + using NodeMaskType = typename NodeType::NodeMaskType; + using NodeMaskSegmentType = NodeMaskSegment; + using NodeMaskSegmentTypePtr = typename NodeMaskSegmentType::Ptr; + using NodeMaskSegmentVector = typename std::vector; + + ConnectNodeMaskSegments(const TreeType& tree, NodeMaskSegmentVector* nodeMaskArray) + : mTree(&tree) + , mNodeMaskArray(nodeMaskArray) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor acc(*mTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + NodeMaskSegmentVector& segments = mNodeMaskArray[n]; + if (segments.empty()) continue; + + std::vector > connections(segments.size()); + + Coord ijk = segments[0]->origin; + + const NodeType* node = acc.template probeConstNode(ijk); + if (!node) continue; + + // get neighbour nodes + + ijk[2] += NodeType::DIM; + const NodeType* nodeZUp = acc.template probeConstNode(ijk); + ijk[2] -= (NodeType::DIM + NodeType::DIM); + const NodeType* nodeZDown = acc.template probeConstNode(ijk); + ijk[2] += NodeType::DIM; + + ijk[1] += NodeType::DIM; + const NodeType* nodeYUp = acc.template probeConstNode(ijk); + ijk[1] -= (NodeType::DIM + NodeType::DIM); + const NodeType* nodeYDown = acc.template probeConstNode(ijk); + ijk[1] += NodeType::DIM; + + ijk[0] += NodeType::DIM; + const NodeType* nodeXUp = acc.template probeConstNode(ijk); + ijk[0] -= (NodeType::DIM + NodeType::DIM); + const NodeType* nodeXDown = acc.template probeConstNode(ijk); + ijk[0] += NodeType::DIM; + + const Index startPos = node->getValueMask().findFirstOn(); + for (Index pos = startPos; pos < NodeMaskType::SIZE; ++pos) { + + if (!node->isValueOn(pos)) continue; + + ijk = NodeType::offsetToLocalCoord(pos); + +#ifdef _MSC_FULL_VER + #if _MSC_FULL_VER >= 190000000 && _MSC_FULL_VER < 190024210 + // Visual Studio 2015 had a codegen bug that wasn't fixed until Update 3 + volatile Index npos = 0; + #else + Index npos = 0; + #endif +#else + Index npos = 0; +#endif + + if (ijk[2] == 0) { + npos = pos + (NodeType::DIM - 1); + if (nodeZDown && nodeZDown->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeZDown)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } else if (ijk[2] == (NodeType::DIM - 1)) { + npos = pos - (NodeType::DIM - 1); + if (nodeZUp && nodeZUp->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeZUp)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } + + if (ijk[1] == 0) { + npos = pos + (NodeType::DIM - 1) * NodeType::DIM; + if (nodeYDown && nodeYDown->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeYDown)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } else if (ijk[1] == (NodeType::DIM - 1)) { + npos = pos - (NodeType::DIM - 1) * NodeType::DIM; + if (nodeYUp && nodeYUp->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeYUp)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } + + if (ijk[0] == 0) { + npos = pos + (NodeType::DIM - 1) * NodeType::DIM * NodeType::DIM; + if (nodeXDown && nodeXDown->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeXDown)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } else if (ijk[0] == (NodeType::DIM - 1)) { + npos = pos - (NodeType::DIM - 1) * NodeType::DIM * NodeType::DIM; + if (nodeXUp && nodeXUp->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeXUp)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } + } + + for (size_t i = 0, I = connections.size(); i < I; ++i) { + + typename std::set::iterator + it = connections[i].begin(), end = connections[i].end(); + + std::vector& segmentConnections = segments[i]->connections; + segmentConnections.reserve(connections.size()); + for (; it != end; ++it) { + segmentConnections.push_back(*it); + } + } + } // end range loop + } + +private: + + static inline size_t getNodeOffset(const NodeType& node) { + return static_cast(node.origin()[0]); + } + + static inline NodeMaskSegmentType* + findNodeMaskSegment(NodeMaskSegmentVector& segments, Index pos) + { + NodeMaskSegmentType* segment = nullptr; + + for (size_t n = 0, N = segments.size(); n < N; ++n) { + if (segments[n]->mask.isOn(pos)) { + segment = segments[n].get(); + break; + } + } + + return segment; + } + + static inline Index + findNodeMaskSegmentIndex(NodeMaskSegmentVector& segments, Index pos) + { + for (Index n = 0, N = Index(segments.size()); n < N; ++n) { + if (segments[n]->mask.isOn(pos)) return n; + } + return Index(-1); + } + + TreeType const * const mTree; + NodeMaskSegmentVector * const mNodeMaskArray; +}; // struct ConnectNodeMaskSegments + + +template +struct MaskSegmentGroup +{ + using LeafNodeType = typename TreeType::LeafNodeType; + using TreeTypePtr = typename TreeType::Ptr; + using NodeMaskSegmentType = NodeMaskSegment; + + MaskSegmentGroup(const std::vector& segments) + : mSegments(!segments.empty() ? &segments.front() : nullptr) + , mTree(new TreeType(false)) + { + } + + MaskSegmentGroup(const MaskSegmentGroup& rhs, tbb::split) + : mSegments(rhs.mSegments) + , mTree(new TreeType(false)) + { + } + + TreeTypePtr& mask() { return mTree; } + + void join(MaskSegmentGroup& rhs) { mTree->merge(*rhs.mTree); } + + void operator()(const tbb::blocked_range& range) { + + tree::ValueAccessor acc(*mTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + NodeMaskSegmentType& segment = *mSegments[n]; + LeafNodeType* node = acc.touchLeaf(segment.origin); + node->getValueMask() |= segment.mask; + } + } + +private: + NodeMaskSegmentType * const * const mSegments; + TreeTypePtr mTree; +}; // struct MaskSegmentGroup + + +//////////////////////////////////////// + + +template +struct ExpandLeafNodeRegion +{ + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using NodeMaskType = typename LeafNodeType::NodeMaskType; + + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + ///// + + ExpandLeafNodeRegion(const TreeType& distTree, BoolTreeType& maskTree, + std::vector& maskNodes) + : mDistTree(&distTree) + , mMaskTree(&maskTree) + , mMaskNodes(!maskNodes.empty() ? &maskNodes.front() : nullptr) + , mNewMaskTree(false) + { + } + + ExpandLeafNodeRegion(const ExpandLeafNodeRegion& rhs, tbb::split) + : mDistTree(rhs.mDistTree) + , mMaskTree(rhs.mMaskTree) + , mMaskNodes(rhs.mMaskNodes) + , mNewMaskTree(false) + { + } + + BoolTreeType& newMaskTree() { return mNewMaskTree; } + + void join(ExpandLeafNodeRegion& rhs) { mNewMaskTree.merge(rhs.mNewMaskTree); } + + void operator()(const tbb::blocked_range& range) { + + using NodeType = LeafNodeType; + + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor maskAcc(*mMaskTree); + tree::ValueAccessor newMaskAcc(mNewMaskTree); + + NodeMaskType maskZUp, maskZDown, maskYUp, maskYDown, maskXUp, maskXDown; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolLeafNodeType& maskNode = *mMaskNodes[n]; + if (maskNode.isEmpty()) continue; + + Coord ijk = maskNode.origin(), nijk; + + const LeafNodeType* distNode = distAcc.probeConstLeaf(ijk); + if (!distNode) continue; + + const ValueType *dataZUp = nullptr, *dataZDown = nullptr, + *dataYUp = nullptr, *dataYDown = nullptr, + *dataXUp = nullptr, *dataXDown = nullptr; + + ijk[2] += NodeType::DIM; + getData(ijk, distAcc, maskAcc, maskZUp, dataZUp); + ijk[2] -= (NodeType::DIM + NodeType::DIM); + getData(ijk, distAcc, maskAcc, maskZDown, dataZDown); + ijk[2] += NodeType::DIM; + + ijk[1] += NodeType::DIM; + getData(ijk, distAcc, maskAcc, maskYUp, dataYUp); + ijk[1] -= (NodeType::DIM + NodeType::DIM); + getData(ijk, distAcc, maskAcc, maskYDown, dataYDown); + ijk[1] += NodeType::DIM; + + ijk[0] += NodeType::DIM; + getData(ijk, distAcc, maskAcc, maskXUp, dataXUp); + ijk[0] -= (NodeType::DIM + NodeType::DIM); + getData(ijk, distAcc, maskAcc, maskXDown, dataXDown); + ijk[0] += NodeType::DIM; + + for (typename BoolLeafNodeType::ValueOnIter it = maskNode.beginValueOn(); it; ++it) { + + const Index pos = it.pos(); + const ValueType val = std::abs(distNode->getValue(pos)); + + ijk = BoolLeafNodeType::offsetToLocalCoord(pos); + nijk = ijk + maskNode.origin(); + + if (dataZUp && ijk[2] == (BoolLeafNodeType::DIM - 1)) { + const Index npos = pos - (NodeType::DIM - 1); + if (maskZUp.isOn(npos) && std::abs(dataZUp[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(0, 0, 1)); + } + } else if (dataZDown && ijk[2] == 0) { + const Index npos = pos + (NodeType::DIM - 1); + if (maskZDown.isOn(npos) && std::abs(dataZDown[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(0, 0, -1)); + } + } + + if (dataYUp && ijk[1] == (BoolLeafNodeType::DIM - 1)) { + const Index npos = pos - (NodeType::DIM - 1) * NodeType::DIM; + if (maskYUp.isOn(npos) && std::abs(dataYUp[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(0, 1, 0)); + } + } else if (dataYDown && ijk[1] == 0) { + const Index npos = pos + (NodeType::DIM - 1) * NodeType::DIM; + if (maskYDown.isOn(npos) && std::abs(dataYDown[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(0, -1, 0)); + } + } + + if (dataXUp && ijk[0] == (BoolLeafNodeType::DIM - 1)) { + const Index npos = pos - (NodeType::DIM - 1) * NodeType::DIM * NodeType::DIM; + if (maskXUp.isOn(npos) && std::abs(dataXUp[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(1, 0, 0)); + } + } else if (dataXDown && ijk[0] == 0) { + const Index npos = pos + (NodeType::DIM - 1) * NodeType::DIM * NodeType::DIM; + if (maskXDown.isOn(npos) && std::abs(dataXDown[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(-1, 0, 0)); + } + } + + } // end value on loop + } // end range loop + } + +private: + + static inline void + getData(const Coord& ijk, tree::ValueAccessor& distAcc, + tree::ValueAccessor& maskAcc, NodeMaskType& mask, + const ValueType*& data) + { + const LeafNodeType* node = distAcc.probeConstLeaf(ijk); + if (node) { + data = node->buffer().data(); + mask = node->getValueMask(); + const BoolLeafNodeType* maskNodePt = maskAcc.probeConstLeaf(ijk); + if (maskNodePt) mask -= maskNodePt->getValueMask(); + } + } + + TreeType const * const mDistTree; + BoolTreeType * const mMaskTree; + BoolLeafNodeType ** const mMaskNodes; + + BoolTreeType mNewMaskTree; +}; // struct ExpandLeafNodeRegion + + +template +struct FillLeafNodeVoxels +{ + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using NodeMaskType = typename LeafNodeType::NodeMaskType; + using BoolLeafNodeType = tree::LeafNode; + + FillLeafNodeVoxels(const TreeType& tree, std::vector& maskNodes) + : mTree(&tree), mMaskNodes(!maskNodes.empty() ? &maskNodes.front() : nullptr) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor distAcc(*mTree); + + std::vector indexList; + indexList.reserve(NodeMaskType::SIZE); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolLeafNodeType& maskNode = *mMaskNodes[n]; + + const LeafNodeType * distNode = distAcc.probeConstLeaf(maskNode.origin()); + if (!distNode) continue; + + NodeMaskType mask(distNode->getValueMask()); + NodeMaskType& narrowbandMask = maskNode.getValueMask(); + + for (Index pos = narrowbandMask.findFirstOn(); pos < NodeMaskType::SIZE; ++pos) { + if (narrowbandMask.isOn(pos)) indexList.push_back(pos); + } + + mask -= narrowbandMask; // bitwise difference + narrowbandMask.setOff(); + + const ValueType* data = distNode->buffer().data(); + Coord ijk(0, 0, 0); + + while (!indexList.empty()) { + + const Index pos = indexList.back(); + indexList.pop_back(); + + if (narrowbandMask.isOn(pos)) continue; + narrowbandMask.setOn(pos); + + const ValueType dist = std::abs(data[pos]); + + ijk = LeafNodeType::offsetToLocalCoord(pos); + + Index npos = pos - 1; + if (ijk[2] != 0 && mask.isOn(npos) && std::abs(data[npos]) > dist) { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + 1; + if ((ijk[2] != (LeafNodeType::DIM - 1)) && mask.isOn(npos) + && std::abs(data[npos]) > dist) + { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos - LeafNodeType::DIM; + if (ijk[1] != 0 && mask.isOn(npos) && std::abs(data[npos]) > dist) { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + LeafNodeType::DIM; + if ((ijk[1] != (LeafNodeType::DIM - 1)) && mask.isOn(npos) + && std::abs(data[npos]) > dist) + { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos - LeafNodeType::DIM * LeafNodeType::DIM; + if (ijk[0] != 0 && mask.isOn(npos) && std::abs(data[npos]) > dist) { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + LeafNodeType::DIM * LeafNodeType::DIM; + if ((ijk[0] != (LeafNodeType::DIM - 1)) && mask.isOn(npos) + && std::abs(data[npos]) > dist) + { + mask.setOff(npos); + indexList.push_back(npos); + } + } // end flood fill loop + } // end range loop + } + + TreeType const * const mTree; + BoolLeafNodeType ** const mMaskNodes; +}; // FillLeafNodeVoxels + + +template +struct ExpandNarrowbandMask +{ + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + using BoolTreeTypePtr = typename BoolTreeType::Ptr; + + ExpandNarrowbandMask(const TreeType& tree, std::vector& segments) + : mTree(&tree), mSegments(!segments.empty() ? &segments.front() : nullptr) + { + } + + void operator()(const tbb::blocked_range& range) const { + + const TreeType& distTree = *mTree; + std::vector nodes; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolTreeType& narrowBandMask = *mSegments[n]; + + BoolTreeType candidateMask(narrowBandMask, false, TopologyCopy()); + + while (true) { + + nodes.clear(); + candidateMask.getNodes(nodes); + if (nodes.empty()) break; + + const tbb::blocked_range nodeRange(0, nodes.size()); + + tbb::parallel_for(nodeRange, FillLeafNodeVoxels(distTree, nodes)); + + narrowBandMask.topologyUnion(candidateMask); + + ExpandLeafNodeRegion op(distTree, narrowBandMask, nodes); + tbb::parallel_reduce(nodeRange, op); + + if (op.newMaskTree().empty()) break; + + candidateMask.clear(); + candidateMask.merge(op.newMaskTree()); + } // end expand loop + } // end range loop + } + + TreeType const * const mTree; + BoolTreeTypePtr * const mSegments; +}; // ExpandNarrowbandMask + + +template +struct FloodFillSign +{ + using TreeTypePtr = typename TreeType::Ptr; + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using RootNodeType = typename TreeType::RootNodeType; + using NodeChainType = typename RootNodeType::NodeChainType; + using InternalNodeType = typename boost::mpl::at >::type; + + FloodFillSign(const TreeType& tree, std::vector& segments) + : mTree(&tree) + , mSegments(!segments.empty() ? &segments.front() : nullptr) + , mMinValue(ValueType(0.0)) + { + ValueType minSDFValue = std::numeric_limits::max(); + + { + std::vector nodes; + tree.getNodes(nodes); + + if (!nodes.empty()) { + FindMinTileValue minOp(&nodes[0]); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), minOp); + minSDFValue = std::min(minSDFValue, minOp.minValue); + } + } + + if (minSDFValue > ValueType(0.0)) { + std::vector nodes; + tree.getNodes(nodes); + if (!nodes.empty()) { + FindMinVoxelValue minOp(&nodes[0]); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), minOp); + minSDFValue = std::min(minSDFValue, minOp.minValue); + } + } + + mMinValue = minSDFValue; + } + + void operator()(const tbb::blocked_range& range) const { + const ValueType interiorValue = -std::abs(mMinValue); + const ValueType exteriorValue = std::abs(mTree->background()); + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + tools::signedFloodFillWithValues(*mSegments[n], exteriorValue, interiorValue); + } + } + +private: + + TreeType const * const mTree; + TreeTypePtr * const mSegments; + ValueType mMinValue; +}; // FloodFillSign + + +template +struct MaskedCopy +{ + using TreeTypePtr = typename TreeType::Ptr; + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolTreeTypePtr = typename BoolTreeType::Ptr; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + MaskedCopy(const TreeType& tree, std::vector& segments, + std::vector& masks) + : mTree(&tree) + , mSegments(!segments.empty() ? &segments.front() : nullptr) + , mMasks(!masks.empty() ? &masks.front() : nullptr) + { + } + + void operator()(const tbb::blocked_range& range) const { + + std::vector nodes; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const BoolTreeType& mask = *mMasks[n]; + + nodes.clear(); + mask.getNodes(nodes); + + Copy op(*mTree, nodes); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), op); + mSegments[n] = op.outputTree(); + } + } + +private: + + struct Copy { + Copy(const TreeType& inputTree, std::vector& maskNodes) + : mInputTree(&inputTree) + , mMaskNodes(!maskNodes.empty() ? &maskNodes.front() : nullptr) + , mOutputTreePtr(new TreeType(inputTree.background())) + { + } + + Copy(const Copy& rhs, tbb::split) + : mInputTree(rhs.mInputTree) + , mMaskNodes(rhs.mMaskNodes) + , mOutputTreePtr(new TreeType(mInputTree->background())) + { + } + + TreeTypePtr& outputTree() { return mOutputTreePtr; } + + void join(Copy& rhs) { mOutputTreePtr->merge(*rhs.mOutputTreePtr); } + + void operator()(const tbb::blocked_range& range) { + + tree::ValueAccessor inputAcc(*mInputTree); + tree::ValueAccessor outputAcc(*mOutputTreePtr); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const BoolLeafNodeType& maskNode = *mMaskNodes[n]; + if (maskNode.isEmpty()) continue; + + const Coord& ijk = maskNode.origin(); + + const LeafNodeType* inputNode = inputAcc.probeConstLeaf(ijk); + if (inputNode) { + + LeafNodeType* outputNode = outputAcc.touchLeaf(ijk); + + for (typename BoolLeafNodeType::ValueOnCIter it = maskNode.cbeginValueOn(); + it; ++it) + { + const Index idx = it.pos(); + outputNode->setValueOn(idx, inputNode->getValue(idx)); + } + } else { + const int valueDepth = inputAcc.getValueDepth(ijk); + if (valueDepth >= 0) { + outputAcc.addTile(TreeType::RootNodeType::LEVEL - valueDepth, + ijk, inputAcc.getValue(ijk), true); + } + } + } + } + + private: + TreeType const * const mInputTree; + BoolLeafNodeType const * const * const mMaskNodes; + TreeTypePtr mOutputTreePtr; + }; // struct Copy + + TreeType const * const mTree; + TreeTypePtr * const mSegments; + BoolTreeTypePtr * const mMasks; +}; // MaskedCopy + + +//////////////////////////////////////// + + +template +struct ComputeActiveVoxelCount +{ + ComputeActiveVoxelCount(std::vector& segments, size_t *countArray) + : mSegments(!segments.empty() ? &segments.front() : nullptr) + , mCountArray(countArray) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + mCountArray[n] = mSegments[n]->activeVoxelCount(); + } + } + + VolumePtrType * const mSegments; + size_t * const mCountArray; +}; + + +struct GreaterCount +{ + GreaterCount(const size_t *countArray) : mCountArray(countArray) {} + + inline bool operator() (const size_t& lhs, const size_t& rhs) const + { + return (mCountArray[lhs] > mCountArray[rhs]); + } + + size_t const * const mCountArray; +}; + +//////////////////////////////////////// + + +template +struct GridOrTreeConstructor +{ + using TreeTypePtr = typename TreeType::Ptr; + using BoolTreePtrType = typename TreeType::template ValueConverter::Type::Ptr; + + static BoolTreePtrType constructMask(const TreeType&, BoolTreePtrType& maskTree) + { return maskTree; } + static TreeTypePtr construct(const TreeType&, TreeTypePtr& tree) { return tree; } +}; + + +template +struct GridOrTreeConstructor > +{ + using GridType = Grid; + using GridTypePtr = typename Grid::Ptr; + using TreeTypePtr = typename TreeType::Ptr; + + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolTreePtrType = typename BoolTreeType::Ptr; + using BoolGridType = Grid; + using BoolGridPtrType = typename BoolGridType::Ptr; + + static BoolGridPtrType constructMask(const GridType& grid, BoolTreePtrType& maskTree) { + BoolGridPtrType maskGrid(BoolGridType::create(maskTree)); + maskGrid->setTransform(grid.transform().copy()); + return maskGrid; + } + + static GridTypePtr construct(const GridType& grid, TreeTypePtr& maskTree) { + GridTypePtr maskGrid(GridType::create(maskTree)); + maskGrid->setTransform(grid.transform().copy()); + maskGrid->insertMeta(grid); + return maskGrid; + } +}; + + +} // namespace level_set_util_internal + + +//////////////////////////////////////// + + +template +inline void +sdfToFogVolume(GridType& grid, typename GridType::ValueType cutoffDistance) +{ + using ValueType = typename GridType::ValueType; + using TreeType = typename GridType::TreeType; + using LeafNodeType = typename TreeType::LeafNodeType; + using RootNodeType = typename TreeType::RootNodeType; + using NodeChainType = typename RootNodeType::NodeChainType; + using InternalNodeType = typename boost::mpl::at>::type; + + ////////// + + TreeType& tree = grid.tree(); + + size_t numLeafNodes = 0, numInternalNodes = 0; + + std::vector nodes; + std::vector leafnodeCount; + + { + // Compute the prefix sum of the leafnode count in each internal node. + std::vector internalNodes; + tree.getNodes(internalNodes); + + numInternalNodes = internalNodes.size(); + + leafnodeCount.push_back(0); + for (size_t n = 0; n < numInternalNodes; ++n) { + leafnodeCount.push_back(leafnodeCount.back() + internalNodes[n]->leafCount()); + } + + numLeafNodes = leafnodeCount.back(); + + // Steal all leafnodes (Removes them from the tree and transfers ownership.) + nodes.reserve(numLeafNodes); + + for (size_t n = 0; n < numInternalNodes; ++n) { + internalNodes[n]->stealNodes(nodes, tree.background(), false); + } + + // Clamp cutoffDistance to min sdf value + ValueType minSDFValue = std::numeric_limits::max(); + + { + level_set_util_internal::FindMinTileValue minOp(&internalNodes[0]); + tbb::parallel_reduce(tbb::blocked_range(0, internalNodes.size()), minOp); + minSDFValue = std::min(minSDFValue, minOp.minValue); + } + + if (minSDFValue > ValueType(0.0)) { + level_set_util_internal::FindMinVoxelValue minOp(&nodes[0]); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), minOp); + minSDFValue = std::min(minSDFValue, minOp.minValue); + } + + cutoffDistance = -std::abs(cutoffDistance); + cutoffDistance = minSDFValue > cutoffDistance ? minSDFValue : cutoffDistance; + } + + // Transform voxel values and delete leafnodes that are uniformly zero after the transformation. + // (Positive values are set to zero with inactive state and negative values are remapped + // from zero to one with active state.) + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + level_set_util_internal::SDFVoxelsToFogVolume(&nodes[0], cutoffDistance)); + + // Populate a new tree with the remaining leafnodes + typename TreeType::Ptr newTree(new TreeType(ValueType(0.0))); + + level_set_util_internal::PopulateTree populate( + *newTree, &nodes[0], &leafnodeCount[0], 0); + tbb::parallel_reduce(tbb::blocked_range(0, numInternalNodes), populate); + + // Transform tile values (Negative valued tiles are set to 1.0 with active state.) + std::vector internalNodes; + newTree->getNodes(internalNodes); + + tbb::parallel_for(tbb::blocked_range(0, internalNodes.size()), + level_set_util_internal::SDFTilesToFogVolume( + tree, &internalNodes[0])); + + { + tree::ValueAccessor acc(tree); + + typename TreeType::ValueAllIter it(*newTree); + it.setMaxDepth(TreeType::ValueAllIter::LEAF_DEPTH - 2); + + for ( ; it; ++it) { + if (acc.getValue(it.getCoord()) < ValueType(0.0)) { + it.setValue(ValueType(1.0)); + it.setActiveState(true); + } + } + } + + // Insert missing root level tiles. (The new tree is constructed from the remaining leafnodes + // and will therefore not contain any root level tiles that may exist in the original tree.) + { + typename TreeType::ValueAllIter it(tree); + it.setMaxDepth(TreeType::ValueAllIter::ROOT_DEPTH); + for ( ; it; ++it) { + if (it.getValue() < ValueType(0.0)) { + newTree->addTile(TreeType::ValueAllIter::ROOT_LEVEL, it.getCoord(), + ValueType(1.0), true); + } + } + } + + grid.setTree(newTree); + grid.setGridClass(GRID_FOG_VOLUME); +} + + +//////////////////////////////////////// + + +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +sdfInteriorMask(const GridOrTreeType& volume, typename GridOrTreeType::ValueType isovalue) +{ + using TreeType = typename TreeAdapter::TreeType; + const TreeType& tree = TreeAdapter::tree(volume); + + using BoolTreePtrType = typename TreeType::template ValueConverter::Type::Ptr; + BoolTreePtrType mask = level_set_util_internal::computeInteriorMask(tree, isovalue); + + return level_set_util_internal::GridOrTreeConstructor::constructMask( + volume, mask); +} + + +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +extractEnclosedRegion(const GridOrTreeType& volume, + typename GridOrTreeType::ValueType isovalue, + const typename TreeAdapter::TreeType::template ValueConverter::Type* + fillMask) +{ + using TreeType = typename TreeAdapter::TreeType; + const TreeType& tree = TreeAdapter::tree(volume); + + using CharTreePtrType = typename TreeType::template ValueConverter::Type::Ptr; + CharTreePtrType regionMask = level_set_util_internal::computeEnclosedRegionMask( + tree, isovalue, fillMask); + + using BoolTreePtrType = typename TreeType::template ValueConverter::Type::Ptr; + BoolTreePtrType mask = level_set_util_internal::computeInteriorMask(*regionMask, 0); + + return level_set_util_internal::GridOrTreeConstructor::constructMask( + volume, mask); +} + + +//////////////////////////////////////// + + +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +extractIsosurfaceMask(const GridOrTreeType& volume, typename GridOrTreeType::ValueType isovalue) +{ + using TreeType = typename TreeAdapter::TreeType; + const TreeType& tree = TreeAdapter::tree(volume); + + std::vector nodes; + tree.getNodes(nodes); + + using BoolTreeType = typename TreeType::template ValueConverter::Type; + typename BoolTreeType::Ptr mask(new BoolTreeType(false)); + + level_set_util_internal::MaskIsovalueCrossingVoxels op(tree, nodes, *mask, isovalue); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), op); + + return level_set_util_internal::GridOrTreeConstructor::constructMask( + volume, mask); +} + + +//////////////////////////////////////// + + +template +inline void +extractActiveVoxelSegmentMasks(const GridOrTreeType& volume, + std::vector::Type::Ptr>& masks) +{ + using TreeType = typename TreeAdapter::TreeType; + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolTreePtrType = typename BoolTreeType::Ptr; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + using NodeMaskSegmentType = level_set_util_internal::NodeMaskSegment; + using NodeMaskSegmentPtrType = typename NodeMaskSegmentType::Ptr; + using NodeMaskSegmentPtrVector = typename std::vector; + using NodeMaskSegmentRawPtrVector = typename std::vector; + + ///// + + const TreeType& tree = TreeAdapter::tree(volume); + + BoolTreeType topologyMask(tree, false, TopologyCopy()); + + // prune out any inactive leaf nodes or inactive tiles + tools::pruneInactive(topologyMask); + + if (topologyMask.hasActiveTiles()) { + topologyMask.voxelizeActiveTiles(); + } + + std::vector leafnodes; + topologyMask.getNodes(leafnodes); + + if (leafnodes.empty()) return; + + // 1. Split node masks into disjoint segments + // Note: The LeafNode origin coord is modified to record the 'leafnodes' array offset. + + std::unique_ptr nodeSegmentArray( + new NodeMaskSegmentPtrVector[leafnodes.size()]); + + tbb::parallel_for(tbb::blocked_range(0, leafnodes.size()), + level_set_util_internal::SegmentNodeMask( + leafnodes, nodeSegmentArray.get())); + + + // 2. Compute segment connectivity + + tbb::parallel_for(tbb::blocked_range(0, leafnodes.size()), + level_set_util_internal::ConnectNodeMaskSegments( + topologyMask, nodeSegmentArray.get())); + + topologyMask.clear(); + + size_t nodeSegmentCount = 0; + for (size_t n = 0, N = leafnodes.size(); n < N; ++n) { + nodeSegmentCount += nodeSegmentArray[n].size(); + } + + // 3. Group connected segments + + std::deque nodeSegmentGroups; + + NodeMaskSegmentType* nextSegment = nodeSegmentArray[0][0].get(); + while (nextSegment) { + + nodeSegmentGroups.push_back(NodeMaskSegmentRawPtrVector()); + + std::vector& segmentGroup = nodeSegmentGroups.back(); + segmentGroup.reserve(nodeSegmentCount); + + std::deque segmentQueue; + segmentQueue.push_back(nextSegment); + nextSegment = nullptr; + + while (!segmentQueue.empty()) { + + NodeMaskSegmentType* segment = segmentQueue.back(); + segmentQueue.pop_back(); + + if (segment->visited) continue; + segment->visited = true; + + segmentGroup.push_back(segment); + + // queue connected segments + std::vector& connections = segment->connections; + for (size_t n = 0, N = connections.size(); n < N; ++n) { + if (!connections[n]->visited) segmentQueue.push_back(connections[n]); + } + } + + // find first unvisited segment + for (size_t n = 0, N = leafnodes.size(); n < N; ++n) { + NodeMaskSegmentPtrVector& nodeSegments = nodeSegmentArray[n]; + for (size_t i = 0, I = nodeSegments.size(); i < I; ++i) { + if (!nodeSegments[i]->visited) nextSegment = nodeSegments[i].get(); + } + } + } + + // 4. Mask segment groups + + if (nodeSegmentGroups.size() == 1) { + + BoolTreePtrType mask(new BoolTreeType(tree, false, TopologyCopy())); + + tools::pruneInactive(*mask); + + if (mask->hasActiveTiles()) { + mask->voxelizeActiveTiles(); + } + + masks.push_back( + level_set_util_internal::GridOrTreeConstructor::constructMask( + volume, mask)); + + } else if (nodeSegmentGroups.size() > 1) { + + for (size_t n = 0, N = nodeSegmentGroups.size(); n < N; ++n) { + + NodeMaskSegmentRawPtrVector& segmentGroup = nodeSegmentGroups[n]; + + level_set_util_internal::MaskSegmentGroup op(segmentGroup); + tbb::parallel_reduce(tbb::blocked_range(0, segmentGroup.size()), op); + + masks.push_back( + level_set_util_internal::GridOrTreeConstructor::constructMask( + volume, op.mask())); + } + } + + // 5. Sort segments in descending order based on the active voxel count. + + if (masks.size() > 1) { + const size_t segmentCount = masks.size(); + + std::unique_ptr segmentOrderArray(new size_t[segmentCount]); + std::unique_ptr voxelCountArray(new size_t[segmentCount]); + + for (size_t n = 0; n < segmentCount; ++n) { + segmentOrderArray[n] = n; + } + + tbb::parallel_for(tbb::blocked_range(0, segmentCount), + level_set_util_internal::ComputeActiveVoxelCount( + masks, voxelCountArray.get())); + + size_t *begin = segmentOrderArray.get(); + tbb::parallel_sort(begin, begin + masks.size(), level_set_util_internal::GreaterCount( + voxelCountArray.get())); + + std::vector orderedMasks; + orderedMasks.reserve(masks.size()); + + for (size_t n = 0; n < segmentCount; ++n) { + orderedMasks.push_back(masks[segmentOrderArray[n]]); + } + + masks.swap(orderedMasks); + } + +} // extractActiveVoxelSegmentMasks() + + +template +inline void +segmentActiveVoxels(const GridOrTreeType& volume, + std::vector& segments) +{ + using TreeType = typename TreeAdapter::TreeType; + using TreePtrType = typename TreeType::Ptr; + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolTreePtrType = typename BoolTreeType::Ptr; + + const TreeType& inputTree = TreeAdapter::tree(volume); + + // 1. Segment active topology mask + std::vector maskSegmentArray; + extractActiveVoxelSegmentMasks(inputTree, maskSegmentArray); + + // 2. Export segments + + const size_t numSegments = std::max(size_t(1), maskSegmentArray.size()); + std::vector outputSegmentArray(numSegments); + + if (maskSegmentArray.empty()) { + // if no active voxels in the original volume, copy just the background + // value of the input tree + outputSegmentArray[0] = TreePtrType(new TreeType(inputTree.background())); + } else if (numSegments == 1) { + // if there's only one segment with active voxels, copy the input tree + TreePtrType segment(new TreeType(inputTree)); + // however, if the leaf counts do not match due to the pruning of inactive leaf + // nodes in the mask, do a topology intersection to drop these inactive leafs + if (segment->leafCount() != inputTree.leafCount()) { + segment->topologyIntersection(*maskSegmentArray[0]); + } + outputSegmentArray[0] = segment; + } else { + const tbb::blocked_range segmentRange(0, numSegments); + tbb::parallel_for(segmentRange, + level_set_util_internal::MaskedCopy(inputTree, outputSegmentArray, + maskSegmentArray)); + } + + for (auto& segment : outputSegmentArray) { + segments.push_back( + level_set_util_internal::GridOrTreeConstructor::construct( + volume, segment)); + } +} + + +template +inline void +segmentSDF(const GridOrTreeType& volume, std::vector& segments) +{ + using TreeType = typename TreeAdapter::TreeType; + using TreePtrType = typename TreeType::Ptr; + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolTreePtrType = typename BoolTreeType::Ptr; + + const TreeType& inputTree = TreeAdapter::tree(volume); + + // 1. Mask zero crossing voxels + BoolTreePtrType mask = extractIsosurfaceMask(inputTree, lsutilGridZero()); + + // 2. Segment the zero crossing mask + std::vector maskSegmentArray; + extractActiveVoxelSegmentMasks(*mask, maskSegmentArray); + + const size_t numSegments = std::max(size_t(1), maskSegmentArray.size()); + std::vector outputSegmentArray(numSegments); + + if (maskSegmentArray.empty()) { + // if no active voxels in the original volume, copy just the background + // value of the input tree + outputSegmentArray[0] = TreePtrType(new TreeType(inputTree.background())); + } else { + const tbb::blocked_range segmentRange(0, numSegments); + + // 3. Expand zero crossing mask to capture sdf narrow band + tbb::parallel_for(segmentRange, + level_set_util_internal::ExpandNarrowbandMask(inputTree, maskSegmentArray)); + + // 4. Export sdf segments + + tbb::parallel_for(segmentRange, level_set_util_internal::MaskedCopy( + inputTree, outputSegmentArray, maskSegmentArray)); + + tbb::parallel_for(segmentRange, + level_set_util_internal::FloodFillSign(inputTree, outputSegmentArray)); + } + + for (auto& segment : outputSegmentArray) { + segments.push_back( + level_set_util_internal::GridOrTreeConstructor::construct( + volume, segment)); + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVEL_SET_UTIL_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Mask.h b/openvdb/tools/Mask.h new file mode 100644 index 00000000..660d8948 --- /dev/null +++ b/openvdb/tools/Mask.h @@ -0,0 +1,123 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file Mask.h +/// +/// @brief Construct boolean mask grids from grids of arbitrary type + +#ifndef OPENVDB_TOOLS_MASK_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_MASK_HAS_BEEN_INCLUDED + +#include +#include "LevelSetUtil.h" // for tools::sdfInteriorMask() +#include // for std::enable_if, std::is_floating_point + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Given an input grid of any type, return a new, boolean grid +/// whose active voxel topology matches the input grid's or, +/// if the input grid is a level set, matches the input grid's interior. +/// @param grid the grid from which to construct a mask +/// @param isovalue for a level set grid, the isovalue that defines the grid's interior +/// @sa tools::sdfInteriorMask() +template +inline typename GridType::template ValueConverter::Type::Ptr +interiorMask(const GridType& grid, const double isovalue = 0.0); + + +//////////////////////////////////////// + + +namespace mask_internal { + +/// @private +template +struct Traits { + static const bool isBool = std::is_same::value; + using BoolGridType = typename GridType::template ValueConverter::Type; + using BoolGridPtrType = typename BoolGridType::Ptr; +}; + + +/// @private +template +inline typename std::enable_if::value, + typename mask_internal::Traits::BoolGridPtrType>::type +doLevelSetInteriorMask(const GridType& grid, const double isovalue) +{ + using GridValueT = typename GridType::ValueType; + using MaskGridPtrT = typename mask_internal::Traits::BoolGridPtrType; + + // If the input grid is a level set (and floating-point), return a mask of its interior. + if (grid.getGridClass() == GRID_LEVEL_SET) { + return tools::sdfInteriorMask(grid, static_cast(isovalue)); + } + return MaskGridPtrT{}; +} + + +/// @private +// No-op specialization for non-floating-point grids +template +inline typename std::enable_if::value, + typename mask_internal::Traits::BoolGridPtrType>::type +doLevelSetInteriorMask(const GridType&, const double /*isovalue*/) +{ + using MaskGridPtrT = typename mask_internal::Traits::BoolGridPtrType; + return MaskGridPtrT{}; +} + + +/// @private +template +inline typename std::enable_if::isBool, + typename mask_internal::Traits::BoolGridPtrType>::type +doInteriorMask(const GridType& grid, const double /*isovalue*/) +{ + // If the input grid is already boolean, return a copy of it. + return grid.deepCopy(); +} + + +/// @private +template +inline typename std::enable_if::isBool), + typename mask_internal::Traits::BoolGridPtrType>::type +doInteriorMask(const GridType& grid, const double isovalue) +{ + using MaskGridT = typename mask_internal::Traits::BoolGridType; + + // If the input grid is a level set, return a mask of its interior. + if (auto maskGridPtr = doLevelSetInteriorMask(grid, isovalue)) { + return maskGridPtr; + } + + // For any other grid type, return a mask of its active voxels. + auto maskGridPtr = MaskGridT::create(/*background=*/false); + maskGridPtr->setTransform(grid.transform().copy()); + maskGridPtr->topologyUnion(grid); + return maskGridPtr; +} + +} // namespace mask_internal + + +template +inline typename GridType::template ValueConverter::Type::Ptr +interiorMask(const GridType& grid, const double isovalue) +{ + return mask_internal::doInteriorMask(grid, isovalue); +} + + +//////////////////////////////////////// + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MASK_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/MeshToVolume.h b/openvdb/tools/MeshToVolume.h new file mode 100644 index 00000000..2ae35fd4 --- /dev/null +++ b/openvdb/tools/MeshToVolume.h @@ -0,0 +1,4216 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file MeshToVolume.h +/// +/// @brief Convert polygonal meshes that consist of quads and/or triangles +/// into signed or unsigned distance field volumes. +/// +/// @note The signed distance field conversion requires a closed surface +/// but not necessarily a manifold surface. Supports surfaces with +/// self intersections and degenerate faces and is independent of +/// mesh surface normals / polygon orientation. +/// +/// @author Mihai Alden + +#ifndef OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED + +#include // for OPENVDB_HAS_CXX11 +#include +#include // for GodunovsNormSqrd +#include // for closestPointOnTriangleToPoint +#include +#include + +#include "ChangeBackground.h" +#include "Prune.h" // for pruneInactive and pruneLevelSet +#include "SignedFloodFill.h" // for signedFloodFillWithValues + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include // for std::sort() +#include // for std::isfinite(), std::isnan() +#include +#include +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +//////////////////////////////////////// + + +/// @brief Mesh to volume conversion flags +enum MeshToVolumeFlags { + + /// Switch from the default signed distance field conversion that classifies + /// regions as either inside or outside the mesh boundary to a unsigned distance + /// field conversion that only computes distance values. This conversion type + /// does not require a closed watertight mesh. + UNSIGNED_DISTANCE_FIELD = 0x1, + + /// Disable the cleanup step that removes voxels created by self intersecting + /// portions of the mesh. + DISABLE_INTERSECTING_VOXEL_REMOVAL = 0x2, + + /// Disable the distance renormalization step that smooths out bumps caused + /// by self intersecting or overlapping portions of the mesh + DISABLE_RENORMALIZATION = 0x4, + + /// Disable the cleanup step that removes active voxels that exceed the + /// narrow band limits. (Only relevant for small limits) + DISABLE_NARROW_BAND_TRIMMING = 0x8 +}; + + +/// @brief Convert polygonal meshes that consist of quads and/or triangles into +/// signed or unsigned distance field volumes. +/// +/// @note Requires a closed surface but not necessarily a manifold surface. +/// Supports surfaces with self intersections and degenerate faces +/// and is independent of mesh surface normals. +/// +/// @interface MeshDataAdapter +/// Expected interface for the MeshDataAdapter class +/// @code +/// struct MeshDataAdapter { +/// size_t polygonCount() const; // Total number of polygons +/// size_t pointCount() const; // Total number of points +/// size_t vertexCount(size_t n) const; // Vertex count for polygon n +/// +/// // Return position pos in local grid index space for polygon n and vertex v +/// void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const; +/// }; +/// @endcode +/// +/// @param mesh mesh data access class that conforms to the MeshDataAdapter +/// interface +/// @param transform world-to-index-space transform +/// @param exteriorBandWidth exterior narrow band width in voxel units +/// @param interiorBandWidth interior narrow band width in voxel units +/// (set to std::numeric_limits::max() to fill object +/// interior with distance values) +/// @param flags optional conversion flags defined in @c MeshToVolumeFlags +/// @param polygonIndexGrid optional grid output that will contain the closest-polygon +/// index for each voxel in the narrow band region +template +inline typename GridType::Ptr +meshToVolume( + const MeshDataAdapter& mesh, + const math::Transform& transform, + float exteriorBandWidth = 3.0f, + float interiorBandWidth = 3.0f, + int flags = 0, + typename GridType::template ValueConverter::Type * polygonIndexGrid = nullptr); + + +/// @brief Convert polygonal meshes that consist of quads and/or triangles into +/// signed or unsigned distance field volumes. +/// +/// @param interrupter a callback to interrupt the conversion process that conforms +/// to the util::NullInterrupter interface +/// @param mesh mesh data access class that conforms to the MeshDataAdapter +/// interface +/// @param transform world-to-index-space transform +/// @param exteriorBandWidth exterior narrow band width in voxel units +/// @param interiorBandWidth interior narrow band width in voxel units (set this value to +/// std::numeric_limits::max() to fill interior regions +/// with distance values) +/// @param flags optional conversion flags defined in @c MeshToVolumeFlags +/// @param polygonIndexGrid optional grid output that will contain the closest-polygon +/// index for each voxel in the active narrow band region +template +inline typename GridType::Ptr +meshToVolume( + Interrupter& interrupter, + const MeshDataAdapter& mesh, + const math::Transform& transform, + float exteriorBandWidth = 3.0f, + float interiorBandWidth = 3.0f, + int flags = 0, + typename GridType::template ValueConverter::Type * polygonIndexGrid = nullptr); + + +//////////////////////////////////////// + + +/// @brief Contiguous quad and triangle data adapter class +/// +/// @details PointType and PolygonType must provide element access +/// through the square brackets operator. +/// @details Points are assumed to be in local grid index space. +/// @details The PolygonType tuple can have either three or four components +/// this property must be specified in a static member variable +/// named @c size, similar to the math::Tuple class. +/// @details A four component tuple can represent a quads or a triangle +/// if the fourth component set to @c util::INVALID_INDEX +template +struct QuadAndTriangleDataAdapter { + + QuadAndTriangleDataAdapter(const std::vector& points, + const std::vector& polygons) + : mPointArray(points.empty() ? nullptr : &points[0]) + , mPointArraySize(points.size()) + , mPolygonArray(polygons.empty() ? nullptr : &polygons[0]) + , mPolygonArraySize(polygons.size()) + { + } + + QuadAndTriangleDataAdapter(const PointType * pointArray, size_t pointArraySize, + const PolygonType* polygonArray, size_t polygonArraySize) + : mPointArray(pointArray) + , mPointArraySize(pointArraySize) + , mPolygonArray(polygonArray) + , mPolygonArraySize(polygonArraySize) + { + } + + size_t polygonCount() const { return mPolygonArraySize; } + size_t pointCount() const { return mPointArraySize; } + + /// @brief Vertex count for polygon @a n + size_t vertexCount(size_t n) const { + return (PolygonType::size == 3 || mPolygonArray[n][3] == util::INVALID_IDX) ? 3 : 4; + } + + /// @brief Returns position @a pos in local grid index space + /// for polygon @a n and vertex @a v + void getIndexSpacePoint(size_t n, size_t v, Vec3d& pos) const { + const PointType& p = mPointArray[mPolygonArray[n][int(v)]]; + pos[0] = double(p[0]); + pos[1] = double(p[1]); + pos[2] = double(p[2]); + } + +private: + PointType const * const mPointArray; + size_t const mPointArraySize; + PolygonType const * const mPolygonArray; + size_t const mPolygonArraySize; +}; // struct QuadAndTriangleDataAdapter + + +//////////////////////////////////////// + + +// Convenience functions for the mesh to volume converter that wrap stl containers. +// +// Note the meshToVolume() method declared above is more flexible and better suited +// for arbitrary data structures. + + +/// @brief Convert a triangle mesh to a level set volume. +/// +/// @return a grid of type @c GridType containing a narrow-band level set +/// representation of the input mesh. +/// +/// @throw TypeError if @c GridType is not scalar or not floating-point +/// +/// @note Requires a closed surface but not necessarily a manifold surface. +/// Supports surfaces with self intersections and degenerate faces +/// and is independent of mesh surface normals. +/// +/// @param xform transform for the output grid +/// @param points list of world space point positions +/// @param triangles triangle index list +/// @param halfWidth half the width of the narrow band, in voxel units +template +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)); + +/// Adds support for a @a interrupter callback used to cancel the conversion. +template +inline typename GridType::Ptr +meshToLevelSet( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)); + + +/// @brief Convert a quad mesh to a level set volume. +/// +/// @return a grid of type @c GridType containing a narrow-band level set +/// representation of the input mesh. +/// +/// @throw TypeError if @c GridType is not scalar or not floating-point +/// +/// @note Requires a closed surface but not necessarily a manifold surface. +/// Supports surfaces with self intersections and degenerate faces +/// and is independent of mesh surface normals. +/// +/// @param xform transform for the output grid +/// @param points list of world space point positions +/// @param quads quad index list +/// @param halfWidth half the width of the narrow band, in voxel units +template +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& quads, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)); + +/// Adds support for a @a interrupter callback used to cancel the conversion. +template +inline typename GridType::Ptr +meshToLevelSet( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& quads, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)); + + +/// @brief Convert a triangle and quad mesh to a level set volume. +/// +/// @return a grid of type @c GridType containing a narrow-band level set +/// representation of the input mesh. +/// +/// @throw TypeError if @c GridType is not scalar or not floating-point +/// +/// @note Requires a closed surface but not necessarily a manifold surface. +/// Supports surfaces with self intersections and degenerate faces +/// and is independent of mesh surface normals. +/// +/// @param xform transform for the output grid +/// @param points list of world space point positions +/// @param triangles triangle index list +/// @param quads quad index list +/// @param halfWidth half the width of the narrow band, in voxel units +template +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)); + +/// Adds support for a @a interrupter callback used to cancel the conversion. +template +inline typename GridType::Ptr +meshToLevelSet( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)); + + +/// @brief Convert a triangle and quad mesh to a signed distance field +/// with an asymmetrical narrow band. +/// +/// @return a grid of type @c GridType containing a narrow-band signed +/// distance field representation of the input mesh. +/// +/// @throw TypeError if @c GridType is not scalar or not floating-point +/// +/// @note Requires a closed surface but not necessarily a manifold surface. +/// Supports surfaces with self intersections and degenerate faces +/// and is independent of mesh surface normals. +/// +/// @param xform transform for the output grid +/// @param points list of world space point positions +/// @param triangles triangle index list +/// @param quads quad index list +/// @param exBandWidth the exterior narrow-band width in voxel units +/// @param inBandWidth the interior narrow-band width in voxel units +template +inline typename GridType::Ptr +meshToSignedDistanceField( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float exBandWidth, + float inBandWidth); + +/// Adds support for a @a interrupter callback used to cancel the conversion. +template +inline typename GridType::Ptr +meshToSignedDistanceField( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float exBandWidth, + float inBandWidth); + + +/// @brief Convert a triangle and quad mesh to an unsigned distance field. +/// +/// @return a grid of type @c GridType containing a narrow-band unsigned +/// distance field representation of the input mesh. +/// +/// @throw TypeError if @c GridType is not scalar or not floating-point +/// +/// @note Does not requires a closed surface. +/// +/// @param xform transform for the output grid +/// @param points list of world space point positions +/// @param triangles triangle index list +/// @param quads quad index list +/// @param bandWidth the width of the narrow band, in voxel units +template +inline typename GridType::Ptr +meshToUnsignedDistanceField( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float bandWidth); + +/// Adds support for a @a interrupter callback used to cancel the conversion. +template +inline typename GridType::Ptr +meshToUnsignedDistanceField( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float bandWidth); + + +//////////////////////////////////////// + + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a box. +/// +/// @param bbox a bounding box in world units +/// @param xform world-to-index-space transform +/// @param halfWidth half the width of the narrow band, in voxel units +template +inline typename GridType::Ptr +createLevelSetBox(const math::BBox& bbox, + const openvdb::math::Transform& xform, + typename VecType::ValueType halfWidth = LEVEL_SET_HALF_WIDTH); + + +//////////////////////////////////////// + + +/// @brief Traces the exterior voxel boundary of closed objects in the input +/// volume @a tree. Exterior voxels are marked with a negative sign, +/// voxels with a value below @c 0.75 are left unchanged and act as +/// the boundary layer. +/// +/// @note Does not propagate sign information into tile regions. +template +inline void +traceExteriorBoundaries(FloatTreeT& tree); + + +//////////////////////////////////////// + + +/// @brief Extracts and stores voxel edge intersection data from a mesh. +class MeshToVoxelEdgeData +{ +public: + + ////////// + + ///@brief Internal edge data type. + struct EdgeData { + EdgeData(float dist = 1.0) + : mXDist(dist), mYDist(dist), mZDist(dist) + , mXPrim(util::INVALID_IDX) + , mYPrim(util::INVALID_IDX) + , mZPrim(util::INVALID_IDX) + { + } + + //@{ + /// Required by several of the tree nodes + /// @note These methods don't perform meaningful operations. + bool operator< (const EdgeData&) const { return false; } + bool operator> (const EdgeData&) const { return false; } + template EdgeData operator+(const T&) const { return *this; } + template EdgeData operator-(const T&) const { return *this; } + EdgeData operator-() const { return *this; } + //@} + + bool operator==(const EdgeData& rhs) const + { + return mXPrim == rhs.mXPrim && mYPrim == rhs.mYPrim && mZPrim == rhs.mZPrim; + } + + float mXDist, mYDist, mZDist; + Index32 mXPrim, mYPrim, mZPrim; + }; + + using TreeType = tree::Tree4::Type; + using Accessor = tree::ValueAccessor; + + + ////////// + + + MeshToVoxelEdgeData(); + + + /// @brief Threaded method to extract voxel edge data, the closest + /// intersection point and corresponding primitive index, + /// from the given mesh. + /// + /// @param pointList List of points in grid index space, preferably unique + /// and shared by different polygons. + /// @param polygonList List of triangles and/or quads. + void convert(const std::vector& pointList, const std::vector& polygonList); + + + /// @brief Returns intersection points with corresponding primitive + /// indices for the given @c ijk voxel. + void getEdgeData(Accessor& acc, const Coord& ijk, + std::vector& points, std::vector& primitives); + + /// @return An accessor of @c MeshToVoxelEdgeData::Accessor type that + /// provides random read access to the internal tree. + Accessor getAccessor() { return Accessor(mTree); } + +private: + void operator=(const MeshToVoxelEdgeData&) {} + TreeType mTree; + class GenEdgeData; +}; + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +// Internal utility objects and implementation details + +namespace mesh_to_volume_internal { + +template +struct TransformPoints { + + TransformPoints(const PointType* pointsIn, PointType* pointsOut, + const math::Transform& xform) + : mPointsIn(pointsIn), mPointsOut(pointsOut), mXform(&xform) + { + } + + void operator()(const tbb::blocked_range& range) const { + + Vec3d pos; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const PointType& wsP = mPointsIn[n]; + pos[0] = double(wsP[0]); + pos[1] = double(wsP[1]); + pos[2] = double(wsP[2]); + + pos = mXform->worldToIndex(pos); + + PointType& isP = mPointsOut[n]; + isP[0] = typename PointType::value_type(pos[0]); + isP[1] = typename PointType::value_type(pos[1]); + isP[2] = typename PointType::value_type(pos[2]); + } + } + + PointType const * const mPointsIn; + PointType * const mPointsOut; + math::Transform const * const mXform; +}; // TransformPoints + + +template +struct Tolerance +{ + static ValueType epsilon() { return ValueType(1e-7); } + static ValueType minNarrowBandWidth() { return ValueType(1.0 + 1e-6); } +}; + + +//////////////////////////////////////// + + +template +class CombineLeafNodes +{ +public: + + using Int32TreeType = typename TreeType::template ValueConverter::Type; + + using LeafNodeType = typename TreeType::LeafNodeType; + using Int32LeafNodeType = typename Int32TreeType::LeafNodeType; + + CombineLeafNodes(TreeType& lhsDistTree, Int32TreeType& lhsIdxTree, + LeafNodeType ** rhsDistNodes, Int32LeafNodeType ** rhsIdxNodes) + : mDistTree(&lhsDistTree) + , mIdxTree(&lhsIdxTree) + , mRhsDistNodes(rhsDistNodes) + , mRhsIdxNodes(rhsIdxNodes) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor idxAcc(*mIdxTree); + + using DistValueType = typename LeafNodeType::ValueType; + using IndexValueType = typename Int32LeafNodeType::ValueType; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const Coord& origin = mRhsDistNodes[n]->origin(); + + LeafNodeType* lhsDistNode = distAcc.probeLeaf(origin); + Int32LeafNodeType* lhsIdxNode = idxAcc.probeLeaf(origin); + + DistValueType* lhsDistData = lhsDistNode->buffer().data(); + IndexValueType* lhsIdxData = lhsIdxNode->buffer().data(); + + const DistValueType* rhsDistData = mRhsDistNodes[n]->buffer().data(); + const IndexValueType* rhsIdxData = mRhsIdxNodes[n]->buffer().data(); + + + for (Index32 offset = 0; offset < LeafNodeType::SIZE; ++offset) { + + if (rhsIdxData[offset] != Int32(util::INVALID_IDX)) { + + const DistValueType& lhsValue = lhsDistData[offset]; + const DistValueType& rhsValue = rhsDistData[offset]; + + if (rhsValue < lhsValue) { + lhsDistNode->setValueOn(offset, rhsValue); + lhsIdxNode->setValueOn(offset, rhsIdxData[offset]); + } else if (math::isExactlyEqual(rhsValue, lhsValue)) { + lhsIdxNode->setValueOn(offset, + std::min(lhsIdxData[offset], rhsIdxData[offset])); + } + } + } + + delete mRhsDistNodes[n]; + delete mRhsIdxNodes[n]; + } + } + +private: + + TreeType * const mDistTree; + Int32TreeType * const mIdxTree; + + LeafNodeType ** const mRhsDistNodes; + Int32LeafNodeType ** const mRhsIdxNodes; +}; // class CombineLeafNodes + + +//////////////////////////////////////// + + +template +struct StashOriginAndStoreOffset +{ + using LeafNodeType = typename TreeType::LeafNodeType; + + StashOriginAndStoreOffset(std::vector& nodes, Coord* coordinates) + : mNodes(nodes.empty() ? nullptr : &nodes[0]), mCoordinates(coordinates) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + Coord& origin = const_cast(mNodes[n]->origin()); + mCoordinates[n] = origin; + origin[0] = static_cast(n); + } + } + + LeafNodeType ** const mNodes; + Coord * const mCoordinates; +}; + + +template +struct RestoreOrigin +{ + using LeafNodeType = typename TreeType::LeafNodeType; + + RestoreOrigin(std::vector& nodes, const Coord* coordinates) + : mNodes(nodes.empty() ? nullptr : &nodes[0]), mCoordinates(coordinates) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + Coord& origin = const_cast(mNodes[n]->origin()); + origin[0] = mCoordinates[n][0]; + } + } + + LeafNodeType ** const mNodes; + Coord const * const mCoordinates; +}; + + +template +class ComputeNodeConnectivity +{ +public: + using LeafNodeType = typename TreeType::LeafNodeType; + + ComputeNodeConnectivity(const TreeType& tree, const Coord* coordinates, + size_t* offsets, size_t numNodes, const CoordBBox& bbox) + : mTree(&tree) + , mCoordinates(coordinates) + , mOffsets(offsets) + , mNumNodes(numNodes) + , mBBox(bbox) + { + } + + ComputeNodeConnectivity(const ComputeNodeConnectivity&) = default; + + // Disallow assignment + ComputeNodeConnectivity& operator=(const ComputeNodeConnectivity&) = delete; + + void operator()(const tbb::blocked_range& range) const { + + size_t* offsetsNextX = mOffsets; + size_t* offsetsPrevX = mOffsets + mNumNodes; + size_t* offsetsNextY = mOffsets + mNumNodes * 2; + size_t* offsetsPrevY = mOffsets + mNumNodes * 3; + size_t* offsetsNextZ = mOffsets + mNumNodes * 4; + size_t* offsetsPrevZ = mOffsets + mNumNodes * 5; + + tree::ValueAccessor acc(*mTree); + Coord ijk; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + const Coord& origin = mCoordinates[n]; + offsetsNextX[n] = findNeighbourNode(acc, origin, Coord(LeafNodeType::DIM, 0, 0)); + offsetsPrevX[n] = findNeighbourNode(acc, origin, Coord(-LeafNodeType::DIM, 0, 0)); + offsetsNextY[n] = findNeighbourNode(acc, origin, Coord(0, LeafNodeType::DIM, 0)); + offsetsPrevY[n] = findNeighbourNode(acc, origin, Coord(0, -LeafNodeType::DIM, 0)); + offsetsNextZ[n] = findNeighbourNode(acc, origin, Coord(0, 0, LeafNodeType::DIM)); + offsetsPrevZ[n] = findNeighbourNode(acc, origin, Coord(0, 0, -LeafNodeType::DIM)); + } + } + + size_t findNeighbourNode(tree::ValueAccessor& acc, + const Coord& start, const Coord& step) const + { + Coord ijk = start + step; + CoordBBox bbox(mBBox); + + while (bbox.isInside(ijk)) { + const LeafNodeType* node = acc.probeConstLeaf(ijk); + if (node) return static_cast(node->origin()[0]); + ijk += step; + } + + return std::numeric_limits::max(); + } + + +private: + TreeType const * const mTree; + Coord const * const mCoordinates; + size_t * const mOffsets; + + const size_t mNumNodes; + const CoordBBox mBBox; +}; // class ComputeNodeConnectivity + + +template +struct LeafNodeConnectivityTable +{ + enum { INVALID_OFFSET = std::numeric_limits::max() }; + + using LeafNodeType = typename TreeType::LeafNodeType; + + LeafNodeConnectivityTable(TreeType& tree) + { + mLeafNodes.reserve(tree.leafCount()); + tree.getNodes(mLeafNodes); + + if (mLeafNodes.empty()) return; + + CoordBBox bbox; + tree.evalLeafBoundingBox(bbox); + + const tbb::blocked_range range(0, mLeafNodes.size()); + + // stash the leafnode origin coordinate and temporarily store the + // linear offset in the origin.x variable. + std::unique_ptr coordinates{new Coord[mLeafNodes.size()]}; + tbb::parallel_for(range, + StashOriginAndStoreOffset(mLeafNodes, coordinates.get())); + + // build the leafnode offset table + mOffsets.reset(new size_t[mLeafNodes.size() * 6]); + + + tbb::parallel_for(range, ComputeNodeConnectivity( + tree, coordinates.get(), mOffsets.get(), mLeafNodes.size(), bbox)); + + // restore the leafnode origin coordinate + tbb::parallel_for(range, RestoreOrigin(mLeafNodes, coordinates.get())); + } + + size_t size() const { return mLeafNodes.size(); } + + std::vector& nodes() { return mLeafNodes; } + const std::vector& nodes() const { return mLeafNodes; } + + + const size_t* offsetsNextX() const { return mOffsets.get(); } + const size_t* offsetsPrevX() const { return mOffsets.get() + mLeafNodes.size(); } + + const size_t* offsetsNextY() const { return mOffsets.get() + mLeafNodes.size() * 2; } + const size_t* offsetsPrevY() const { return mOffsets.get() + mLeafNodes.size() * 3; } + + const size_t* offsetsNextZ() const { return mOffsets.get() + mLeafNodes.size() * 4; } + const size_t* offsetsPrevZ() const { return mOffsets.get() + mLeafNodes.size() * 5; } + +private: + std::vector mLeafNodes; + std::unique_ptr mOffsets; +}; // struct LeafNodeConnectivityTable + + +template +class SweepExteriorSign +{ +public: + + enum Axis { X_AXIS = 0, Y_AXIS = 1, Z_AXIS = 2 }; + + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using ConnectivityTable = LeafNodeConnectivityTable; + + SweepExteriorSign(Axis axis, const std::vector& startNodeIndices, + ConnectivityTable& connectivity) + : mStartNodeIndices(startNodeIndices.empty() ? nullptr : &startNodeIndices[0]) + , mConnectivity(&connectivity) + , mAxis(axis) + { + } + + void operator()(const tbb::blocked_range& range) const { + + std::vector& nodes = mConnectivity->nodes(); + + // Z Axis + size_t idxA = 0, idxB = 1; + Index step = 1; + + const size_t* nextOffsets = mConnectivity->offsetsNextZ(); + const size_t* prevOffsets = mConnectivity->offsetsPrevZ(); + + if (mAxis == Y_AXIS) { + + idxA = 0; + idxB = 2; + step = LeafNodeType::DIM; + + nextOffsets = mConnectivity->offsetsNextY(); + prevOffsets = mConnectivity->offsetsPrevY(); + + } else if (mAxis == X_AXIS) { + + idxA = 1; + idxB = 2; + step = LeafNodeType::DIM * LeafNodeType::DIM; + + nextOffsets = mConnectivity->offsetsNextX(); + prevOffsets = mConnectivity->offsetsPrevX(); + } + + Coord ijk(0, 0, 0); + + int& a = ijk[idxA]; + int& b = ijk[idxB]; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + size_t startOffset = mStartNodeIndices[n]; + size_t lastOffset = startOffset; + + Index pos(0); + + for (a = 0; a < int(LeafNodeType::DIM); ++a) { + for (b = 0; b < int(LeafNodeType::DIM); ++b) { + + pos = LeafNodeType::coordToOffset(ijk); + size_t offset = startOffset; + + // sweep in +axis direction until a boundary voxel is hit. + while ( offset != ConnectivityTable::INVALID_OFFSET && + traceVoxelLine(*nodes[offset], pos, step) ) { + + lastOffset = offset; + offset = nextOffsets[offset]; + } + + // find last leafnode in +axis direction + offset = lastOffset; + while (offset != ConnectivityTable::INVALID_OFFSET) { + lastOffset = offset; + offset = nextOffsets[offset]; + } + + // sweep in -axis direction until a boundary voxel is hit. + offset = lastOffset; + pos += step * (LeafNodeType::DIM - 1); + while ( offset != ConnectivityTable::INVALID_OFFSET && + traceVoxelLine(*nodes[offset], pos, -step)) { + offset = prevOffsets[offset]; + } + } + } + } + } + + + bool traceVoxelLine(LeafNodeType& node, Index pos, Index step) const { + + ValueType* data = node.buffer().data(); + + bool isOutside = true; + + for (Index i = 0; i < LeafNodeType::DIM; ++i) { + + ValueType& dist = data[pos]; + + if (dist < ValueType(0.0)) { + isOutside = true; + } else { + // Boundary voxel check. (Voxel that intersects the surface) + if (!(dist > ValueType(0.75))) isOutside = false; + + if (isOutside) dist = ValueType(-dist); + } + + pos += step; + } + + return isOutside; + } + + +private: + size_t const * const mStartNodeIndices; + ConnectivityTable * const mConnectivity; + + const Axis mAxis; +}; // class SweepExteriorSign + + +template +inline void +seedFill(LeafNodeType& node) +{ + using ValueType = typename LeafNodeType::ValueType; + using Queue = std::deque; + + + ValueType* data = node.buffer().data(); + + // find seed points + Queue seedPoints; + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + if (data[pos] < 0.0) seedPoints.push_back(pos); + } + + if (seedPoints.empty()) return; + + // clear sign information + for (Queue::iterator it = seedPoints.begin(); it != seedPoints.end(); ++it) { + ValueType& dist = data[*it]; + dist = -dist; + } + + // flood fill + + Coord ijk(0, 0, 0); + Index pos(0), nextPos(0); + + while (!seedPoints.empty()) { + + pos = seedPoints.back(); + seedPoints.pop_back(); + + ValueType& dist = data[pos]; + + if (!(dist < ValueType(0.0))) { + + dist = -dist; // flip sign + + ijk = LeafNodeType::offsetToLocalCoord(pos); + + if (ijk[0] != 0) { // i - 1, j, k + nextPos = pos - LeafNodeType::DIM * LeafNodeType::DIM; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[0] != (LeafNodeType::DIM - 1)) { // i + 1, j, k + nextPos = pos + LeafNodeType::DIM * LeafNodeType::DIM; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[1] != 0) { // i, j - 1, k + nextPos = pos - LeafNodeType::DIM; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[1] != (LeafNodeType::DIM - 1)) { // i, j + 1, k + nextPos = pos + LeafNodeType::DIM; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[2] != 0) { // i, j, k - 1 + nextPos = pos - 1; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[2] != (LeafNodeType::DIM - 1)) { // i, j, k + 1 + nextPos = pos + 1; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + } + } +} // seedFill() + + +template +inline bool +scanFill(LeafNodeType& node) +{ + bool updatedNode = false; + + using ValueType = typename LeafNodeType::ValueType; + ValueType* data = node.buffer().data(); + + Coord ijk(0, 0, 0); + + bool updatedSign = true; + while (updatedSign) { + + updatedSign = false; + + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + + ValueType& dist = data[pos]; + + if (!(dist < ValueType(0.0)) && dist > ValueType(0.75)) { + + ijk = LeafNodeType::offsetToLocalCoord(pos); + + // i, j, k - 1 + if (ijk[2] != 0 && data[pos - 1] < ValueType(0.0)) { + updatedSign = true; + dist = ValueType(-dist); + + // i, j, k + 1 + } else if (ijk[2] != (LeafNodeType::DIM - 1) && data[pos + 1] < ValueType(0.0)) { + updatedSign = true; + dist = ValueType(-dist); + + // i, j - 1, k + } else if (ijk[1] != 0 && data[pos - LeafNodeType::DIM] < ValueType(0.0)) { + updatedSign = true; + dist = ValueType(-dist); + + // i, j + 1, k + } else if (ijk[1] != (LeafNodeType::DIM - 1) + && data[pos + LeafNodeType::DIM] < ValueType(0.0)) + { + updatedSign = true; + dist = ValueType(-dist); + + // i - 1, j, k + } else if (ijk[0] != 0 + && data[pos - LeafNodeType::DIM * LeafNodeType::DIM] < ValueType(0.0)) + { + updatedSign = true; + dist = ValueType(-dist); + + // i + 1, j, k + } else if (ijk[0] != (LeafNodeType::DIM - 1) + && data[pos + LeafNodeType::DIM * LeafNodeType::DIM] < ValueType(0.0)) + { + updatedSign = true; + dist = ValueType(-dist); + } + } + } // end value loop + + updatedNode |= updatedSign; + } // end update loop + + return updatedNode; +} // scanFill() + + +template +class SeedFillExteriorSign +{ +public: + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + + SeedFillExteriorSign(std::vector& nodes, bool* changedNodeMask) + : mNodes(nodes.empty() ? nullptr : &nodes[0]) + , mChangedNodeMask(changedNodeMask) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + if (mChangedNodeMask[n]) { + //seedFill(*mNodes[n]); + mChangedNodeMask[n] = scanFill(*mNodes[n]); + } + } + } + + LeafNodeType ** const mNodes; + bool * const mChangedNodeMask; +}; + + +template +struct FillArray +{ + FillArray(ValueType* array, const ValueType v) : mArray(array), mValue(v) { } + + void operator()(const tbb::blocked_range& range) const { + const ValueType v = mValue; + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + mArray[n] = v; + } + } + + ValueType * const mArray; + const ValueType mValue; +}; + + +template +inline void +fillArray(ValueType* array, const ValueType val, const size_t length) +{ + const auto grainSize = std::max( + length / tbb::task_scheduler_init::default_num_threads(), 1024); + const tbb::blocked_range range(0, length, grainSize); + tbb::parallel_for(range, FillArray(array, val), tbb::simple_partitioner()); +} + + +template +class SyncVoxelMask +{ +public: + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + + SyncVoxelMask(std::vector& nodes, + const bool* changedNodeMask, bool* changedVoxelMask) + : mNodes(nodes.empty() ? nullptr : &nodes[0]) + , mChangedNodeMask(changedNodeMask) + , mChangedVoxelMask(changedVoxelMask) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + if (mChangedNodeMask[n]) { + bool* mask = &mChangedVoxelMask[n * LeafNodeType::SIZE]; + + ValueType* data = mNodes[n]->buffer().data(); + + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + if (mask[pos]) { + data[pos] = ValueType(-data[pos]); + mask[pos] = false; + } + } + } + } + } + + LeafNodeType ** const mNodes; + bool const * const mChangedNodeMask; + bool * const mChangedVoxelMask; +}; + + +template +class SeedPoints +{ +public: + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using ConnectivityTable = LeafNodeConnectivityTable; + + SeedPoints(ConnectivityTable& connectivity, + bool* changedNodeMask, bool* nodeMask, bool* changedVoxelMask) + : mConnectivity(&connectivity) + , mChangedNodeMask(changedNodeMask) + , mNodeMask(nodeMask) + , mChangedVoxelMask(changedVoxelMask) + { + } + + void operator()(const tbb::blocked_range& range) const { + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + if (!mChangedNodeMask[n]) { + + bool changedValue = false; + + changedValue |= processZ(n, /*firstFace=*/true); + changedValue |= processZ(n, /*firstFace=*/false); + + changedValue |= processY(n, /*firstFace=*/true); + changedValue |= processY(n, /*firstFace=*/false); + + changedValue |= processX(n, /*firstFace=*/true); + changedValue |= processX(n, /*firstFace=*/false); + + mNodeMask[n] = changedValue; + } + } + } + + + bool processZ(const size_t n, bool firstFace) const + { + const size_t offset = + firstFace ? mConnectivity->offsetsPrevZ()[n] : mConnectivity->offsetsNextZ()[n]; + if (offset != ConnectivityTable::INVALID_OFFSET && mChangedNodeMask[offset]) { + + bool* mask = &mChangedVoxelMask[n * LeafNodeType::SIZE]; + + const ValueType* lhsData = mConnectivity->nodes()[n]->buffer().data(); + const ValueType* rhsData = mConnectivity->nodes()[offset]->buffer().data(); + + const Index lastOffset = LeafNodeType::DIM - 1; + const Index lhsOffset = + firstFace ? 0 : lastOffset, rhsOffset = firstFace ? lastOffset : 0; + + Index tmpPos(0), pos(0); + bool changedValue = false; + + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + tmpPos = x << (2 * LeafNodeType::LOG2DIM); + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + pos = tmpPos + (y << LeafNodeType::LOG2DIM); + + if (lhsData[pos + lhsOffset] > ValueType(0.75)) { + if (rhsData[pos + rhsOffset] < ValueType(0.0)) { + changedValue = true; + mask[pos + lhsOffset] = true; + } + } + } + } + + return changedValue; + } + + return false; + } + + bool processY(const size_t n, bool firstFace) const + { + const size_t offset = + firstFace ? mConnectivity->offsetsPrevY()[n] : mConnectivity->offsetsNextY()[n]; + if (offset != ConnectivityTable::INVALID_OFFSET && mChangedNodeMask[offset]) { + + bool* mask = &mChangedVoxelMask[n * LeafNodeType::SIZE]; + + const ValueType* lhsData = mConnectivity->nodes()[n]->buffer().data(); + const ValueType* rhsData = mConnectivity->nodes()[offset]->buffer().data(); + + const Index lastOffset = LeafNodeType::DIM * (LeafNodeType::DIM - 1); + const Index lhsOffset = + firstFace ? 0 : lastOffset, rhsOffset = firstFace ? lastOffset : 0; + + Index tmpPos(0), pos(0); + bool changedValue = false; + + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + tmpPos = x << (2 * LeafNodeType::LOG2DIM); + for (Index z = 0; z < LeafNodeType::DIM; ++z) { + pos = tmpPos + z; + + if (lhsData[pos + lhsOffset] > ValueType(0.75)) { + if (rhsData[pos + rhsOffset] < ValueType(0.0)) { + changedValue = true; + mask[pos + lhsOffset] = true; + } + } + } + } + + return changedValue; + } + + return false; + } + + bool processX(const size_t n, bool firstFace) const + { + const size_t offset = + firstFace ? mConnectivity->offsetsPrevX()[n] : mConnectivity->offsetsNextX()[n]; + if (offset != ConnectivityTable::INVALID_OFFSET && mChangedNodeMask[offset]) { + + bool* mask = &mChangedVoxelMask[n * LeafNodeType::SIZE]; + + const ValueType* lhsData = mConnectivity->nodes()[n]->buffer().data(); + const ValueType* rhsData = mConnectivity->nodes()[offset]->buffer().data(); + + const Index lastOffset = LeafNodeType::DIM * LeafNodeType::DIM * (LeafNodeType::DIM-1); + const Index lhsOffset = + firstFace ? 0 : lastOffset, rhsOffset = firstFace ? lastOffset : 0; + + Index tmpPos(0), pos(0); + bool changedValue = false; + + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + tmpPos = y << LeafNodeType::LOG2DIM; + for (Index z = 0; z < LeafNodeType::DIM; ++z) { + pos = tmpPos + z; + + if (lhsData[pos + lhsOffset] > ValueType(0.75)) { + if (rhsData[pos + rhsOffset] < ValueType(0.0)) { + changedValue = true; + mask[pos + lhsOffset] = true; + } + } + } + } + + return changedValue; + } + + return false; + } + + ConnectivityTable * const mConnectivity; + bool * const mChangedNodeMask; + bool * const mNodeMask; + bool * const mChangedVoxelMask; +}; + + +//////////////////////////////////////// + +template +struct ComputeIntersectingVoxelSign +{ + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using Int32TreeType = typename TreeType::template ValueConverter::Type; + using Int32LeafNodeType = typename Int32TreeType::LeafNodeType; + + using PointArray = std::unique_ptr; + using MaskArray = std::unique_ptr; + using LocalData = std::pair; + using LocalDataTable = tbb::enumerable_thread_specific; + + ComputeIntersectingVoxelSign( + std::vector& distNodes, + const TreeType& distTree, + const Int32TreeType& indexTree, + const MeshDataAdapter& mesh) + : mDistNodes(distNodes.empty() ? nullptr : &distNodes[0]) + , mDistTree(&distTree) + , mIndexTree(&indexTree) + , mMesh(&mesh) + , mLocalDataTable(new LocalDataTable()) + { + } + + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor idxAcc(*mIndexTree); + + ValueType nval; + CoordBBox bbox; + Index xPos(0), yPos(0); + Coord ijk, nijk, nodeMin, nodeMax; + Vec3d cp, xyz, nxyz, dir1, dir2; + + LocalData& localData = mLocalDataTable->local(); + + PointArray& points = localData.first; + if (!points) points.reset(new Vec3d[LeafNodeType::SIZE * 2]); + + MaskArray& mask = localData.second; + if (!mask) mask.reset(new bool[LeafNodeType::SIZE]); + + + typename LeafNodeType::ValueOnCIter it; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& node = *mDistNodes[n]; + ValueType* data = node.buffer().data(); + + const Int32LeafNodeType* idxNode = idxAcc.probeConstLeaf(node.origin()); + const Int32* idxData = idxNode->buffer().data(); + + nodeMin = node.origin(); + nodeMax = nodeMin.offsetBy(LeafNodeType::DIM - 1); + + // reset computed voxel mask. + memset(mask.get(), 0, sizeof(bool) * LeafNodeType::SIZE); + + for (it = node.cbeginValueOn(); it; ++it) { + Index pos = it.pos(); + + ValueType& dist = data[pos]; + if (dist < 0.0 || dist > 0.75) continue; + + ijk = node.offsetToGlobalCoord(pos); + + xyz[0] = double(ijk[0]); + xyz[1] = double(ijk[1]); + xyz[2] = double(ijk[2]); + + + bbox.min() = Coord::maxComponent(ijk.offsetBy(-1), nodeMin); + bbox.max() = Coord::minComponent(ijk.offsetBy(1), nodeMax); + + bool flipSign = false; + + for (nijk[0] = bbox.min()[0]; nijk[0] <= bbox.max()[0] && !flipSign; ++nijk[0]) { + xPos = (nijk[0] & (LeafNodeType::DIM - 1u)) << (2 * LeafNodeType::LOG2DIM); + for (nijk[1]=bbox.min()[1]; nijk[1] <= bbox.max()[1] && !flipSign; ++nijk[1]) { + yPos = xPos + ((nijk[1] & (LeafNodeType::DIM-1u)) << LeafNodeType::LOG2DIM); + for (nijk[2] = bbox.min()[2]; nijk[2] <= bbox.max()[2]; ++nijk[2]) { + pos = yPos + (nijk[2] & (LeafNodeType::DIM - 1u)); + + const Int32& polyIdx = idxData[pos]; + + if (polyIdx == Int32(util::INVALID_IDX) || !(data[pos] < -0.75)) + continue; + + const Index pointIndex = pos * 2; + + if (!mask[pos]) { + + mask[pos] = true; + + nxyz[0] = double(nijk[0]); + nxyz[1] = double(nijk[1]); + nxyz[2] = double(nijk[2]); + + Vec3d& point = points[pointIndex]; + + point = closestPoint(nxyz, polyIdx); + + Vec3d& direction = points[pointIndex + 1]; + direction = nxyz - point; + direction.normalize(); + } + + dir1 = xyz - points[pointIndex]; + dir1.normalize(); + + if (points[pointIndex + 1].dot(dir1) > 0.0) { + flipSign = true; + break; + } + } + } + } + + if (flipSign) { + dist = -dist; + } else { + for (Int32 m = 0; m < 26; ++m) { + nijk = ijk + util::COORD_OFFSETS[m]; + + if (!bbox.isInside(nijk) && distAcc.probeValue(nijk, nval) && nval<-0.75) { + nxyz[0] = double(nijk[0]); + nxyz[1] = double(nijk[1]); + nxyz[2] = double(nijk[2]); + + cp = closestPoint(nxyz, idxAcc.getValue(nijk)); + + dir1 = xyz - cp; + dir1.normalize(); + + dir2 = nxyz - cp; + dir2.normalize(); + + if (dir2.dot(dir1) > 0.0) { + dist = -dist; + break; + } + } + } + } + + } // active voxel loop + } // leaf node loop + } + +private: + + Vec3d closestPoint(const Vec3d& center, Int32 polyIdx) const + { + Vec3d a, b, c, cp, uvw; + + const size_t polygon = size_t(polyIdx); + mMesh->getIndexSpacePoint(polygon, 0, a); + mMesh->getIndexSpacePoint(polygon, 1, b); + mMesh->getIndexSpacePoint(polygon, 2, c); + + cp = closestPointOnTriangleToPoint(a, c, b, center, uvw); + + if (4 == mMesh->vertexCount(polygon)) { + + mMesh->getIndexSpacePoint(polygon, 3, b); + + c = closestPointOnTriangleToPoint(a, b, c, center, uvw); + + if ((center - c).lengthSqr() < (center - cp).lengthSqr()) { + cp = c; + } + } + + return cp; + } + + + LeafNodeType ** const mDistNodes; + TreeType const * const mDistTree; + Int32TreeType const * const mIndexTree; + MeshDataAdapter const * const mMesh; + + SharedPtr mLocalDataTable; +}; // ComputeIntersectingVoxelSign + + +//////////////////////////////////////// + + +template +inline void +maskNodeInternalNeighbours(const Index pos, bool (&mask)[26]) +{ + using NodeT = LeafNodeType; + + const Coord ijk = NodeT::offsetToLocalCoord(pos); + + // Face adjacent neighbours + // i+1, j, k + mask[0] = ijk[0] != (NodeT::DIM - 1); + // i-1, j, k + mask[1] = ijk[0] != 0; + // i, j+1, k + mask[2] = ijk[1] != (NodeT::DIM - 1); + // i, j-1, k + mask[3] = ijk[1] != 0; + // i, j, k+1 + mask[4] = ijk[2] != (NodeT::DIM - 1); + // i, j, k-1 + mask[5] = ijk[2] != 0; + + // Edge adjacent neighbour + // i+1, j, k-1 + mask[6] = mask[0] && mask[5]; + // i-1, j, k-1 + mask[7] = mask[1] && mask[5]; + // i+1, j, k+1 + mask[8] = mask[0] && mask[4]; + // i-1, j, k+1 + mask[9] = mask[1] && mask[4]; + // i+1, j+1, k + mask[10] = mask[0] && mask[2]; + // i-1, j+1, k + mask[11] = mask[1] && mask[2]; + // i+1, j-1, k + mask[12] = mask[0] && mask[3]; + // i-1, j-1, k + mask[13] = mask[1] && mask[3]; + // i, j-1, k+1 + mask[14] = mask[3] && mask[4]; + // i, j-1, k-1 + mask[15] = mask[3] && mask[5]; + // i, j+1, k+1 + mask[16] = mask[2] && mask[4]; + // i, j+1, k-1 + mask[17] = mask[2] && mask[5]; + + // Corner adjacent neighbours + // i-1, j-1, k-1 + mask[18] = mask[1] && mask[3] && mask[5]; + // i-1, j-1, k+1 + mask[19] = mask[1] && mask[3] && mask[4]; + // i+1, j-1, k+1 + mask[20] = mask[0] && mask[3] && mask[4]; + // i+1, j-1, k-1 + mask[21] = mask[0] && mask[3] && mask[5]; + // i-1, j+1, k-1 + mask[22] = mask[1] && mask[2] && mask[5]; + // i-1, j+1, k+1 + mask[23] = mask[1] && mask[2] && mask[4]; + // i+1, j+1, k+1 + mask[24] = mask[0] && mask[2] && mask[4]; + // i+1, j+1, k-1 + mask[25] = mask[0] && mask[2] && mask[5]; +} + + +template +inline bool +checkNeighbours(const Index pos, const typename LeafNodeType::ValueType * data, bool (&mask)[26]) +{ + using NodeT = LeafNodeType; + + // i, j, k - 1 + if (mask[5] && Compare::check(data[pos - 1])) return true; + // i, j, k + 1 + if (mask[4] && Compare::check(data[pos + 1])) return true; + // i, j - 1, k + if (mask[3] && Compare::check(data[pos - NodeT::DIM])) return true; + // i, j + 1, k + if (mask[2] && Compare::check(data[pos + NodeT::DIM])) return true; + // i - 1, j, k + if (mask[1] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM])) return true; + // i + 1, j, k + if (mask[0] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM])) return true; + // i+1, j, k-1 + if (mask[6] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM])) return true; + // i-1, j, k-1 + if (mask[7] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM - 1])) return true; + // i+1, j, k+1 + if (mask[8] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM + 1])) return true; + // i-1, j, k+1 + if (mask[9] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM + 1])) return true; + // i+1, j+1, k + if (mask[10] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM + NodeT::DIM])) return true; + // i-1, j+1, k + if (mask[11] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM + NodeT::DIM])) return true; + // i+1, j-1, k + if (mask[12] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM - NodeT::DIM])) return true; + // i-1, j-1, k + if (mask[13] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM - NodeT::DIM])) return true; + // i, j-1, k+1 + if (mask[14] && Compare::check(data[pos - NodeT::DIM + 1])) return true; + // i, j-1, k-1 + if (mask[15] && Compare::check(data[pos - NodeT::DIM - 1])) return true; + // i, j+1, k+1 + if (mask[16] && Compare::check(data[pos + NodeT::DIM + 1])) return true; + // i, j+1, k-1 + if (mask[17] && Compare::check(data[pos + NodeT::DIM - 1])) return true; + // i-1, j-1, k-1 + if (mask[18] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM - NodeT::DIM - 1])) return true; + // i-1, j-1, k+1 + if (mask[19] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM - NodeT::DIM + 1])) return true; + // i+1, j-1, k+1 + if (mask[20] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM - NodeT::DIM + 1])) return true; + // i+1, j-1, k-1 + if (mask[21] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM - NodeT::DIM - 1])) return true; + // i-1, j+1, k-1 + if (mask[22] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM + NodeT::DIM - 1])) return true; + // i-1, j+1, k+1 + if (mask[23] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM + NodeT::DIM + 1])) return true; + // i+1, j+1, k+1 + if (mask[24] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM + NodeT::DIM + 1])) return true; + // i+1, j+1, k-1 + if (mask[25] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM + NodeT::DIM - 1])) return true; + + return false; +} + + +template +inline bool +checkNeighbours(const Coord& ijk, AccessorType& acc, bool (&mask)[26]) +{ + for (Int32 m = 0; m < 26; ++m) { + if (!mask[m] && Compare::check(acc.getValue(ijk + util::COORD_OFFSETS[m]))) { + return true; + } + } + + return false; +} + + +template +struct ValidateIntersectingVoxels +{ + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + + struct IsNegative { static bool check(const ValueType v) { return v < ValueType(0.0); } }; + + ValidateIntersectingVoxels(TreeType& tree, std::vector& nodes) + : mTree(&tree) + , mNodes(nodes.empty() ? nullptr : &nodes[0]) + { + } + + void operator()(const tbb::blocked_range& range) const + { + tree::ValueAccessor acc(*mTree); + bool neighbourMask[26]; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& node = *mNodes[n]; + ValueType* data = node.buffer().data(); + + typename LeafNodeType::ValueOnCIter it; + for (it = node.cbeginValueOn(); it; ++it) { + + const Index pos = it.pos(); + + ValueType& dist = data[pos]; + if (dist < 0.0 || dist > 0.75) continue; + + // Mask node internal neighbours + maskNodeInternalNeighbours(pos, neighbourMask); + + const bool hasNegativeNeighbour = + checkNeighbours(pos, data, neighbourMask) || + checkNeighbours(node.offsetToGlobalCoord(pos), acc, neighbourMask); + + if (!hasNegativeNeighbour) { + // push over boundary voxel distance + dist = ValueType(0.75) + Tolerance::epsilon(); + } + } + } + } + + TreeType * const mTree; + LeafNodeType ** const mNodes; +}; // ValidateIntersectingVoxels + + +template +struct RemoveSelfIntersectingSurface +{ + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using Int32TreeType = typename TreeType::template ValueConverter::Type; + + struct Comp { static bool check(const ValueType v) { return !(v > ValueType(0.75)); } }; + + RemoveSelfIntersectingSurface(std::vector& nodes, + TreeType& distTree, Int32TreeType& indexTree) + : mNodes(nodes.empty() ? nullptr : &nodes[0]) + , mDistTree(&distTree) + , mIndexTree(&indexTree) + { + } + + void operator()(const tbb::blocked_range& range) const + { + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor idxAcc(*mIndexTree); + bool neighbourMask[26]; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& distNode = *mNodes[n]; + ValueType* data = distNode.buffer().data(); + + typename Int32TreeType::LeafNodeType* idxNode = + idxAcc.probeLeaf(distNode.origin()); + + typename LeafNodeType::ValueOnCIter it; + for (it = distNode.cbeginValueOn(); it; ++it) { + + const Index pos = it.pos(); + + if (!(data[pos] > 0.75)) continue; + + // Mask node internal neighbours + maskNodeInternalNeighbours(pos, neighbourMask); + + const bool hasBoundaryNeighbour = + checkNeighbours(pos, data, neighbourMask) || + checkNeighbours(distNode.offsetToGlobalCoord(pos),distAcc,neighbourMask); + + if (!hasBoundaryNeighbour) { + distNode.setValueOff(pos); + idxNode->setValueOff(pos); + } + } + } + } + + LeafNodeType * * const mNodes; + TreeType * const mDistTree; + Int32TreeType * const mIndexTree; +}; // RemoveSelfIntersectingSurface + + +//////////////////////////////////////// + + +template +struct ReleaseChildNodes +{ + ReleaseChildNodes(NodeType ** nodes) : mNodes(nodes) {} + + void operator()(const tbb::blocked_range& range) const { + + using NodeMaskType = typename NodeType::NodeMaskType; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + const_cast(mNodes[n]->getChildMask()).setOff(); + } + } + + NodeType ** const mNodes; +}; + + +template +inline void +releaseLeafNodes(TreeType& tree) +{ + using RootNodeType = typename TreeType::RootNodeType; + using NodeChainType = typename RootNodeType::NodeChainType; + using InternalNodeType = typename boost::mpl::at >::type; + + std::vector nodes; + tree.getNodes(nodes); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + ReleaseChildNodes(nodes.empty() ? nullptr : &nodes[0])); +} + + +template +struct StealUniqueLeafNodes +{ + using LeafNodeType = typename TreeType::LeafNodeType; + + StealUniqueLeafNodes(TreeType& lhsTree, TreeType& rhsTree, + std::vector& overlappingNodes) + : mLhsTree(&lhsTree) + , mRhsTree(&rhsTree) + , mNodes(&overlappingNodes) + { + } + + void operator()() const { + + std::vector rhsLeafNodes; + + rhsLeafNodes.reserve(mRhsTree->leafCount()); + //mRhsTree->getNodes(rhsLeafNodes); + //releaseLeafNodes(*mRhsTree); + mRhsTree->stealNodes(rhsLeafNodes); + + tree::ValueAccessor acc(*mLhsTree); + + for (size_t n = 0, N = rhsLeafNodes.size(); n < N; ++n) { + if (!acc.probeLeaf(rhsLeafNodes[n]->origin())) { + acc.addLeaf(rhsLeafNodes[n]); + } else { + mNodes->push_back(rhsLeafNodes[n]); + } + } + } + +private: + TreeType * const mLhsTree; + TreeType * const mRhsTree; + std::vector * const mNodes; +}; + + +template +inline void +combineData(DistTreeType& lhsDist, IndexTreeType& lhsIdx, + DistTreeType& rhsDist, IndexTreeType& rhsIdx) +{ + using DistLeafNodeType = typename DistTreeType::LeafNodeType; + using IndexLeafNodeType = typename IndexTreeType::LeafNodeType; + + std::vector overlappingDistNodes; + std::vector overlappingIdxNodes; + + // Steal unique leafnodes + tbb::task_group tasks; + tasks.run(StealUniqueLeafNodes(lhsDist, rhsDist, overlappingDistNodes)); + tasks.run(StealUniqueLeafNodes(lhsIdx, rhsIdx, overlappingIdxNodes)); + tasks.wait(); + + // Combine overlapping leaf nodes + if (!overlappingDistNodes.empty() && !overlappingIdxNodes.empty()) { + tbb::parallel_for(tbb::blocked_range(0, overlappingDistNodes.size()), + CombineLeafNodes(lhsDist, lhsIdx, + &overlappingDistNodes[0], &overlappingIdxNodes[0])); + } +} + +/// @brief TBB body object to voxelize a mesh of triangles and/or quads into a collection +/// of VDB grids, namely a squared distance grid, a closest primitive grid and an +/// intersecting voxels grid (masks the mesh intersecting voxels) +/// @note Only the leaf nodes that intersect the mesh are allocated, and only voxels in +/// a narrow band (of two to three voxels in proximity to the mesh's surface) are activated. +/// They are populated with distance values and primitive indices. +template +struct VoxelizationData { + + using Ptr = std::unique_ptr; + using ValueType = typename TreeType::ValueType; + + using Int32TreeType = typename TreeType::template ValueConverter::Type; + using UCharTreeType = typename TreeType::template ValueConverter::Type; + + using FloatTreeAcc = tree::ValueAccessor; + using Int32TreeAcc = tree::ValueAccessor; + using UCharTreeAcc = tree::ValueAccessor; + + + VoxelizationData() + : distTree(std::numeric_limits::max()) + , distAcc(distTree) + , indexTree(Int32(util::INVALID_IDX)) + , indexAcc(indexTree) + , primIdTree(MaxPrimId) + , primIdAcc(primIdTree) + , mPrimCount(0) + { + } + + TreeType distTree; + FloatTreeAcc distAcc; + + Int32TreeType indexTree; + Int32TreeAcc indexAcc; + + UCharTreeType primIdTree; + UCharTreeAcc primIdAcc; + + unsigned char getNewPrimId() { + + /// @warning Don't use parallel methods here! + /// The primIdTree is used as a "scratch" pad to mark visits for a given polygon + /// into voxels which it may contribute to. The tree is kept as lightweight as + /// possible and is reset when a maximum count or size is reached. A previous + /// bug here occurred due to the calling of tree methods with multi-threaded + /// implementations, resulting in nested parallelization and re-use of the TLS + /// from the initial task. This consequently resulted in non deterministic values + /// of mPrimCount on the return of the initial task, and could potentially end up + /// with a mPrimCount equal to that of the MaxPrimId. This is used as the background + /// value of the scratch tree. + /// @see jira.aswf.io/browse/OVDB-117, PR #564 + /// @todo Consider profiling this operator with tree.clear() and Investigate the + /// chosen value of MaxPrimId + + if (mPrimCount == MaxPrimId || primIdTree.leafCount() > 1000) { + mPrimCount = 0; + primIdTree.root().clear(); + primIdTree.clearAllAccessors(); + assert(mPrimCount == 0); + } + + return mPrimCount++; + } + +private: + + enum { MaxPrimId = 100 }; + + unsigned char mPrimCount; +}; + + +template +class VoxelizePolygons +{ +public: + + using VoxelizationDataType = VoxelizationData; + using DataTable = tbb::enumerable_thread_specific; + + VoxelizePolygons(DataTable& dataTable, + const MeshDataAdapter& mesh, + Interrupter* interrupter = nullptr) + : mDataTable(&dataTable) + , mMesh(&mesh) + , mInterrupter(interrupter) + { + } + + void operator()(const tbb::blocked_range& range) const { + + typename VoxelizationDataType::Ptr& dataPtr = mDataTable->local(); + if (!dataPtr) dataPtr.reset(new VoxelizationDataType()); + + Triangle prim; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + if (this->wasInterrupted()) { + tbb::task::self().cancel_group_execution(); + break; + } + + const size_t numVerts = mMesh->vertexCount(n); + + // rasterize triangles and quads. + if (numVerts == 3 || numVerts == 4) { + + prim.index = Int32(n); + + mMesh->getIndexSpacePoint(n, 0, prim.a); + mMesh->getIndexSpacePoint(n, 1, prim.b); + mMesh->getIndexSpacePoint(n, 2, prim.c); + + evalTriangle(prim, *dataPtr); + + if (numVerts == 4) { + mMesh->getIndexSpacePoint(n, 3, prim.b); + evalTriangle(prim, *dataPtr); + } + } + } + } + +private: + + bool wasInterrupted() const { return mInterrupter && mInterrupter->wasInterrupted(); } + + struct Triangle { Vec3d a, b, c; Int32 index; }; + + struct SubTask + { + enum { POLYGON_LIMIT = 1000 }; + + SubTask(const Triangle& prim, DataTable& dataTable, + int subdivisionCount, size_t polygonCount, + Interrupter* interrupter = nullptr) + : mLocalDataTable(&dataTable) + , mPrim(prim) + , mSubdivisionCount(subdivisionCount) + , mPolygonCount(polygonCount) + , mInterrupter(interrupter) + { + } + + void operator()() const + { + if (mSubdivisionCount <= 0 || mPolygonCount >= POLYGON_LIMIT) { + + typename VoxelizationDataType::Ptr& dataPtr = mLocalDataTable->local(); + if (!dataPtr) dataPtr.reset(new VoxelizationDataType()); + + voxelizeTriangle(mPrim, *dataPtr, mInterrupter); + + } else if (!(mInterrupter && mInterrupter->wasInterrupted())) { + spawnTasks(mPrim, *mLocalDataTable, mSubdivisionCount, mPolygonCount, mInterrupter); + } + } + + DataTable * const mLocalDataTable; + Triangle const mPrim; + int const mSubdivisionCount; + size_t const mPolygonCount; + Interrupter * const mInterrupter; + }; // struct SubTask + + inline static int evalSubdivisionCount(const Triangle& prim) + { + const double ax = prim.a[0], bx = prim.b[0], cx = prim.c[0]; + const double dx = std::max(ax, std::max(bx, cx)) - std::min(ax, std::min(bx, cx)); + + const double ay = prim.a[1], by = prim.b[1], cy = prim.c[1]; + const double dy = std::max(ay, std::max(by, cy)) - std::min(ay, std::min(by, cy)); + + const double az = prim.a[2], bz = prim.b[2], cz = prim.c[2]; + const double dz = std::max(az, std::max(bz, cz)) - std::min(az, std::min(bz, cz)); + + return int(std::max(dx, std::max(dy, dz)) / double(TreeType::LeafNodeType::DIM * 2)); + } + + void evalTriangle(const Triangle& prim, VoxelizationDataType& data) const + { + const size_t polygonCount = mMesh->polygonCount(); + const int subdivisionCount = + polygonCount < SubTask::POLYGON_LIMIT ? evalSubdivisionCount(prim) : 0; + + if (subdivisionCount <= 0) { + voxelizeTriangle(prim, data, mInterrupter); + } else { + spawnTasks(prim, *mDataTable, subdivisionCount, polygonCount, mInterrupter); + } + } + + static void spawnTasks( + const Triangle& mainPrim, + DataTable& dataTable, + int subdivisionCount, + size_t polygonCount, + Interrupter* const interrupter) + { + subdivisionCount -= 1; + polygonCount *= 4; + + tbb::task_group tasks; + + const Vec3d ac = (mainPrim.a + mainPrim.c) * 0.5; + const Vec3d bc = (mainPrim.b + mainPrim.c) * 0.5; + const Vec3d ab = (mainPrim.a + mainPrim.b) * 0.5; + + Triangle prim; + prim.index = mainPrim.index; + + prim.a = mainPrim.a; + prim.b = ab; + prim.c = ac; + tasks.run(SubTask(prim, dataTable, subdivisionCount, polygonCount, interrupter)); + + prim.a = ab; + prim.b = bc; + prim.c = ac; + tasks.run(SubTask(prim, dataTable, subdivisionCount, polygonCount, interrupter)); + + prim.a = ab; + prim.b = mainPrim.b; + prim.c = bc; + tasks.run(SubTask(prim, dataTable, subdivisionCount, polygonCount, interrupter)); + + prim.a = ac; + prim.b = bc; + prim.c = mainPrim.c; + tasks.run(SubTask(prim, dataTable, subdivisionCount, polygonCount, interrupter)); + + tasks.wait(); + } + + static void voxelizeTriangle(const Triangle& prim, VoxelizationDataType& data, Interrupter* const interrupter) + { + std::deque coordList; + Coord ijk, nijk; + + ijk = Coord::floor(prim.a); + coordList.push_back(ijk); + + // The first point may not be quite in bounds, and rely + // on one of the neighbours to have the first valid seed, + // so we cannot early-exit here. + updateDistance(ijk, prim, data); + + unsigned char primId = data.getNewPrimId(); + data.primIdAcc.setValueOnly(ijk, primId); + + while (!coordList.empty()) { + if (interrupter && interrupter->wasInterrupted()) { + tbb::task::self().cancel_group_execution(); + break; + } + for (Int32 pass = 0; pass < 1048576 && !coordList.empty(); ++pass) { + ijk = coordList.back(); + coordList.pop_back(); + + for (Int32 i = 0; i < 26; ++i) { + nijk = ijk + util::COORD_OFFSETS[i]; + if (primId != data.primIdAcc.getValue(nijk)) { + data.primIdAcc.setValueOnly(nijk, primId); + if(updateDistance(nijk, prim, data)) coordList.push_back(nijk); + } + } + } + } + } + + static bool updateDistance(const Coord& ijk, const Triangle& prim, VoxelizationDataType& data) + { + Vec3d uvw, voxelCenter(ijk[0], ijk[1], ijk[2]); + + using ValueType = typename TreeType::ValueType; + + const ValueType dist = ValueType((voxelCenter - + closestPointOnTriangleToPoint(prim.a, prim.c, prim.b, voxelCenter, uvw)).lengthSqr()); + + // Either the points may be NAN, or they could be far enough from + // the origin that computing distance fails. + if (std::isnan(dist)) + return false; + + const ValueType oldDist = data.distAcc.getValue(ijk); + + if (dist < oldDist) { + data.distAcc.setValue(ijk, dist); + data.indexAcc.setValue(ijk, prim.index); + } else if (math::isExactlyEqual(dist, oldDist)) { + // makes reduction deterministic when different polygons + // produce the same distance value. + data.indexAcc.setValueOnly(ijk, std::min(prim.index, data.indexAcc.getValue(ijk))); + } + + return !(dist > 0.75); // true if the primitive intersects the voxel. + } + + DataTable * const mDataTable; + MeshDataAdapter const * const mMesh; + Interrupter * const mInterrupter; +}; // VoxelizePolygons + + +//////////////////////////////////////// + + +template +struct DiffLeafNodeMask +{ + using AccessorType = typename tree::ValueAccessor; + using LeafNodeType = typename TreeType::LeafNodeType; + + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + DiffLeafNodeMask(const TreeType& rhsTree, + std::vector& lhsNodes) + : mRhsTree(&rhsTree), mLhsNodes(lhsNodes.empty() ? nullptr : &lhsNodes[0]) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor acc(*mRhsTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolLeafNodeType* lhsNode = mLhsNodes[n]; + const LeafNodeType* rhsNode = acc.probeConstLeaf(lhsNode->origin()); + + if (rhsNode) lhsNode->topologyDifference(*rhsNode, false); + } + } + +private: + TreeType const * const mRhsTree; + BoolLeafNodeType ** const mLhsNodes; +}; + + +template +struct UnionValueMasks +{ + UnionValueMasks(std::vector& nodesA, std::vector& nodesB) + : mNodesA(nodesA.empty() ? nullptr : &nodesA[0]) + , mNodesB(nodesB.empty() ? nullptr : &nodesB[0]) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + mNodesA[n]->topologyUnion(*mNodesB[n]); + } + } + +private: + LeafNodeTypeA ** const mNodesA; + LeafNodeTypeB ** const mNodesB; +}; + + +template +struct ConstructVoxelMask +{ + using LeafNodeType = typename TreeType::LeafNodeType; + + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + ConstructVoxelMask(BoolTreeType& maskTree, const TreeType& tree, + std::vector& nodes) + : mTree(&tree) + , mNodes(nodes.empty() ? nullptr : &nodes[0]) + , mLocalMaskTree(false) + , mMaskTree(&maskTree) + { + } + + ConstructVoxelMask(ConstructVoxelMask& rhs, tbb::split) + : mTree(rhs.mTree) + , mNodes(rhs.mNodes) + , mLocalMaskTree(false) + , mMaskTree(&mLocalMaskTree) + { + } + + void operator()(const tbb::blocked_range& range) + { + using Iterator = typename LeafNodeType::ValueOnCIter; + + tree::ValueAccessor acc(*mTree); + tree::ValueAccessor maskAcc(*mMaskTree); + + Coord ijk, nijk, localCorod; + Index pos, npos; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + LeafNodeType& node = *mNodes[n]; + + CoordBBox bbox = node.getNodeBoundingBox(); + bbox.expand(-1); + + BoolLeafNodeType& maskNode = *maskAcc.touchLeaf(node.origin()); + + for (Iterator it = node.cbeginValueOn(); it; ++it) { + ijk = it.getCoord(); + pos = it.pos(); + + localCorod = LeafNodeType::offsetToLocalCoord(pos); + + if (localCorod[2] < int(LeafNodeType::DIM - 1)) { + npos = pos + 1; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(0, 0, 1); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[2] > 0) { + npos = pos - 1; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(0, 0, -1); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[1] < int(LeafNodeType::DIM - 1)) { + npos = pos + LeafNodeType::DIM; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(0, 1, 0); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[1] > 0) { + npos = pos - LeafNodeType::DIM; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(0, -1, 0); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[0] < int(LeafNodeType::DIM - 1)) { + npos = pos + LeafNodeType::DIM * LeafNodeType::DIM; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(1, 0, 0); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[0] > 0) { + npos = pos - LeafNodeType::DIM * LeafNodeType::DIM; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(-1, 0, 0); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + } + } + } + + void join(ConstructVoxelMask& rhs) { mMaskTree->merge(*rhs.mMaskTree); } + +private: + TreeType const * const mTree; + LeafNodeType ** const mNodes; + + BoolTreeType mLocalMaskTree; + BoolTreeType * const mMaskTree; +}; + + +/// @note The interior and exterior widths should be in world space units and squared. +template +struct ExpandNarrowband +{ + using ValueType = typename TreeType::ValueType; + using LeafNodeType = typename TreeType::LeafNodeType; + using NodeMaskType = typename LeafNodeType::NodeMaskType; + using Int32TreeType = typename TreeType::template ValueConverter::Type; + using Int32LeafNodeType = typename Int32TreeType::LeafNodeType; + using BoolTreeType = typename TreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + struct Fragment + { + Int32 idx, x, y, z; + ValueType dist; + + Fragment() : idx(0), x(0), y(0), z(0), dist(0.0) {} + + Fragment(Int32 idx_, Int32 x_, Int32 y_, Int32 z_, ValueType dist_) + : idx(idx_), x(x_), y(y_), z(z_), dist(dist_) + { + } + + bool operator<(const Fragment& rhs) const { return idx < rhs.idx; } + }; // struct Fragment + + //////////////////// + + ExpandNarrowband( + std::vector& maskNodes, + BoolTreeType& maskTree, + TreeType& distTree, + Int32TreeType& indexTree, + const MeshDataAdapter& mesh, + ValueType exteriorBandWidth, + ValueType interiorBandWidth, + ValueType voxelSize) + : mMaskNodes(maskNodes.empty() ? nullptr : &maskNodes[0]) + , mMaskTree(&maskTree) + , mDistTree(&distTree) + , mIndexTree(&indexTree) + , mMesh(&mesh) + , mNewMaskTree(false) + , mDistNodes() + , mUpdatedDistNodes() + , mIndexNodes() + , mUpdatedIndexNodes() + , mExteriorBandWidth(exteriorBandWidth) + , mInteriorBandWidth(interiorBandWidth) + , mVoxelSize(voxelSize) + { + } + + ExpandNarrowband(const ExpandNarrowband& rhs, tbb::split) + : mMaskNodes(rhs.mMaskNodes) + , mMaskTree(rhs.mMaskTree) + , mDistTree(rhs.mDistTree) + , mIndexTree(rhs.mIndexTree) + , mMesh(rhs.mMesh) + , mNewMaskTree(false) + , mDistNodes() + , mUpdatedDistNodes() + , mIndexNodes() + , mUpdatedIndexNodes() + , mExteriorBandWidth(rhs.mExteriorBandWidth) + , mInteriorBandWidth(rhs.mInteriorBandWidth) + , mVoxelSize(rhs.mVoxelSize) + { + } + + void join(ExpandNarrowband& rhs) + { + mDistNodes.insert(mDistNodes.end(), rhs.mDistNodes.begin(), rhs.mDistNodes.end()); + mIndexNodes.insert(mIndexNodes.end(), rhs.mIndexNodes.begin(), rhs.mIndexNodes.end()); + + mUpdatedDistNodes.insert(mUpdatedDistNodes.end(), + rhs.mUpdatedDistNodes.begin(), rhs.mUpdatedDistNodes.end()); + + mUpdatedIndexNodes.insert(mUpdatedIndexNodes.end(), + rhs.mUpdatedIndexNodes.begin(), rhs.mUpdatedIndexNodes.end()); + + mNewMaskTree.merge(rhs.mNewMaskTree); + } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor newMaskAcc(mNewMaskTree); + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor indexAcc(*mIndexTree); + + std::vector fragments; + fragments.reserve(256); + + std::unique_ptr newDistNodePt; + std::unique_ptr newIndexNodePt; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolLeafNodeType& maskNode = *mMaskNodes[n]; + if (maskNode.isEmpty()) continue; + + // Setup local caches + + const Coord& origin = maskNode.origin(); + + LeafNodeType * distNodePt = distAcc.probeLeaf(origin); + Int32LeafNodeType * indexNodePt = indexAcc.probeLeaf(origin); + + assert(!distNodePt == !indexNodePt); + + bool usingNewNodes = false; + + if (!distNodePt && !indexNodePt) { + + const ValueType backgroundDist = distAcc.getValue(origin); + + if (!newDistNodePt.get() && !newIndexNodePt.get()) { + newDistNodePt.reset(new LeafNodeType(origin, backgroundDist)); + newIndexNodePt.reset(new Int32LeafNodeType(origin, indexAcc.getValue(origin))); + } else { + + if ((backgroundDist < ValueType(0.0)) != + (newDistNodePt->getValue(0) < ValueType(0.0))) { + newDistNodePt->buffer().fill(backgroundDist); + } + + newDistNodePt->setOrigin(origin); + newIndexNodePt->setOrigin(origin); + } + + distNodePt = newDistNodePt.get(); + indexNodePt = newIndexNodePt.get(); + + usingNewNodes = true; + } + + + // Gather neighbour information + + CoordBBox bbox(Coord::max(), Coord::min()); + for (typename BoolLeafNodeType::ValueOnIter it = maskNode.beginValueOn(); it; ++it) { + bbox.expand(it.getCoord()); + } + + bbox.expand(1); + + gatherFragments(fragments, bbox, distAcc, indexAcc); + + + // Compute first voxel layer + + bbox = maskNode.getNodeBoundingBox(); + NodeMaskType mask; + bool updatedLeafNodes = false; + + for (typename BoolLeafNodeType::ValueOnIter it = maskNode.beginValueOn(); it; ++it) { + + const Coord ijk = it.getCoord(); + + if (updateVoxel(ijk, 5, fragments, *distNodePt, *indexNodePt, &updatedLeafNodes)) { + + for (Int32 i = 0; i < 6; ++i) { + const Coord nijk = ijk + util::COORD_OFFSETS[i]; + if (bbox.isInside(nijk)) { + mask.setOn(BoolLeafNodeType::coordToOffset(nijk)); + } else { + newMaskAcc.setValueOn(nijk); + } + } + + for (Int32 i = 6; i < 26; ++i) { + const Coord nijk = ijk + util::COORD_OFFSETS[i]; + if (bbox.isInside(nijk)) { + mask.setOn(BoolLeafNodeType::coordToOffset(nijk)); + } + } + } + } + + if (updatedLeafNodes) { + + // Compute second voxel layer + mask -= indexNodePt->getValueMask(); + + for (typename NodeMaskType::OnIterator it = mask.beginOn(); it; ++it) { + + const Index pos = it.pos(); + const Coord ijk = maskNode.origin() + LeafNodeType::offsetToLocalCoord(pos); + + if (updateVoxel(ijk, 6, fragments, *distNodePt, *indexNodePt)) { + for (Int32 i = 0; i < 6; ++i) { + newMaskAcc.setValueOn(ijk + util::COORD_OFFSETS[i]); + } + } + } + + // Export new distance values + if (usingNewNodes) { + newDistNodePt->topologyUnion(*newIndexNodePt); + mDistNodes.push_back(newDistNodePt.release()); + mIndexNodes.push_back(newIndexNodePt.release()); + } else { + mUpdatedDistNodes.push_back(distNodePt); + mUpdatedIndexNodes.push_back(indexNodePt); + } + } + } // end leafnode loop + } + + ////////// + + BoolTreeType& newMaskTree() { return mNewMaskTree; } + + std::vector& newDistNodes() { return mDistNodes; } + std::vector& updatedDistNodes() { return mUpdatedDistNodes; } + + std::vector& newIndexNodes() { return mIndexNodes; } + std::vector& updatedIndexNodes() { return mUpdatedIndexNodes; } + +private: + + /// @note The output fragment list is ordered by the primitive index + void + gatherFragments(std::vector& fragments, const CoordBBox& bbox, + tree::ValueAccessor& distAcc, tree::ValueAccessor& indexAcc) + { + fragments.clear(); + const Coord nodeMin = bbox.min() & ~(LeafNodeType::DIM - 1); + const Coord nodeMax = bbox.max() & ~(LeafNodeType::DIM - 1); + + CoordBBox region; + Coord ijk; + + for (ijk[0] = nodeMin[0]; ijk[0] <= nodeMax[0]; ijk[0] += LeafNodeType::DIM) { + for (ijk[1] = nodeMin[1]; ijk[1] <= nodeMax[1]; ijk[1] += LeafNodeType::DIM) { + for (ijk[2] = nodeMin[2]; ijk[2] <= nodeMax[2]; ijk[2] += LeafNodeType::DIM) { + if (LeafNodeType* distleaf = distAcc.probeLeaf(ijk)) { + region.min() = Coord::maxComponent(bbox.min(), ijk); + region.max() = Coord::minComponent(bbox.max(), + ijk.offsetBy(LeafNodeType::DIM - 1)); + gatherFragments(fragments, region, *distleaf, *indexAcc.probeLeaf(ijk)); + } + } + } + } + + std::sort(fragments.begin(), fragments.end()); + } + + void + gatherFragments(std::vector& fragments, const CoordBBox& bbox, + const LeafNodeType& distLeaf, const Int32LeafNodeType& idxLeaf) const + { + const typename LeafNodeType::NodeMaskType& mask = distLeaf.getValueMask(); + const ValueType* distData = distLeaf.buffer().data(); + const Int32* idxData = idxLeaf.buffer().data(); + + for (int x = bbox.min()[0]; x <= bbox.max()[0]; ++x) { + const Index xPos = (x & (LeafNodeType::DIM - 1u)) << (2 * LeafNodeType::LOG2DIM); + for (int y = bbox.min()[1]; y <= bbox.max()[1]; ++y) { + const Index yPos = xPos + ((y & (LeafNodeType::DIM - 1u)) << LeafNodeType::LOG2DIM); + for (int z = bbox.min()[2]; z <= bbox.max()[2]; ++z) { + const Index pos = yPos + (z & (LeafNodeType::DIM - 1u)); + if (mask.isOn(pos)) { + fragments.push_back(Fragment(idxData[pos],x,y,z, std::abs(distData[pos]))); + } + } + } + } + } + + /// @note This method expects the fragment list to be ordered by the primitive index + /// to avoid redundant distance computations. + ValueType + computeDistance(const Coord& ijk, const Int32 manhattanLimit, + const std::vector& fragments, Int32& closestPrimIdx) const + { + Vec3d a, b, c, uvw, voxelCenter(ijk[0], ijk[1], ijk[2]); + double primDist, tmpDist, dist = std::numeric_limits::max(); + Int32 lastIdx = Int32(util::INVALID_IDX); + + for (size_t n = 0, N = fragments.size(); n < N; ++n) { + + const Fragment& fragment = fragments[n]; + if (lastIdx == fragment.idx) continue; + + const Int32 dx = std::abs(fragment.x - ijk[0]); + const Int32 dy = std::abs(fragment.y - ijk[1]); + const Int32 dz = std::abs(fragment.z - ijk[2]); + + const Int32 manhattan = dx + dy + dz; + if (manhattan > manhattanLimit) continue; + + lastIdx = fragment.idx; + + const size_t polygon = size_t(lastIdx); + + mMesh->getIndexSpacePoint(polygon, 0, a); + mMesh->getIndexSpacePoint(polygon, 1, b); + mMesh->getIndexSpacePoint(polygon, 2, c); + + primDist = (voxelCenter - + closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw)).lengthSqr(); + + // Split quad into a second triangle + if (4 == mMesh->vertexCount(polygon)) { + + mMesh->getIndexSpacePoint(polygon, 3, b); + + tmpDist = (voxelCenter - closestPointOnTriangleToPoint( + a, b, c, voxelCenter, uvw)).lengthSqr(); + + if (tmpDist < primDist) primDist = tmpDist; + } + + if (primDist < dist) { + dist = primDist; + closestPrimIdx = lastIdx; + } + } + + return ValueType(std::sqrt(dist)) * mVoxelSize; + } + + /// @note Returns true if the current voxel was updated and neighbouring + /// voxels need to be evaluated. + bool + updateVoxel(const Coord& ijk, const Int32 manhattanLimit, + const std::vector& fragments, + LeafNodeType& distLeaf, Int32LeafNodeType& idxLeaf, bool* updatedLeafNodes = nullptr) + { + Int32 closestPrimIdx = 0; + const ValueType distance = computeDistance(ijk, manhattanLimit, fragments, closestPrimIdx); + + const Index pos = LeafNodeType::coordToOffset(ijk); + const bool inside = distLeaf.getValue(pos) < ValueType(0.0); + + bool activateNeighbourVoxels = false; + + if (!inside && distance < mExteriorBandWidth) { + if (updatedLeafNodes) *updatedLeafNodes = true; + activateNeighbourVoxels = (distance + mVoxelSize) < mExteriorBandWidth; + distLeaf.setValueOnly(pos, distance); + idxLeaf.setValueOn(pos, closestPrimIdx); + } else if (inside && distance < mInteriorBandWidth) { + if (updatedLeafNodes) *updatedLeafNodes = true; + activateNeighbourVoxels = (distance + mVoxelSize) < mInteriorBandWidth; + distLeaf.setValueOnly(pos, -distance); + idxLeaf.setValueOn(pos, closestPrimIdx); + } + + return activateNeighbourVoxels; + } + + ////////// + + BoolLeafNodeType ** const mMaskNodes; + BoolTreeType * const mMaskTree; + TreeType * const mDistTree; + Int32TreeType * const mIndexTree; + + MeshDataAdapter const * const mMesh; + + BoolTreeType mNewMaskTree; + + std::vector mDistNodes, mUpdatedDistNodes; + std::vector mIndexNodes, mUpdatedIndexNodes; + + const ValueType mExteriorBandWidth, mInteriorBandWidth, mVoxelSize; +}; // struct ExpandNarrowband + + +template +struct AddNodes { + using LeafNodeType = typename TreeType::LeafNodeType; + + AddNodes(TreeType& tree, std::vector& nodes) + : mTree(&tree) , mNodes(&nodes) + { + } + + void operator()() const { + tree::ValueAccessor acc(*mTree); + std::vector& nodes = *mNodes; + for (size_t n = 0, N = nodes.size(); n < N; ++n) { + acc.addLeaf(nodes[n]); + } + } + + TreeType * const mTree; + std::vector * const mNodes; +}; // AddNodes + + +template +inline void +expandNarrowband( + TreeType& distTree, + Int32TreeType& indexTree, + BoolTreeType& maskTree, + std::vector& maskNodes, + const MeshDataAdapter& mesh, + typename TreeType::ValueType exteriorBandWidth, + typename TreeType::ValueType interiorBandWidth, + typename TreeType::ValueType voxelSize) +{ + ExpandNarrowband expandOp(maskNodes, maskTree, + distTree, indexTree, mesh, exteriorBandWidth, interiorBandWidth, voxelSize); + + tbb::parallel_reduce(tbb::blocked_range(0, maskNodes.size()), expandOp); + + tbb::parallel_for(tbb::blocked_range(0, expandOp.updatedIndexNodes().size()), + UnionValueMasks( + expandOp.updatedDistNodes(), expandOp.updatedIndexNodes())); + + tbb::task_group tasks; + tasks.run(AddNodes(distTree, expandOp.newDistNodes())); + tasks.run(AddNodes(indexTree, expandOp.newIndexNodes())); + tasks.wait(); + + maskTree.clear(); + maskTree.merge(expandOp.newMaskTree()); +} + + +//////////////////////////////////////// + + +// Transform values (sqrt, world space scaling and sign flip if sdf) +template +struct TransformValues +{ + using LeafNodeType = typename TreeType::LeafNodeType; + using ValueType = typename TreeType::ValueType; + + TransformValues(std::vector& nodes, + ValueType voxelSize, bool unsignedDist) + : mNodes(&nodes[0]) + , mVoxelSize(voxelSize) + , mUnsigned(unsignedDist) + { + } + + void operator()(const tbb::blocked_range& range) const { + + typename LeafNodeType::ValueOnIter iter; + + const bool udf = mUnsigned; + const ValueType w[2] = { -mVoxelSize, mVoxelSize }; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + for (iter = mNodes[n]->beginValueOn(); iter; ++iter) { + ValueType& val = const_cast(iter.getValue()); + val = w[udf || (val < ValueType(0.0))] * std::sqrt(std::abs(val)); + } + } + } + +private: + LeafNodeType * * const mNodes; + const ValueType mVoxelSize; + const bool mUnsigned; +}; + + +// Inactivate values outside the (exBandWidth, inBandWidth) range. +template +struct InactivateValues +{ + using LeafNodeType = typename TreeType::LeafNodeType; + using ValueType = typename TreeType::ValueType; + + InactivateValues(std::vector& nodes, + ValueType exBandWidth, ValueType inBandWidth) + : mNodes(nodes.empty() ? nullptr : &nodes[0]) + , mExBandWidth(exBandWidth) + , mInBandWidth(inBandWidth) + { + } + + void operator()(const tbb::blocked_range& range) const { + + typename LeafNodeType::ValueOnIter iter; + const ValueType exVal = mExBandWidth; + const ValueType inVal = -mInBandWidth; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + for (iter = mNodes[n]->beginValueOn(); iter; ++iter) { + + ValueType& val = const_cast(iter.getValue()); + + const bool inside = val < ValueType(0.0); + + if (inside && !(val > inVal)) { + val = inVal; + iter.setValueOff(); + } else if (!inside && !(val < exVal)) { + val = exVal; + iter.setValueOff(); + } + } + } + } + +private: + LeafNodeType * * const mNodes; + const ValueType mExBandWidth, mInBandWidth; +}; + + +template +struct OffsetValues +{ + using LeafNodeType = typename TreeType::LeafNodeType; + using ValueType = typename TreeType::ValueType; + + OffsetValues(std::vector& nodes, ValueType offset) + : mNodes(nodes.empty() ? nullptr : &nodes[0]), mOffset(offset) + { + } + + void operator()(const tbb::blocked_range& range) const { + + const ValueType offset = mOffset; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + typename LeafNodeType::ValueOnIter iter = mNodes[n]->beginValueOn(); + + for (; iter; ++iter) { + ValueType& val = const_cast(iter.getValue()); + val += offset; + } + } + } + +private: + LeafNodeType * * const mNodes; + const ValueType mOffset; +}; + + +template +struct Renormalize +{ + using LeafNodeType = typename TreeType::LeafNodeType; + using ValueType = typename TreeType::ValueType; + + Renormalize(const TreeType& tree, const std::vector& nodes, + ValueType* buffer, ValueType voxelSize) + : mTree(&tree) + , mNodes(nodes.empty() ? nullptr : &nodes[0]) + , mBuffer(buffer) + , mVoxelSize(voxelSize) + { + } + + void operator()(const tbb::blocked_range& range) const + { + using Vec3Type = math::Vec3; + + tree::ValueAccessor acc(*mTree); + + Coord ijk; + Vec3Type up, down; + + const ValueType dx = mVoxelSize, invDx = ValueType(1.0) / mVoxelSize; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + ValueType* bufferData = &mBuffer[n * LeafNodeType::SIZE]; + + typename LeafNodeType::ValueOnCIter iter = mNodes[n]->cbeginValueOn(); + for (; iter; ++iter) { + + const ValueType phi0 = *iter; + + ijk = iter.getCoord(); + + up[0] = acc.getValue(ijk.offsetBy(1, 0, 0)) - phi0; + up[1] = acc.getValue(ijk.offsetBy(0, 1, 0)) - phi0; + up[2] = acc.getValue(ijk.offsetBy(0, 0, 1)) - phi0; + + down[0] = phi0 - acc.getValue(ijk.offsetBy(-1, 0, 0)); + down[1] = phi0 - acc.getValue(ijk.offsetBy(0, -1, 0)); + down[2] = phi0 - acc.getValue(ijk.offsetBy(0, 0, -1)); + + const ValueType normSqGradPhi = math::GodunovsNormSqrd(phi0 > 0.0, down, up); + + const ValueType diff = math::Sqrt(normSqGradPhi) * invDx - ValueType(1.0); + const ValueType S = phi0 / (math::Sqrt(math::Pow2(phi0) + normSqGradPhi)); + + bufferData[iter.pos()] = phi0 - dx * S * diff; + } + } + } + +private: + TreeType const * const mTree; + LeafNodeType const * const * const mNodes; + ValueType * const mBuffer; + + const ValueType mVoxelSize; +}; + + +template +struct MinCombine +{ + using LeafNodeType = typename TreeType::LeafNodeType; + using ValueType = typename TreeType::ValueType; + + MinCombine(std::vector& nodes, const ValueType* buffer) + : mNodes(nodes.empty() ? nullptr : &nodes[0]), mBuffer(buffer) + { + } + + void operator()(const tbb::blocked_range& range) const { + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const ValueType* bufferData = &mBuffer[n * LeafNodeType::SIZE]; + + typename LeafNodeType::ValueOnIter iter = mNodes[n]->beginValueOn(); + + for (; iter; ++iter) { + ValueType& val = const_cast(iter.getValue()); + val = std::min(val, bufferData[iter.pos()]); + } + } + } + +private: + LeafNodeType * * const mNodes; + ValueType const * const mBuffer; +}; + + +} // mesh_to_volume_internal namespace + + +//////////////////////////////////////// + +// Utility method implementation + + +template +inline void +traceExteriorBoundaries(FloatTreeT& tree) +{ + using ConnectivityTable = mesh_to_volume_internal::LeafNodeConnectivityTable; + + ConnectivityTable nodeConnectivity(tree); + + std::vector zStartNodes, yStartNodes, xStartNodes; + + for (size_t n = 0; n < nodeConnectivity.size(); ++n) { + if (ConnectivityTable::INVALID_OFFSET == nodeConnectivity.offsetsPrevX()[n]) { + xStartNodes.push_back(n); + } + + if (ConnectivityTable::INVALID_OFFSET == nodeConnectivity.offsetsPrevY()[n]) { + yStartNodes.push_back(n); + } + + if (ConnectivityTable::INVALID_OFFSET == nodeConnectivity.offsetsPrevZ()[n]) { + zStartNodes.push_back(n); + } + } + + using SweepingOp = mesh_to_volume_internal::SweepExteriorSign; + + tbb::parallel_for(tbb::blocked_range(0, zStartNodes.size()), + SweepingOp(SweepingOp::Z_AXIS, zStartNodes, nodeConnectivity)); + + tbb::parallel_for(tbb::blocked_range(0, yStartNodes.size()), + SweepingOp(SweepingOp::Y_AXIS, yStartNodes, nodeConnectivity)); + + tbb::parallel_for(tbb::blocked_range(0, xStartNodes.size()), + SweepingOp(SweepingOp::X_AXIS, xStartNodes, nodeConnectivity)); + + const size_t numLeafNodes = nodeConnectivity.size(); + const size_t numVoxels = numLeafNodes * FloatTreeT::LeafNodeType::SIZE; + + std::unique_ptr changedNodeMaskA{new bool[numLeafNodes]}; + std::unique_ptr changedNodeMaskB{new bool[numLeafNodes]}; + std::unique_ptr changedVoxelMask{new bool[numVoxels]}; + + mesh_to_volume_internal::fillArray(changedNodeMaskA.get(), true, numLeafNodes); + mesh_to_volume_internal::fillArray(changedNodeMaskB.get(), false, numLeafNodes); + mesh_to_volume_internal::fillArray(changedVoxelMask.get(), false, numVoxels); + + const tbb::blocked_range nodeRange(0, numLeafNodes); + + bool nodesUpdated = false; + do { + tbb::parallel_for(nodeRange, mesh_to_volume_internal::SeedFillExteriorSign( + nodeConnectivity.nodes(), changedNodeMaskA.get())); + + tbb::parallel_for(nodeRange, mesh_to_volume_internal::SeedPoints( + nodeConnectivity, changedNodeMaskA.get(), changedNodeMaskB.get(), + changedVoxelMask.get())); + + changedNodeMaskA.swap(changedNodeMaskB); + + nodesUpdated = false; + for (size_t n = 0; n < numLeafNodes; ++n) { + nodesUpdated |= changedNodeMaskA[n]; + if (nodesUpdated) break; + } + + if (nodesUpdated) { + tbb::parallel_for(nodeRange, mesh_to_volume_internal::SyncVoxelMask( + nodeConnectivity.nodes(), changedNodeMaskA.get(), changedVoxelMask.get())); + } + } while (nodesUpdated); + +} // void traceExteriorBoundaries() + + +//////////////////////////////////////// + + +template +inline typename GridType::Ptr +meshToVolume( + Interrupter& interrupter, + const MeshDataAdapter& mesh, + const math::Transform& transform, + float exteriorBandWidth, + float interiorBandWidth, + int flags, + typename GridType::template ValueConverter::Type * polygonIndexGrid) +{ + using GridTypePtr = typename GridType::Ptr; + using TreeType = typename GridType::TreeType; + using LeafNodeType = typename TreeType::LeafNodeType; + using ValueType = typename GridType::ValueType; + + using Int32GridType = typename GridType::template ValueConverter::Type; + using Int32TreeType = typename Int32GridType::TreeType; + + using BoolTreeType = typename TreeType::template ValueConverter::Type; + + ////////// + + // Setup + + GridTypePtr distGrid(new GridType(std::numeric_limits::max())); + distGrid->setTransform(transform.copy()); + + ValueType exteriorWidth = ValueType(exteriorBandWidth); + ValueType interiorWidth = ValueType(interiorBandWidth); + + // Note: inf interior width is all right, this value makes the converter fill + // interior regions with distance values. + if (!std::isfinite(exteriorWidth) || std::isnan(interiorWidth)) { + std::stringstream msg; + msg << "Illegal narrow band width: exterior = " << exteriorWidth + << ", interior = " << interiorWidth; + OPENVDB_LOG_DEBUG(msg.str()); + return distGrid; + } + + const ValueType voxelSize = ValueType(transform.voxelSize()[0]); + + if (!std::isfinite(voxelSize) || math::isZero(voxelSize)) { + std::stringstream msg; + msg << "Illegal transform, voxel size = " << voxelSize; + OPENVDB_LOG_DEBUG(msg.str()); + return distGrid; + } + + // Convert narrow band width from voxel units to world space units. + exteriorWidth *= voxelSize; + // Avoid the unit conversion if the interior band width is set to + // inf or std::numeric_limits::max(). + if (interiorWidth < std::numeric_limits::max()) { + interiorWidth *= voxelSize; + } + + const bool computeSignedDistanceField = (flags & UNSIGNED_DISTANCE_FIELD) == 0; + const bool removeIntersectingVoxels = (flags & DISABLE_INTERSECTING_VOXEL_REMOVAL) == 0; + const bool renormalizeValues = (flags & DISABLE_RENORMALIZATION) == 0; + const bool trimNarrowBand = (flags & DISABLE_NARROW_BAND_TRIMMING) == 0; + + Int32GridType* indexGrid = nullptr; + + typename Int32GridType::Ptr temporaryIndexGrid; + + if (polygonIndexGrid) { + indexGrid = polygonIndexGrid; + } else { + temporaryIndexGrid.reset(new Int32GridType(Int32(util::INVALID_IDX))); + indexGrid = temporaryIndexGrid.get(); + } + + indexGrid->newTree(); + indexGrid->setTransform(transform.copy()); + + if (computeSignedDistanceField) { + distGrid->setGridClass(GRID_LEVEL_SET); + } else { + distGrid->setGridClass(GRID_UNKNOWN); + interiorWidth = ValueType(0.0); + } + + TreeType& distTree = distGrid->tree(); + Int32TreeType& indexTree = indexGrid->tree(); + + + ////////// + + // Voxelize mesh + + { + using VoxelizationDataType = mesh_to_volume_internal::VoxelizationData; + using DataTable = tbb::enumerable_thread_specific; + + DataTable data; + using Voxelizer = + mesh_to_volume_internal::VoxelizePolygons; + + const tbb::blocked_range polygonRange(0, mesh.polygonCount()); + + tbb::parallel_for(polygonRange, Voxelizer(data, mesh, &interrupter)); + + for (typename DataTable::iterator i = data.begin(); i != data.end(); ++i) { + VoxelizationDataType& dataItem = **i; + mesh_to_volume_internal::combineData( + distTree, indexTree, dataItem.distTree, dataItem.indexTree); + } + } + + // The progress estimates are based on the observed average time for a few different + // test cases and is only intended to provide some rough progression feedback to the user. + if (interrupter.wasInterrupted(30)) return distGrid; + + + ////////// + + // Classify interior and exterior regions + + if (computeSignedDistanceField) { + + // Determines the inside/outside state for the narrow band of voxels. + traceExteriorBoundaries(distTree); + + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + const tbb::blocked_range nodeRange(0, nodes.size()); + + using SignOp = + mesh_to_volume_internal::ComputeIntersectingVoxelSign; + + tbb::parallel_for(nodeRange, SignOp(nodes, distTree, indexTree, mesh)); + + if (interrupter.wasInterrupted(45)) return distGrid; + + // Remove voxels created by self intersecting portions of the mesh. + if (removeIntersectingVoxels) { + + tbb::parallel_for(nodeRange, + mesh_to_volume_internal::ValidateIntersectingVoxels(distTree, nodes)); + + tbb::parallel_for(nodeRange, + mesh_to_volume_internal::RemoveSelfIntersectingSurface( + nodes, distTree, indexTree)); + + tools::pruneInactive(distTree, /*threading=*/true); + tools::pruneInactive(indexTree, /*threading=*/true); + } + } + + if (interrupter.wasInterrupted(50)) return distGrid; + + if (distTree.activeVoxelCount() == 0) { + distTree.clear(); + distTree.root().setBackground(exteriorWidth, /*updateChildNodes=*/false); + return distGrid; + } + + // Transform values (world space scaling etc.). + { + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::TransformValues( + nodes, voxelSize, !computeSignedDistanceField)); + } + + // Propagate sign information into tile regions. + if (computeSignedDistanceField) { + distTree.root().setBackground(exteriorWidth, /*updateChildNodes=*/false); + tools::signedFloodFillWithValues(distTree, exteriorWidth, -interiorWidth); + } else { + tools::changeBackground(distTree, exteriorWidth); + } + + if (interrupter.wasInterrupted(54)) return distGrid; + + + ////////// + + // Expand the narrow band region + + const ValueType minBandWidth = voxelSize * ValueType(2.0); + + if (interiorWidth > minBandWidth || exteriorWidth > minBandWidth) { + + // Create the initial voxel mask. + BoolTreeType maskTree(false); + + { + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + mesh_to_volume_internal::ConstructVoxelMask op(maskTree, distTree, nodes); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), op); + } + + // Progress estimation + unsigned maxIterations = std::numeric_limits::max(); + + float progress = 54.0f, step = 0.0f; + double estimated = + 2.0 * std::ceil((std::max(interiorWidth, exteriorWidth) - minBandWidth) / voxelSize); + + if (estimated < double(maxIterations)) { + maxIterations = unsigned(estimated); + step = 40.0f / float(maxIterations); + } + + std::vector maskNodes; + + unsigned count = 0; + while (true) { + + if (interrupter.wasInterrupted(int(progress))) return distGrid; + + const size_t maskNodeCount = maskTree.leafCount(); + if (maskNodeCount == 0) break; + + maskNodes.clear(); + maskNodes.reserve(maskNodeCount); + maskTree.getNodes(maskNodes); + + const tbb::blocked_range range(0, maskNodes.size()); + + tbb::parallel_for(range, + mesh_to_volume_internal::DiffLeafNodeMask(distTree, maskNodes)); + + mesh_to_volume_internal::expandNarrowband(distTree, indexTree, maskTree, maskNodes, + mesh, exteriorWidth, interiorWidth, voxelSize); + + if ((++count) >= maxIterations) break; + progress += step; + } + } + + if (interrupter.wasInterrupted(94)) return distGrid; + + if (!polygonIndexGrid) indexGrid->clear(); + + + ///////// + + // Renormalize distances to smooth out bumps caused by self intersecting + // and overlapping portions of the mesh and renormalize the level set. + + if (computeSignedDistanceField && renormalizeValues) { + + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + std::unique_ptr buffer{new ValueType[LeafNodeType::SIZE * nodes.size()]}; + + const ValueType offset = ValueType(0.8 * voxelSize); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::OffsetValues(nodes, -offset)); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::Renormalize( + distTree, nodes, buffer.get(), voxelSize)); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::MinCombine(nodes, buffer.get())); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::OffsetValues( + nodes, offset - mesh_to_volume_internal::Tolerance::epsilon())); + } + + if (interrupter.wasInterrupted(99)) return distGrid; + + + ///////// + + // Remove active voxels that exceed the narrow band limits + + if (trimNarrowBand && std::min(interiorWidth, exteriorWidth) < voxelSize * ValueType(4.0)) { + + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::InactivateValues( + nodes, exteriorWidth, computeSignedDistanceField ? interiorWidth : exteriorWidth)); + + tools::pruneLevelSet( + distTree, exteriorWidth, computeSignedDistanceField ? -interiorWidth : -exteriorWidth); + } + + return distGrid; +} + + +template +inline typename GridType::Ptr +meshToVolume( + const MeshDataAdapter& mesh, + const math::Transform& transform, + float exteriorBandWidth, + float interiorBandWidth, + int flags, + typename GridType::template ValueConverter::Type * polygonIndexGrid) +{ + util::NullInterrupter nullInterrupter; + return meshToVolume(nullInterrupter, mesh, transform, + exteriorBandWidth, interiorBandWidth, flags, polygonIndexGrid); +} + + +//////////////////////////////////////// + + +//{ +/// @cond OPENVDB_MESH_TO_VOLUME_INTERNAL + +/// @internal This overload is enabled only for grids with a scalar, floating-point ValueType. +template +inline typename std::enable_if::value, + typename GridType::Ptr>::type +doMeshConversion( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float exBandWidth, + float inBandWidth, + bool unsignedDistanceField = false) +{ + if (points.empty()) { + return typename GridType::Ptr(new GridType(typename GridType::ValueType(exBandWidth))); + } + + const size_t numPoints = points.size(); + std::unique_ptr indexSpacePoints{new Vec3s[numPoints]}; + + // transform points to local grid index space + tbb::parallel_for(tbb::blocked_range(0, numPoints), + mesh_to_volume_internal::TransformPoints( + &points[0], indexSpacePoints.get(), xform)); + + const int conversionFlags = unsignedDistanceField ? UNSIGNED_DISTANCE_FIELD : 0; + + if (quads.empty()) { + + QuadAndTriangleDataAdapter + mesh(indexSpacePoints.get(), numPoints, &triangles[0], triangles.size()); + + return meshToVolume( + interrupter, mesh, xform, exBandWidth, inBandWidth, conversionFlags); + + } else if (triangles.empty()) { + + QuadAndTriangleDataAdapter + mesh(indexSpacePoints.get(), numPoints, &quads[0], quads.size()); + + return meshToVolume( + interrupter, mesh, xform, exBandWidth, inBandWidth, conversionFlags); + } + + // pack primitives + + const size_t numPrimitives = triangles.size() + quads.size(); + std::unique_ptr prims{new Vec4I[numPrimitives]}; + + for (size_t n = 0, N = triangles.size(); n < N; ++n) { + const Vec3I& triangle = triangles[n]; + Vec4I& prim = prims[n]; + prim[0] = triangle[0]; + prim[1] = triangle[1]; + prim[2] = triangle[2]; + prim[3] = util::INVALID_IDX; + } + + const size_t offset = triangles.size(); + for (size_t n = 0, N = quads.size(); n < N; ++n) { + prims[offset + n] = quads[n]; + } + + QuadAndTriangleDataAdapter + mesh(indexSpacePoints.get(), numPoints, prims.get(), numPrimitives); + + return meshToVolume(interrupter, mesh, xform, + exBandWidth, inBandWidth, conversionFlags); +} + + +/// @internal This overload is enabled only for grids that do not have a scalar, +/// floating-point ValueType. +template +inline typename std::enable_if::value, + typename GridType::Ptr>::type +doMeshConversion( + Interrupter&, + const math::Transform& /*xform*/, + const std::vector& /*points*/, + const std::vector& /*triangles*/, + const std::vector& /*quads*/, + float /*exBandWidth*/, + float /*inBandWidth*/, + bool /*unsignedDistanceField*/ = false) +{ + OPENVDB_THROW(TypeError, + "mesh to volume conversion is supported only for scalar floating-point grids"); +} + +/// @endcond +//} + + +//////////////////////////////////////// + + +template +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + float halfWidth) +{ + util::NullInterrupter nullInterrupter; + std::vector quads(0); + return doMeshConversion(nullInterrupter, xform, points, triangles, quads, + halfWidth, halfWidth); +} + + +template +inline typename GridType::Ptr +meshToLevelSet( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + float halfWidth) +{ + std::vector quads(0); + return doMeshConversion(interrupter, xform, points, triangles, quads, + halfWidth, halfWidth); +} + + +template +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& quads, + float halfWidth) +{ + util::NullInterrupter nullInterrupter; + std::vector triangles(0); + return doMeshConversion(nullInterrupter, xform, points, triangles, quads, + halfWidth, halfWidth); +} + + +template +inline typename GridType::Ptr +meshToLevelSet( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& quads, + float halfWidth) +{ + std::vector triangles(0); + return doMeshConversion(interrupter, xform, points, triangles, quads, + halfWidth, halfWidth); +} + + +template +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float halfWidth) +{ + util::NullInterrupter nullInterrupter; + return doMeshConversion(nullInterrupter, xform, points, triangles, quads, + halfWidth, halfWidth); +} + + +template +inline typename GridType::Ptr +meshToLevelSet( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float halfWidth) +{ + return doMeshConversion(interrupter, xform, points, triangles, quads, + halfWidth, halfWidth); +} + + +template +inline typename GridType::Ptr +meshToSignedDistanceField( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float exBandWidth, + float inBandWidth) +{ + util::NullInterrupter nullInterrupter; + return doMeshConversion(nullInterrupter, xform, points, triangles, + quads, exBandWidth, inBandWidth); +} + + +template +inline typename GridType::Ptr +meshToSignedDistanceField( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float exBandWidth, + float inBandWidth) +{ + return doMeshConversion(interrupter, xform, points, triangles, + quads, exBandWidth, inBandWidth); +} + + +template +inline typename GridType::Ptr +meshToUnsignedDistanceField( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float bandWidth) +{ + util::NullInterrupter nullInterrupter; + return doMeshConversion(nullInterrupter, xform, points, triangles, quads, + bandWidth, bandWidth, true); +} + + +template +inline typename GridType::Ptr +meshToUnsignedDistanceField( + Interrupter& interrupter, + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float bandWidth) +{ + return doMeshConversion(interrupter, xform, points, triangles, quads, + bandWidth, bandWidth, true); +} + + +//////////////////////////////////////////////////////////////////////////////// + + +// Required by several of the tree nodes +inline std::ostream& +operator<<(std::ostream& ostr, const MeshToVoxelEdgeData::EdgeData& rhs) +{ + ostr << "{[ " << rhs.mXPrim << ", " << rhs.mXDist << "]"; + ostr << " [ " << rhs.mYPrim << ", " << rhs.mYDist << "]"; + ostr << " [ " << rhs.mZPrim << ", " << rhs.mZDist << "]}"; + return ostr; +} + +// Required by math::Abs +inline MeshToVoxelEdgeData::EdgeData +Abs(const MeshToVoxelEdgeData::EdgeData& x) +{ + return x; +} + + +//////////////////////////////////////// + + +class MeshToVoxelEdgeData::GenEdgeData +{ +public: + + GenEdgeData( + const std::vector& pointList, + const std::vector& polygonList); + + void run(bool threaded = true); + + GenEdgeData(GenEdgeData& rhs, tbb::split); + inline void operator() (const tbb::blocked_range &range); + inline void join(GenEdgeData& rhs); + + inline TreeType& tree() { return mTree; } + +private: + void operator=(const GenEdgeData&) {} + + struct Primitive { Vec3d a, b, c, d; Int32 index; }; + + template + inline void voxelize(const Primitive&); + + template + inline bool evalPrimitive(const Coord&, const Primitive&); + + inline bool rayTriangleIntersection( const Vec3d& origin, const Vec3d& dir, + const Vec3d& a, const Vec3d& b, const Vec3d& c, double& t); + + + TreeType mTree; + Accessor mAccessor; + + const std::vector& mPointList; + const std::vector& mPolygonList; + + // Used internally for acceleration + using IntTreeT = TreeType::ValueConverter::Type; + IntTreeT mLastPrimTree; + tree::ValueAccessor mLastPrimAccessor; +}; // class MeshToVoxelEdgeData::GenEdgeData + + +inline +MeshToVoxelEdgeData::GenEdgeData::GenEdgeData( + const std::vector& pointList, + const std::vector& polygonList) + : mTree(EdgeData()) + , mAccessor(mTree) + , mPointList(pointList) + , mPolygonList(polygonList) + , mLastPrimTree(Int32(util::INVALID_IDX)) + , mLastPrimAccessor(mLastPrimTree) +{ +} + + +inline +MeshToVoxelEdgeData::GenEdgeData::GenEdgeData(GenEdgeData& rhs, tbb::split) + : mTree(EdgeData()) + , mAccessor(mTree) + , mPointList(rhs.mPointList) + , mPolygonList(rhs.mPolygonList) + , mLastPrimTree(Int32(util::INVALID_IDX)) + , mLastPrimAccessor(mLastPrimTree) +{ +} + + +inline void +MeshToVoxelEdgeData::GenEdgeData::run(bool threaded) +{ + if (threaded) { + tbb::parallel_reduce(tbb::blocked_range(0, mPolygonList.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mPolygonList.size())); + } +} + + +inline void +MeshToVoxelEdgeData::GenEdgeData::join(GenEdgeData& rhs) +{ + using RootNodeType = TreeType::RootNodeType; + using NodeChainType = RootNodeType::NodeChainType; + static_assert(boost::mpl::size::value > 1, "expected tree height > 1"); + using InternalNodeType = boost::mpl::at >::type; + + Coord ijk; + Index offset; + + rhs.mTree.clearAllAccessors(); + + TreeType::LeafIter leafIt = rhs.mTree.beginLeaf(); + for ( ; leafIt; ++leafIt) { + ijk = leafIt->origin(); + + TreeType::LeafNodeType* lhsLeafPt = mTree.probeLeaf(ijk); + + if (!lhsLeafPt) { + + mAccessor.addLeaf(rhs.mAccessor.probeLeaf(ijk)); + InternalNodeType* node = rhs.mAccessor.getNode(); + node->stealNode(ijk, EdgeData(), false); + rhs.mAccessor.clear(); + + } else { + + TreeType::LeafNodeType::ValueOnCIter it = leafIt->cbeginValueOn(); + for ( ; it; ++it) { + + offset = it.pos(); + const EdgeData& rhsValue = it.getValue(); + + if (!lhsLeafPt->isValueOn(offset)) { + lhsLeafPt->setValueOn(offset, rhsValue); + } else { + + EdgeData& lhsValue = const_cast(lhsLeafPt->getValue(offset)); + + if (rhsValue.mXDist < lhsValue.mXDist) { + lhsValue.mXDist = rhsValue.mXDist; + lhsValue.mXPrim = rhsValue.mXPrim; + } + + if (rhsValue.mYDist < lhsValue.mYDist) { + lhsValue.mYDist = rhsValue.mYDist; + lhsValue.mYPrim = rhsValue.mYPrim; + } + + if (rhsValue.mZDist < lhsValue.mZDist) { + lhsValue.mZDist = rhsValue.mZDist; + lhsValue.mZPrim = rhsValue.mZPrim; + } + + } + } // end value iteration + } + } // end leaf iteration +} + + +inline void +MeshToVoxelEdgeData::GenEdgeData::operator()(const tbb::blocked_range &range) +{ + Primitive prim; + + for (size_t n = range.begin(); n < range.end(); ++n) { + + const Vec4I& verts = mPolygonList[n]; + + prim.index = Int32(n); + prim.a = Vec3d(mPointList[verts[0]]); + prim.b = Vec3d(mPointList[verts[1]]); + prim.c = Vec3d(mPointList[verts[2]]); + + if (util::INVALID_IDX != verts[3]) { + prim.d = Vec3d(mPointList[verts[3]]); + voxelize(prim); + } else { + voxelize(prim); + } + } +} + + +template +inline void +MeshToVoxelEdgeData::GenEdgeData::voxelize(const Primitive& prim) +{ + std::deque coordList; + Coord ijk, nijk; + + ijk = Coord::floor(prim.a); + coordList.push_back(ijk); + + evalPrimitive(ijk, prim); + + while (!coordList.empty()) { + + ijk = coordList.back(); + coordList.pop_back(); + + for (Int32 i = 0; i < 26; ++i) { + nijk = ijk + util::COORD_OFFSETS[i]; + + if (prim.index != mLastPrimAccessor.getValue(nijk)) { + mLastPrimAccessor.setValue(nijk, prim.index); + if(evalPrimitive(nijk, prim)) coordList.push_back(nijk); + } + } + } +} + + +template +inline bool +MeshToVoxelEdgeData::GenEdgeData::evalPrimitive(const Coord& ijk, const Primitive& prim) +{ + Vec3d uvw, org(ijk[0], ijk[1], ijk[2]); + bool intersecting = false; + double t; + + EdgeData edgeData; + mAccessor.probeValue(ijk, edgeData); + + // Evaluate first triangle + double dist = (org - + closestPointOnTriangleToPoint(prim.a, prim.c, prim.b, org, uvw)).lengthSqr(); + + if (rayTriangleIntersection(org, Vec3d(1.0, 0.0, 0.0), prim.a, prim.c, prim.b, t)) { + if (t < edgeData.mXDist) { + edgeData.mXDist = float(t); + edgeData.mXPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 1.0, 0.0), prim.a, prim.c, prim.b, t)) { + if (t < edgeData.mYDist) { + edgeData.mYDist = float(t); + edgeData.mYPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 0.0, 1.0), prim.a, prim.c, prim.b, t)) { + if (t < edgeData.mZDist) { + edgeData.mZDist = float(t); + edgeData.mZPrim = prim.index; + intersecting = true; + } + } + + if (IsQuad) { + // Split quad into a second triangle and calculate distance. + double secondDist = (org - + closestPointOnTriangleToPoint(prim.a, prim.d, prim.c, org, uvw)).lengthSqr(); + + if (secondDist < dist) dist = secondDist; + + if (rayTriangleIntersection(org, Vec3d(1.0, 0.0, 0.0), prim.a, prim.d, prim.c, t)) { + if (t < edgeData.mXDist) { + edgeData.mXDist = float(t); + edgeData.mXPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 1.0, 0.0), prim.a, prim.d, prim.c, t)) { + if (t < edgeData.mYDist) { + edgeData.mYDist = float(t); + edgeData.mYPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 0.0, 1.0), prim.a, prim.d, prim.c, t)) { + if (t < edgeData.mZDist) { + edgeData.mZDist = float(t); + edgeData.mZPrim = prim.index; + intersecting = true; + } + } + } + + if (intersecting) mAccessor.setValue(ijk, edgeData); + + return (dist < 0.86602540378443861); +} + + +inline bool +MeshToVoxelEdgeData::GenEdgeData::rayTriangleIntersection( + const Vec3d& origin, const Vec3d& dir, + const Vec3d& a, const Vec3d& b, const Vec3d& c, + double& t) +{ + // Check if ray is parallel with triangle + + Vec3d e1 = b - a; + Vec3d e2 = c - a; + Vec3d s1 = dir.cross(e2); + + double divisor = s1.dot(e1); + if (!(std::abs(divisor) > 0.0)) return false; + + // Compute barycentric coordinates + + double inv_divisor = 1.0 / divisor; + Vec3d d = origin - a; + double b1 = d.dot(s1) * inv_divisor; + + if (b1 < 0.0 || b1 > 1.0) return false; + + Vec3d s2 = d.cross(e1); + double b2 = dir.dot(s2) * inv_divisor; + + if (b2 < 0.0 || (b1 + b2) > 1.0) return false; + + // Compute distance to intersection point + + t = e2.dot(s2) * inv_divisor; + return (t < 0.0) ? false : true; +} + + +//////////////////////////////////////// + + +inline +MeshToVoxelEdgeData::MeshToVoxelEdgeData() + : mTree(EdgeData()) +{ +} + + +inline void +MeshToVoxelEdgeData::convert( + const std::vector& pointList, + const std::vector& polygonList) +{ + GenEdgeData converter(pointList, polygonList); + converter.run(); + + mTree.clear(); + mTree.merge(converter.tree()); +} + + +inline void +MeshToVoxelEdgeData::getEdgeData( + Accessor& acc, + const Coord& ijk, + std::vector& points, + std::vector& primitives) +{ + EdgeData data; + Vec3d point; + + Coord coord = ijk; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + + } + + coord[0] += 1; + + if (acc.probeValue(coord, data)) { + + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + } + + coord[2] += 1; + + if (acc.probeValue(coord, data)) { + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + } + + coord[0] -= 1; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + } + + + coord[1] += 1; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + } + + coord[2] -= 1; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + } + + coord[0] += 1; + + if (acc.probeValue(coord, data)) { + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + } +} + + +template +inline typename GridType::Ptr +createLevelSetBox(const math::BBox& bbox, + const openvdb::math::Transform& xform, + typename VecType::ValueType halfWidth) +{ + const Vec3s pmin = Vec3s(xform.worldToIndex(bbox.min())); + const Vec3s pmax = Vec3s(xform.worldToIndex(bbox.max())); + + Vec3s points[8]; + points[0] = Vec3s(pmin[0], pmin[1], pmin[2]); + points[1] = Vec3s(pmin[0], pmin[1], pmax[2]); + points[2] = Vec3s(pmax[0], pmin[1], pmax[2]); + points[3] = Vec3s(pmax[0], pmin[1], pmin[2]); + points[4] = Vec3s(pmin[0], pmax[1], pmin[2]); + points[5] = Vec3s(pmin[0], pmax[1], pmax[2]); + points[6] = Vec3s(pmax[0], pmax[1], pmax[2]); + points[7] = Vec3s(pmax[0], pmax[1], pmin[2]); + + Vec4I faces[6]; + faces[0] = Vec4I(0, 1, 2, 3); // bottom + faces[1] = Vec4I(7, 6, 5, 4); // top + faces[2] = Vec4I(4, 5, 1, 0); // front + faces[3] = Vec4I(6, 7, 3, 2); // back + faces[4] = Vec4I(0, 3, 7, 4); // left + faces[5] = Vec4I(1, 5, 6, 2); // right + + QuadAndTriangleDataAdapter mesh(points, 8, faces, 6); + + return meshToVolume(mesh, xform, halfWidth, halfWidth); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Morphology.h b/openvdb/tools/Morphology.h new file mode 100644 index 00000000..f6596543 --- /dev/null +++ b/openvdb/tools/Morphology.h @@ -0,0 +1,1069 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Morphology.h +/// +/// @brief Implementation of morphological dilation and erosion. +/// +/// @note By design the morphological operations only change the +/// state of voxels, not their values. If one desires to +/// change the values of voxels that change state an efficient +/// technique is to construct a boolean mask by performing a +/// topology difference between the original and final grids. +/// +/// @todo Extend erosion with 18 and 26 neighbors (coming soon!) +/// +/// @author Ken Museth +/// + +#ifndef OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED + +#include +#include +#include // for isApproxEqual() +#include +#include +#include +#include "Prune.h"// for pruneLevelSet +#include "ValueTransformer.h" // for foreach() +#include +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Voxel topology of nearest neighbors +/// @details +///
+///
NN_FACE +///
face adjacency (6 nearest neighbors, defined as all neighbor +/// voxels connected along one of the primary axes) +/// +///
NN_FACE_EDGE +///
face and edge adjacency (18 nearest neighbors, defined as all +/// neighbor voxels connected along either one or two of the primary axes) +/// +///
NN_FACE_EDGE_VERTEX +///
face, edge and vertex adjacency (26 nearest neighbors, defined +/// as all neighbor voxels connected along either one, two or all +/// three of the primary axes) +///
+enum NearestNeighbors { NN_FACE = 6, NN_FACE_EDGE = 18, NN_FACE_EDGE_VERTEX = 26 }; + +/// @brief Different policies when dilating trees with active tiles +/// @details +///
+///
IGNORE_TILES +///
Active tiles are ignores, i.e. only active voxels are dilates. +/// +///
EXPAND_TILES +///
Active tiles are expanded into active voxels and then dilated. +/// +///
PRESERVE_TILES +///
Active tiles remain unchanged but they still contribute to the +/// dilation as if they were active voxels. +///
+enum TilePolicy { IGNORE_TILES, EXPAND_TILES, PRESERVE_TILES }; + +/// @brief Topologically dilate all active values (i.e. both voxels +/// and tiles) in a tree using one of three nearest neighbor +/// connectivity patterns. +/// @note This method is fully multi-threaded and support active tiles! +/// +/// @param tree tree to be dilated +/// @param iterations number of iterations to apply the dilation +/// @param nn connectivity pattern of the dilation: either +/// face-adjacent (6 nearest neighbors), face- and edge-adjacent +/// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 +/// nearest neighbors). +/// @param mode Defined the policy for handling active tiles +/// (see above for details) +/// +/// @note The values of any voxels are unchanged. +template OPENVDB_STATIC_SPECIALIZATION +inline void dilateActiveValues(TreeType& tree, + int iterations = 1, + NearestNeighbors nn = NN_FACE, + TilePolicy mode = PRESERVE_TILES); + +/// @brief Topologically dilate all active values (i.e. both voxels +/// and tiles) in a tree using one of three nearest neighbor +/// connectivity patterns. +/// +/// @warning Unlike the method above this one takes a LeafManger, +/// however (unlike dilateVoxels method below) it offers no performance +/// advantage over the one that takes a tree. Its merely included for +/// API compatability. The leaf nodes in the manger are updated +/// after the dilation, which incurres a (very small) overhead. +/// +/// @note This method is fully multi-threaded and support active tiles! +/// +/// @param manager Leaf node manager for the tree to be dilated. +/// On exit it is updated to include all the leaf +/// nodes of the dilated tree. +/// @param iterations number of iterations to apply the dilation +/// @param nn connectivity pattern of the dilation: either +/// face-adjacent (6 nearest neighbors), face- and edge-adjacent +/// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 +/// nearest neighbors). +/// @param mode Defined the policy for handling active tiles +/// (see above for details) +/// +/// @note The values of any voxels are unchanged. +template OPENVDB_STATIC_SPECIALIZATION +inline void dilateActiveValues(tree::LeafManager& manager, + int iterations = 1, + NearestNeighbors nn = NN_FACE, + TilePolicy mode = PRESERVE_TILES); + + +/// @brief Topologically dilate all leaf-level active voxels in a tree +/// using one of three nearest neighbor connectivity patterns. +/// @warning This method is NOT multi-threaded and ignores active tiles! +/// +/// @param tree tree to be dilated +/// @param iterations number of iterations to apply the dilation +/// @param nn connectivity pattern of the dilation: either +/// face-adjacent (6 nearest neighbors), face- and edge-adjacent +/// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 +/// nearest neighbors). +/// +/// @note The values of any voxels are unchanged. +template OPENVDB_STATIC_SPECIALIZATION +inline void dilateVoxels(TreeType& tree, + int iterations = 1, + NearestNeighbors nn = NN_FACE); + +/// @brief Topologically dilate all leaf-level active voxels in a tree +/// using one of three nearest neighbor connectivity patterns. +/// @warning This method is NOT multi-threaded and ignores active tiles! +/// +/// @param manager LeafManager containing the tree to be dilated. +/// On exit it is updated to include all the leaf +/// nodes of the dilated tree. +/// @param iterations number of iterations to apply the dilation +/// @param nn connectivity pattern of the dilation: either +/// face-adjacent (6 nearest neighbors), face- and edge-adjacent +/// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 +/// nearest neighbors). +/// +/// @note The values of any voxels are unchanged. +template OPENVDB_STATIC_SPECIALIZATION +inline void dilateVoxels(tree::LeafManager& manager, + int iterations = 1, + NearestNeighbors nn = NN_FACE); + + +//@{ +/// @brief Topologically erode all leaf-level active voxels in the given tree. +/// @details That is, shrink the set of active voxels by @a iterations voxels +/// in the +x, -x, +y, -y, +z and -z directions, but don't change the values +/// of any voxels, only their active states. +/// @todo Currently operates only on leaf voxels; need to extend to tiles. +template OPENVDB_STATIC_SPECIALIZATION +inline void erodeVoxels(TreeType& tree, + int iterations=1, + NearestNeighbors nn = NN_FACE); + +template OPENVDB_STATIC_SPECIALIZATION +inline void erodeVoxels(tree::LeafManager& manager, + int iterations = 1, + NearestNeighbors nn = NN_FACE); +//@} + + +/// @brief Mark as active any inactive tiles or voxels in the given grid or tree +/// whose values are equal to @a value (optionally to within the given @a tolerance). +template +inline void activate( + GridOrTree&, + const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance = zeroVal() +); + + +/// @brief Mark as inactive any active tiles or voxels in the given grid or tree +/// whose values are equal to @a value (optionally to within the given @a tolerance). +template +inline void deactivate( + GridOrTree&, + const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance = zeroVal() +); + + +//////////////////////////////////////// + + +/// Mapping from a Log2Dim to a data type of size 2^Log2Dim bits +template struct DimToWord {}; +template<> struct DimToWord<3> { using Type = uint8_t; }; +template<> struct DimToWord<4> { using Type = uint16_t; }; +template<> struct DimToWord<5> { using Type = uint32_t; }; +template<> struct DimToWord<6> { using Type = uint64_t; }; + + +//////////////////////////////////////// + + +template +class Morphology +{ +public: + using ManagerType = tree::LeafManager; + + Morphology(TreeType& tree): + mOwnsManager(true), mManager(new ManagerType(tree)), mAcc(tree), mSteps(1) {} + Morphology(ManagerType* mgr): + mOwnsManager(false), mManager(mgr), mAcc(mgr->tree()), mSteps(1) {} + virtual ~Morphology() { if (mOwnsManager) delete mManager; } + + /// @brief Face-adjacent dilation pattern + void dilateVoxels6(); + /// @brief Face- and edge-adjacent dilation pattern. + void dilateVoxels18(); + /// @brief Face-, edge- and vertex-adjacent dilation pattern. + void dilateVoxels26(); + void dilateVoxels(int iterations = 1, NearestNeighbors nn = NN_FACE); + + /// @brief Face-adjacent erosion pattern. + void erodeVoxels6() { mSteps = 1; this->doErosion(NN_FACE); } + /// @brief Face- and edge-adjacent erosion pattern. + void erodeVoxels18() { mSteps = 1; this->doErosion(NN_FACE_EDGE); } + /// @brief Face-, edge- and vertex-adjacent erosion pattern. + void erodeVoxels26() { mSteps = 1; this->doErosion(NN_FACE_EDGE_VERTEX); } + void erodeVoxels(int iterations = 1, NearestNeighbors nn = NN_FACE) + { + mSteps = iterations; + this->doErosion(nn); + } + +protected: + + void doErosion(NearestNeighbors nn); + + using LeafType = typename TreeType::LeafNodeType; + using MaskType = typename LeafType::NodeMaskType; + using AccessorType = tree::ValueAccessor; + + const bool mOwnsManager; + ManagerType* mManager; + AccessorType mAcc; + int mSteps; + + static const int LEAF_DIM = LeafType::DIM; + static const int LEAF_LOG2DIM = LeafType::LOG2DIM; + using Word = typename DimToWord::Type; + + struct Neighbor { + LeafType* leaf;//null if a tile + bool init;//true if initialization is required + bool isOn;//true if an active tile + Neighbor() : leaf(nullptr), init(true) {} + inline void clear() { leaf = nullptr; init = true; } + template + void scatter(AccessorType& acc, const Coord &xyz, int indx, Word mask) + { + if (init) { + init = false; + Coord orig = xyz.offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM); + leaf = acc.probeLeaf(orig); + if ((leaf == nullptr) && !acc.isValueOn(orig)) leaf = acc.touchLeaf(orig); + } + static const int N = (LEAF_DIM - 1)*(DY + DX*LEAF_DIM); + if (leaf) leaf->getValueMask().template getWord(indx-N) |= mask; + } + + template + Word gather(AccessorType& acc, const Coord &xyz, int indx) + { + if (init) { + init = false; + Coord orig = xyz.offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM); + leaf = acc.probeLeaf(orig); + isOn = leaf ? false : acc.isValueOn(orig); + } + static const int N = (LEAF_DIM -1 )*(DY + DX*LEAF_DIM); + return leaf ? leaf->getValueMask().template getWord(indx-N) + : isOn ? ~Word(0) : Word(0); + } + };// Neighbor + + struct LeafCache + { + LeafCache(size_t n, TreeType& tree) : size(n), leafs(new LeafType*[n]), acc(tree) + { + onTile.setValuesOn(); + this->clear(); + } + ~LeafCache() { delete [] leafs; } + LeafType*& operator[](int offset) { return leafs[offset]; } + inline void clear() { for (size_t i = 0; i < size; ++i) leafs[i] = nullptr; } + inline void setOrigin(const Coord& xyz) { origin = &xyz; } + inline void scatter(int n, int indx) + { + assert(leafs[n]); + leafs[n]->getValueMask().template getWord(indx) |= mask; + } + template + inline void scatter(int n, int indx) + { + if (!leafs[n]) { + const Coord xyz = origin->offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM); + leafs[n] = acc.probeLeaf(xyz); + if (!leafs[n]) leafs[n] = acc.isValueOn(xyz) ? &onTile : acc.touchLeaf(xyz); + } + this->scatter(n, indx - (LEAF_DIM - 1)*(DY + DX*LEAF_DIM)); + } + inline Word gather(int n, int indx) + { + assert(leafs[n]); + return leafs[n]->getValueMask().template getWord(indx); + } + template + inline Word gather(int n, int indx) + { + if (!leafs[n]) { + const Coord xyz = origin->offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM); + leafs[n] = acc.probeLeaf(xyz); + if (!leafs[n]) leafs[n] = acc.isValueOn(xyz) ? &onTile : &offTile; + } + return this->gather(n, indx - (LEAF_DIM -1 )*(DY + DX*LEAF_DIM)); + } + // Scatters in the xy face-directions relative to leaf i1 + void scatterFacesXY(int x, int y, int i1, int n, int i2); + + // Scatters in the xy edge-directions relative to leaf i1 + void scatterEdgesXY(int x, int y, int i1, int n, int i2); + + Word gatherFacesXY(int x, int y, int i1, int n, int i2); + + Word gatherEdgesXY(int x, int y, int i1, int n, int i2); + + const Coord* origin; + size_t size; + LeafType** leafs; + LeafType onTile, offTile; + AccessorType acc; + Word mask; + };// LeafCache + + struct ErodeVoxelsOp { + using RangeT = tbb::blocked_range; + ErodeVoxelsOp(std::vector& masks, ManagerType& manager) + : mTask(nullptr), mSavedMasks(masks) , mManager(manager) {} + void runParallel(NearestNeighbors nn); + void operator()(const RangeT& r) const {mTask(const_cast(this), r);} + void erode6( const RangeT&) const; + void erode18(const RangeT&) const; + void erode26(const RangeT&) const; + private: + using FuncT = typename std::function; + FuncT mTask; + std::vector& mSavedMasks; + ManagerType& mManager; + };// ErodeVoxelsOp + + struct MaskManager { + MaskManager(std::vector& masks, ManagerType& manager) + : mMasks(masks) , mManager(manager), mSaveMasks(true) {} + + void save() { mSaveMasks = true; tbb::parallel_for(mManager.getRange(), *this); } + void update() { mSaveMasks = false; tbb::parallel_for(mManager.getRange(), *this); } + void operator()(const tbb::blocked_range& range) const + { + if (mSaveMasks) { + for (size_t i = range.begin(); i < range.end(); ++i) { + mMasks[i] = mManager.leaf(i).getValueMask(); + } + } else { + for (size_t i = range.begin(); i < range.end(); ++i) { + mManager.leaf(i).setValueMask(mMasks[i]); + } + } + } + private: + std::vector& mMasks; + ManagerType& mManager; + bool mSaveMasks; + };// MaskManager + + struct UpdateMasks { + UpdateMasks(const std::vector& masks, ManagerType& manager) + : mMasks(masks), mManager(manager) {} + void update() { tbb::parallel_for(mManager.getRange(), *this); } + void operator()(const tbb::blocked_range& r) const { + for (size_t i=r.begin(); i& mMasks; + ManagerType& mManager; + }; + struct CopyMasks { + CopyMasks(std::vector& masks, const ManagerType& manager) + : mMasks(masks), mManager(manager) {} + void copy() { tbb::parallel_for(mManager.getRange(), *this); } + void operator()(const tbb::blocked_range& r) const { + for (size_t i=r.begin(); i& mMasks; + const ManagerType& mManager; + }; + void copyMasks(std::vector& a, const ManagerType& b) {CopyMasks c(a, b); c.copy();} +};// Morphology + + +template +inline void +Morphology::dilateVoxels(int iterations, NearestNeighbors nn) +{ + for (int i=0; idilateVoxels18(); + break; + case NN_FACE_EDGE_VERTEX: + this->dilateVoxels26(); + break; + case NN_FACE: + default: + this->dilateVoxels6(); + } + } +} + + +template +inline void +Morphology::dilateVoxels6() +{ + /// @todo Currently operates only on leaf voxels; need to extend to tiles. + const int leafCount = static_cast(mManager->leafCount()); + + // Save the value masks of all leaf nodes. + std::vector savedMasks(leafCount); + this->copyMasks(savedMasks, *mManager); + LeafCache cache(7, mManager->tree()); + for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) { + const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node + cache[0] = &mManager->leaf(leafIdx); + cache.setOrigin(cache[0]->origin()); + for (int x = 0; x < LEAF_DIM; ++x ) { + for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) { + // Extract the portion of the original mask that corresponds to a row in z. + if (const Word w = oldMask.template getWord(n)) { + + // Dilate the current leaf in the +z and -z direction + cache.mask = Word(w | (w>>1) | (w<<1)); cache.scatter(0, n); + + // Dilate into neighbor leaf in the -z direction + if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) { + cache.template scatter< 0, 0,-1>(1, n); + } + // Dilate into neighbor leaf in the +z direction + if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) { + cache.template scatter< 0, 0, 1>(2, n); + } + // Dilate in the xy-face directions relative to the center leaf + cache.mask = w; cache.scatterFacesXY(x, y, 0, n, 3); + } + }// loop over y + }//loop over x + cache.clear(); + }//loop over leafs + + mManager->rebuildLeafArray(); +}//dilateVoxels6 + + +template +inline void +Morphology::dilateVoxels18() +{ + /// @todo Currently operates only on leaf voxels; need to extend to tiles. + const int leafCount = static_cast(mManager->leafCount()); + + // Save the value masks of all leaf nodes. + std::vector savedMasks(leafCount); + this->copyMasks(savedMasks, *mManager); + LeafCache cache(19, mManager->tree()); + Coord orig_mz, orig_pz;//origins of neighbor leaf nodes in the -z and +z directions + for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) { + const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node + cache[0] = &mManager->leaf(leafIdx); + orig_mz = cache[0]->origin().offsetBy(0, 0, -LEAF_DIM); + orig_pz = cache[0]->origin().offsetBy(0, 0, LEAF_DIM); + for (int x = 0; x < LEAF_DIM; ++x ) { + for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) { + if (const Word w = oldMask.template getWord(n)) { + { + cache.mask = Word(w | (w>>1) | (w<<1)); + cache.setOrigin(cache[0]->origin()); + cache.scatter(0, n); + cache.scatterFacesXY(x, y, 0, n, 3); + cache.mask = w; + cache.scatterEdgesXY(x, y, 0, n, 3); + } + if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) { + cache.setOrigin(cache[0]->origin()); + cache.template scatter< 0, 0,-1>(1, n); + cache.setOrigin(orig_mz); + cache.scatterFacesXY(x, y, 1, n, 11); + } + if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) { + cache.setOrigin(cache[0]->origin()); + cache.template scatter< 0, 0, 1>(2, n); + cache.setOrigin(orig_pz); + cache.scatterFacesXY(x, y, 2, n, 15); + } + } + }// loop over y + }//loop over x + cache.clear(); + }//loop over leafs + + mManager->rebuildLeafArray(); +}// dilateVoxels18 + + +template +inline void +Morphology::dilateVoxels26() +{ + const int leafCount = static_cast(mManager->leafCount()); + + // Save the value masks of all leaf nodes. + std::vector savedMasks(leafCount); + this->copyMasks(savedMasks, *mManager); + LeafCache cache(27, mManager->tree()); + Coord orig_mz, orig_pz;//origins of neighbor leaf nodes in the -z and +z directions + for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) { + const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node + cache[0] = &mManager->leaf(leafIdx); + orig_mz = cache[0]->origin().offsetBy(0, 0, -LEAF_DIM); + orig_pz = cache[0]->origin().offsetBy(0, 0, LEAF_DIM); + for (int x = 0; x < LEAF_DIM; ++x ) { + for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) { + if (const Word w = oldMask.template getWord(n)) { + { + cache.mask = Word(w | (w>>1) | (w<<1)); + cache.setOrigin(cache[0]->origin()); + cache.scatter(0, n); + cache.scatterFacesXY(x, y, 0, n, 3); + cache.scatterEdgesXY(x, y, 0, n, 3); + } + if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) { + cache.setOrigin(cache[0]->origin()); + cache.template scatter< 0, 0,-1>(1, n); + cache.setOrigin(orig_mz); + cache.scatterFacesXY(x, y, 1, n, 11); + cache.scatterEdgesXY(x, y, 1, n, 11); + } + if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) { + cache.setOrigin(cache[0]->origin()); + cache.template scatter< 0, 0, 1>(2, n); + cache.setOrigin(orig_pz); + cache.scatterFacesXY(x, y, 2, n, 19); + cache.scatterEdgesXY(x, y, 2, n, 19); + } + } + }// loop over y + }//loop over x + cache.clear(); + }//loop over leafs + + mManager->rebuildLeafArray(); +}// dilateVoxels26 + + +template +inline void +Morphology::LeafCache::scatterFacesXY(int x, int y, int i1, int n, int i2) +{ + // dilate current leaf or neighbor in the -x direction + if (x > 0) { + this->scatter(i1, n-LEAF_DIM); + } else { + this->template scatter<-1, 0, 0>(i2, n); + } + // dilate current leaf or neighbor in the +x direction + if (x < LEAF_DIM-1) { + this->scatter(i1, n+LEAF_DIM); + } else { + this->template scatter< 1, 0, 0>(i2+1, n); + } + // dilate current leaf or neighbor in the -y direction + if (y > 0) { + this->scatter(i1, n-1); + } else { + this->template scatter< 0,-1, 0>(i2+2, n); + } + // dilate current leaf or neighbor in the +y direction + if (y < LEAF_DIM-1) { + this->scatter(i1, n+1); + } else { + this->template scatter< 0, 1, 0>(i2+3, n); + } +} + + +template +inline void +Morphology::LeafCache::scatterEdgesXY(int x, int y, int i1, int n, int i2) +{ + if (x > 0) { + if (y > 0) { + this->scatter(i1, n-LEAF_DIM-1); + } else { + this->template scatter< 0,-1, 0>(i2+2, n-LEAF_DIM); + } + if (y < LEAF_DIM-1) { + this->scatter(i1, n-LEAF_DIM+1); + } else { + this->template scatter< 0, 1, 0>(i2+3, n-LEAF_DIM); + } + } else { + if (y < LEAF_DIM-1) { + this->template scatter<-1, 0, 0>(i2 , n+1); + } else { + this->template scatter<-1, 1, 0>(i2+7, n ); + } + if (y > 0) { + this->template scatter<-1, 0, 0>(i2 , n-1); + } else { + this->template scatter<-1,-1, 0>(i2+4, n ); + } + } + if (x < LEAF_DIM-1) { + if (y > 0) { + this->scatter(i1, n+LEAF_DIM-1); + } else { + this->template scatter< 0,-1, 0>(i2+2, n+LEAF_DIM); + } + if (y < LEAF_DIM-1) { + this->scatter(i1, n+LEAF_DIM+1); + } else { + this->template scatter< 0, 1, 0>(i2+3, n+LEAF_DIM); + } + } else { + if (y > 0) { + this->template scatter< 1, 0, 0>(i2+1, n-1); + } else { + this->template scatter< 1,-1, 0>(i2+6, n ); + } + if (y < LEAF_DIM-1) { + this->template scatter< 1, 0, 0>(i2+1, n+1); + } else { + this->template scatter< 1, 1, 0>(i2+5, n ); + } + } +} + + +template +inline void +Morphology::ErodeVoxelsOp::runParallel(NearestNeighbors nn) +{ + namespace ph = std::placeholders; + switch (nn) { + case NN_FACE_EDGE: + mTask = std::bind(&ErodeVoxelsOp::erode18, ph::_1, ph::_2); + break; + case NN_FACE_EDGE_VERTEX: + mTask = std::bind(&ErodeVoxelsOp::erode26, ph::_1, ph::_2); + break; + case NN_FACE: + default: + mTask = std::bind(&ErodeVoxelsOp::erode6, ph::_1, ph::_2); + } + tbb::parallel_for(mManager.getRange(), *this); +} + + +template +inline typename Morphology::Word +Morphology::LeafCache::gatherFacesXY(int x, int y, int i1, int n, int i2) +{ + // erode current leaf or neighbor in negative x-direction + Word w = x>0 ? this->gather(i1,n-LEAF_DIM) : this->template gather<-1,0,0>(i2, n); + + // erode current leaf or neighbor in positive x-direction + w = Word(w & (xgather(i1,n+LEAF_DIM):this->template gather<1,0,0>(i2+1,n))); + + // erode current leaf or neighbor in negative y-direction + w = Word(w & (y>0 ? this->gather(i1, n-1) : this->template gather<0,-1,0>(i2+2, n))); + + // erode current leaf or neighbor in positive y-direction + w = Word(w & (ygather(i1, n+1) : this->template gather<0,1,0>(i2+3, n))); + + return w; +} + + +template +inline typename Morphology::Word +Morphology::LeafCache::gatherEdgesXY(int x, int y, int i1, int n, int i2) +{ + Word w = ~Word(0); + + if (x > 0) { + w &= y > 0 ? this->gather(i1, n-LEAF_DIM-1) : + this->template gather< 0,-1, 0>(i2+2, n-LEAF_DIM); + w &= y < LEAF_DIM-1 ? this->gather(i1, n-LEAF_DIM+1) : + this->template gather< 0, 1, 0>(i2+3, n-LEAF_DIM); + } else { + w &= y < LEAF_DIM-1 ? this->template gather<-1, 0, 0>(i2 , n+1): + this->template gather<-1, 1, 0>(i2+7, n ); + w &= y > 0 ? this->template gather<-1, 0, 0>(i2 , n-1): + this->template gather<-1,-1, 0>(i2+4, n ); + } + if (x < LEAF_DIM-1) { + w &= y > 0 ? this->gather(i1, n+LEAF_DIM-1) : + this->template gather< 0,-1, 0>(i2+2, n+LEAF_DIM); + w &= y < LEAF_DIM-1 ? this->gather(i1, n+LEAF_DIM+1) : + this->template gather< 0, 1, 0>(i2+3, n+LEAF_DIM); + } else { + w &= y > 0 ? this->template gather< 1, 0, 0>(i2+1, n-1): + this->template gather< 1,-1, 0>(i2+6, n ); + w &= y < LEAF_DIM-1 ? this->template gather< 1, 0, 0>(i2+1, n+1): + this->template gather< 1, 1, 0>(i2+5, n ); + } + + return w; +} + + +template +inline void +Morphology::ErodeVoxelsOp::erode6(const RangeT& range) const +{ + LeafCache cache(7, mManager.tree()); + for (size_t leafIdx = range.begin(); leafIdx < range.end(); ++leafIdx) { + cache[0] = &mManager.leaf(leafIdx); + if (cache[0]->isEmpty()) continue; + cache.setOrigin(cache[0]->origin()); + MaskType& newMask = mSavedMasks[leafIdx];//original bit-mask of current leaf node + for (int x = 0; x < LEAF_DIM; ++x ) { + for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) { + // Extract the portion of the original mask that corresponds to a row in z. + if (Word& w = newMask.template getWord(n)) { + + // erode in two z directions (this is first since it uses the original w) + w = Word(w & + (Word(w<<1 | (cache.template gather<0,0,-1>(1, n)>>(LEAF_DIM-1))) & + Word(w>>1 | (cache.template gather<0,0, 1>(2, n)<<(LEAF_DIM-1))))); + + w = Word(w & cache.gatherFacesXY(x, y, 0, n, 3)); + } + }// loop over y + }//loop over x + cache.clear(); + }//loop over leafs +} + + +template +inline void +Morphology::ErodeVoxelsOp::erode18(const RangeT&) const +{ + OPENVDB_THROW(NotImplementedError, "tools::erode18 is not implemented yet!"); +} + + +template +inline void +Morphology::ErodeVoxelsOp::erode26(const RangeT&) const +{ + OPENVDB_THROW(NotImplementedError, "tools::erode26 is not implemented yet!"); +} + + +template +inline void +Morphology::doErosion(NearestNeighbors nn) +{ + /// @todo Currently operates only on leaf voxels; need to extend to tiles. + const size_t leafCount = mManager->leafCount(); + + // Save the value masks of all leaf nodes. + std::vector savedMasks(leafCount); + this->copyMasks(savedMasks, *mManager); + UpdateMasks a(savedMasks, *mManager); + ErodeVoxelsOp erode(savedMasks, *mManager); + + for (int i = 0; i < mSteps; ++i) { + erode.runParallel(nn); + a.update(); + } + + tools::pruneLevelSet(mManager->tree()); +} + + +//////////////////////////////////////// + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +dilateVoxels(tree::LeafManager& manager, int iterations, NearestNeighbors nn) +{ + if (iterations > 0 ) { + Morphology m(&manager); + m.dilateVoxels(iterations, nn); + } +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +dilateVoxels(TreeType& tree, int iterations, NearestNeighbors nn) +{ + if (iterations > 0 ) { + Morphology m(tree); + m.dilateVoxels(iterations, nn); + } +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +erodeVoxels(tree::LeafManager& manager, int iterations, NearestNeighbors nn) +{ + if (iterations > 0 ) { + Morphology m(&manager); + m.erodeVoxels(iterations, nn); + } +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +erodeVoxels(TreeType& tree, int iterations, NearestNeighbors nn) +{ + if (iterations > 0 ) { + Morphology m(tree); + m.erodeVoxels(iterations, nn); + } +} + + +//////////////////////////////////////// + + +namespace activation { + +template +class ActivationOp +{ +public: + using ValueT = typename TreeType::ValueType; + + ActivationOp(bool state, const ValueT& val, const ValueT& tol) + : mActivate(state) + , mValue(val) + , mTolerance(tol) + {} + + void operator()(const typename TreeType::ValueOnIter& it) const + { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + it.setValueOff(); + } + } + + void operator()(const typename TreeType::ValueOffIter& it) const + { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + it.setActiveState(/*on=*/true); + } + } + + void operator()(const typename TreeType::LeafIter& lit) const + { + using LeafT = typename TreeType::LeafNodeType; + LeafT& leaf = *lit; + if (mActivate) { + for (typename LeafT::ValueOffIter it = leaf.beginValueOff(); it; ++it) { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + leaf.setValueOn(it.pos()); + } + } + } else { + for (typename LeafT::ValueOnIter it = leaf.beginValueOn(); it; ++it) { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + leaf.setValueOff(it.pos()); + } + } + } + } + +private: + bool mActivate; + const ValueT mValue, mTolerance; +}; // class ActivationOp + +} // namespace activation + + +template +inline void +activate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance) +{ + using Adapter = TreeAdapter; + using TreeType = typename Adapter::TreeType; + + TreeType& tree = Adapter::tree(gridOrTree); + + activation::ActivationOp op(/*activate=*/true, value, tolerance); + + // Process all leaf nodes in parallel. + foreach(tree.beginLeaf(), op); + + // Process all other inactive values serially (because changing active states + // is not thread-safe unless no two threads modify the same node). + typename TreeType::ValueOffIter it = tree.beginValueOff(); + it.setMaxDepth(tree.treeDepth() - 2); + foreach(it, op, /*threaded=*/false); +} + + +template +inline void +deactivate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance) +{ + using Adapter = TreeAdapter; + using TreeType = typename Adapter::TreeType; + + TreeType& tree = Adapter::tree(gridOrTree); + + activation::ActivationOp op(/*activate=*/false, value, tolerance); + + // Process all leaf nodes in parallel. + foreach(tree.beginLeaf(), op); + + // Process all other active values serially (because changing active states + // is not thread-safe unless no two threads modify the same node). + typename TreeType::ValueOnIter it = tree.beginValueOn(); + it.setMaxDepth(tree.treeDepth() - 2); + foreach(it, op, /*threaded=*/false); +} + +/// @brief Class that performs multi-threaded dilation with support for active tiles. +/// @warning Dont use this class directly, instead call the function dilateActiveValues! +template +class DilationOp +{ + using MaskT = typename TreeT::template ValueConverter::Type; + using PoolT = tbb::enumerable_thread_specific; + using LeafT = typename MaskT::LeafNodeType; + + // Very light-weight member data + const int mIter;// number of iterations + const tools::NearestNeighbors mNN;//enum to specify the dilation scheme + PoolT *mPool;// pointer to the thread-local pool of mask trees + LeafT **mLeafs;// raw array of pointers to leaf nodes + +public: + + DilationOp(TreeT &tree, int iterations, NearestNeighbors nn, TilePolicy mode) + : mIter(iterations), mNN(nn), mPool(nullptr), mLeafs(nullptr) + { + const size_t numLeafs = this->init( tree, mode ); + const size_t numThreads = size_t(tbb::task_scheduler_init::default_num_threads()); + const size_t grainSize = math::Max(size_t(1), numLeafs/(2*numThreads)); + + MaskT mask; + PoolT pool(mask);// Scoped thread-local storage of mask trees + mPool = &pool; + + tbb::parallel_for(tbb::blocked_range(mLeafs, mLeafs+numLeafs, grainSize), *this); + + delete [] mLeafs;// no more need for the array of leaf node pointers + + using IterT = typename PoolT::iterator; + for (IterT it=pool.begin(); it!=pool.end(); ++it) mask.merge(*it);// fast stealing + + if (mode == PRESERVE_TILES) tools::prune(mask);//multithreaded + + tree.topologyUnion(mask);//multithreaded + } + + // This is required by tbb and should never be called directly + void operator()(const tbb::blocked_range &r) const + { + MaskT mask;// thread-local temporary mask tree + for (LeafT** it=r.begin(); it!=r.end(); ++it) mask.addLeaf( *it ); + tree::LeafManager manager(mask, r.begin(), r.end()); + tools::dilateVoxels(manager, mIter, mNN);// serial dilation of active voxels + mPool->local().merge(mask, MERGE_ACTIVE_STATES); + } +private: + + // Simple wrapper of a raw double-pointer to mimic a std container + struct MyArray { + using value_type = LeafT*;//required by Tree::stealNodes + value_type* ptr; + MyArray(value_type* array) : ptr(array) {} + void push_back(value_type leaf) { *ptr++ = leaf; }//required by Tree::stealNodes + }; + + // Convert active tiles to leafs and de-construct the tree into a linear array of leafs. + size_t linearize(MaskT& mask, TilePolicy mode) + { + if (mode != IGNORE_TILES) mask.voxelizeActiveTiles();//lightweight since this is a mask tree + const size_t numLeafs = mask.leafCount(); + mLeafs = new LeafT*[numLeafs];// fast pre-allocation + MyArray tmp(mLeafs); + mask.stealNodes(tmp);// serializes the mask tree and leaves it empty + return numLeafs; + } + + template + typename std::enable_if::value, size_t>::type + init(T& tree, TilePolicy mode) + { + return this->linearize(tree, mode); + } + + template + typename std::enable_if::value, size_t>::type + init(const T& tree, TilePolicy mode) + { + MaskT mask(tree, false, true, TopologyCopy()); + return this->linearize(mask, mode); + } + +};// DilationOp + +template +OPENVDB_STATIC_SPECIALIZATION inline void +dilateActiveValues(TreeType& tree, int iterations, NearestNeighbors nn, TilePolicy mode) +{ + if (iterations > 0 ) DilationOp tmp(tree, iterations, nn, mode); +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +dilateActiveValues(tree::LeafManager& manager, + int iterations, + NearestNeighbors nn, + TilePolicy mode) +{ + if (iterations > 0 ) { + DilationOp tmp(manager.tree(), iterations, nn, mode); + manager.rebuildLeafArray(); + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/MultiResGrid.h b/openvdb/tools/MultiResGrid.h new file mode 100644 index 00000000..284b07f1 --- /dev/null +++ b/openvdb/tools/MultiResGrid.h @@ -0,0 +1,951 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file MultiResGrid.h +/// +/// @author Ken Museth +/// +/// @warning This class is fairly new and as such has not seen a lot of +/// use in production. Please report any issues or request for new +/// features directly to ken.museth@dreamworks.com. +/// +/// @brief Multi-resolution grid that contains LoD sequences of trees +/// with powers of two refinements. +/// +/// @note While this class can arguably be used to implement a sparse +/// Multi-Grid solver it is currently intended as a means to +/// efficiently compute LoD levels for applications like rendering +/// +/// @note Prolongation means interpolation from coarse -> fine +/// @note Restriction means interpolation (or remapping) from fine -> coarse +/// +/// @todo Add option to define the level of the input grid (currenlty +/// 0) so as to allow for super-sampling. + +#ifndef OPENVDB_TOOLS_MULTIRESGRID_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_MULTIRESGRID_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Interpolation.h" +#include "Morphology.h" +#include "Prune.h" +#include "SignedFloodFill.h" +#include "ValueTransformer.h" + +#include +#include +#include + +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +template +class MultiResGrid: public MetaMap +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + using ValueType = typename TreeType::ValueType; + using ValueOnCIter = typename TreeType::ValueOnCIter; + using ValueOnIter = typename TreeType::ValueOnIter; + using TreePtr = typename TreeType::Ptr; + using ConstTreePtr = typename TreeType::ConstPtr; + using GridPtr = typename Grid::Ptr; + using ConstGridPtr = typename Grid::ConstPtr; + + ////////////////////////////////////////////////////////////////////// + + /// @brief Constructor of empty grids + /// @param levels The number of trees in this MultiResGrid + /// @param background Background value + /// @param voxelSize Size of a (uniform voxel). Defaults to one. + /// @note The multiple grids are all empty. + MultiResGrid(size_t levels, ValueType background, double voxelSize = 1.0); + + /// @brief Given an initial high-resolution grid this constructor + /// generates all the coarser grids by means of restriction. + /// @param levels The number of trees in this MultiResGrid + /// @param grid High-resolution input grid + /// @param useInjection Use restriction by injection, vs + /// full-weighting. It defaults to false and should rarely be used. + /// @note This constructor will perform a deep copy of the input + /// grid and use it as the highest level grid. + MultiResGrid(size_t levels, const Grid &grid, bool useInjection = false); + + /// @brief Given an initial high-resolution grid this constructor + /// generates all the coarser grids by means of restriction. + /// @param levels The number of trees in this MultiResGrid + /// @param grid High-resolution input grid + /// @param useInjection Use restriction by injection, vs + /// full-weighting. It defaults to false and should rarely be used. + /// @note This constructor will steal the input grid and use it + /// as the highest level grid. On output the grid is empty. + MultiResGrid(size_t levels, GridPtr grid, bool useInjection = false); + + ////////////////////////////////////////////////////////////////////// + + /// @brief Return the number of levels, i.e. trees, in this MultiResGrid + /// @note level 0 is the finest level and numLevels()-1 is the coarsest + /// level. + size_t numLevels() const { return mTrees.size(); } + + /// @brief Return the level of the finest grid (always 0) + static size_t finestLevel() { return 0; } + + /// @brief Return the level of the coarsest grid, i.e. numLevels()-1 + size_t coarsestLevel() const { return mTrees.size()-1; } + + ////////////////////////////////////////////////////////////////////// + + /// @brief Return a reference to the tree at the specified level + /// @param level The level of the tree to be returned + /// @note Level 0 is by definition the finest tree. + TreeType& tree(size_t level); + + /// @brief Return a const reference to the tree at the specified level + /// @param level The level of the tree to be returned + /// @note Level 0 is by definition the finest tree. + const TreeType& constTree(size_t level) const; + + /// @brief Return a shared pointer to the tree at the specified level + /// @param level The level of the tree to be returned + /// @note Level 0 is by definition the finest tree. + TreePtr treePtr(size_t level); + + /// @brief Return a const shared pointer to the tree at the specified level + /// @param level The level of the tree to be returned + /// @note Level 0 is by definition the finest tree. + ConstTreePtr constTreePtr(size_t level) const; + + /// @brief Return a reference to the tree at the finest level + TreeType& finestTree() { return *mTrees.front(); } + + /// @brief Return a const reference to the tree at the finest level + const TreeType& finestConstTree() const { return *mTrees.front(); } + + /// @brief Return a shared pointer to the tree at the finest level + TreePtr finestTreePtr() { return mTrees.front(); } + + /// @brief Return a const shared pointer to the tree at the finest level + ConstTreePtr finestConstTreePtr() const { return mTrees.front(); } + + /// @brief Return a reference to the tree at the coarsest level + TreeType& coarsestTree() { return *mTrees.back(); } + + /// @brief Return a const reference to the tree at the coarsest level + const TreeType& coarsestConstTree() const { return *mTrees.back(); } + + /// @brief Return a shared pointer to the tree at the coarsest level + TreePtr coarsestTreePtr() { return mTrees.back(); } + + /// @brief Return a const shared pointer to the tree at the coarsest level + ConstTreePtr coarsestConstTreePtr() const { return mTrees.back(); } + + ////////////////////////////////////////////////////////////////////// + + /// @brief Return a shared pointer to the grid at the specified integer level + /// @param level Integer level of the grid to be returned + /// @note Level 0 is by definition the finest grid. + GridPtr grid(size_t level); + + /// @brief Return a const shared pointer to the grid at the specified level + /// @param level The level of the grid to be returned + /// @note Level 0 is by definition the finest grid. + ConstGridPtr grid(size_t level) const; + + /// @brief Return a shared pointer to a new grid at the specified + /// floating-point level. + /// @param level Floating-point level of the grid to be returned + /// @param grainSize Grain size for the multi-threading + /// @details Interpolation of the specified order is performed + /// between the bracketing integer levels. + /// @note Level 0 is by definition the finest grid. + template + GridPtr createGrid(float level, size_t grainSize = 1) const; + + /// @brief Return a shared pointer to a vector of all the base + /// grids in this instance of the MultiResGrid. + /// @brief This method is useful for I/O + GridPtrVecPtr grids(); + + /// @brief Return a const shared pointer to a vector of all the base + /// grids in this instance of the MultiResGrid. + /// @brief This method is useful for I/O + GridCPtrVecPtr grids() const; + + ////////////////////////////////////////////////////////////////////// + + //@{ + /// @brief Return a reference to the finest grid's transform, which might be + /// shared with other grids. + /// @note Calling setTransform() on this grid invalidates all references + /// previously returned by this method. + /// @warning The transform is relative to the finest level (=0) grid! + math::Transform& transform() { return *mTransform; } + const math::Transform& transform() const { return *mTransform; } + const math::Transform& constTransform() const { return *mTransform; } + //@} + + ////////////////////////////////////////////////////////////////////// + + //@{ + /// @brief Return the floating-point index coordinate at out_level given + /// the index coordinate in_xyz at in_level. + static Vec3R xyz(const Coord& in_ijk, size_t in_level, size_t out_level); + static Vec3R xyz(const Vec3R& in_xyz, size_t in_level, size_t out_level); + static Vec3R xyz(const Vec3R& in_xyz, double in_level, double out_level); + //@} + + ////////////////////////////////////////////////////////////////////// + + + + //@{ + /// @brief Return the value at the specified coordinate position using + /// interpolation of the specified order into the tree at the out_level. + /// + /// @details First in_ijk is mapped from index space at in_level to + /// out_level, and then a value is interpolated from the tree at out_level. + /// + /// @param in_ijk Index coordinate position relative to tree at in_level + /// @param in_level Integer level of the input coordinate in_ijk + /// @param out_level Integer level of the interpolated value + template + ValueType sampleValue(const Coord& in_ijk, size_t in_level, size_t out_level) const; + template + ValueType sampleValue(const Vec3R& in_ijk, size_t in_level, size_t out_level) const; + //@} + + /// @brief Return the value at the specified integer coordinate position + /// and level using interpolation of the specified order. + /// @param ijk Integer coordinate position relative to the highest level (=0) grid + /// @param level Floating-point level from which to interpolate the value. + /// @brief Non-integer values of the level will use linear-interpolation + /// between the neighboring integer levels. + template + ValueType sampleValue(const Coord& ijk, double level) const; + + /// @brief Return the value at the specified floating-point coordinate position + /// and level using interpolation of the specified order. + /// @param xyz Floating-point coordinate position relative to the highest level grid + /// @param level Floating-point level from which to interpolate + /// the value. + /// @brief Non-integer values of the level will use linear-interpolation + /// between the neighboring integer levels. + template + ValueType sampleValue(const Vec3R& xyz, double level) const; + + ////////////////////////////////////////////////////////////////////// + + /// @brief Return the value at coordinate location in @a level tree + /// from the coarser tree at @a level+1 using trilinear interpolation + /// @param coords input coords relative to the fine tree at level + /// @param level The fine level to receive values from the coarser + /// level-1 + /// @note Prolongation means to interpolation from coarse -> fine + ValueType prolongateVoxel(const Coord& coords, const size_t level) const; + + + /// (coarse->fine) Populates all the active voxel values in a fine (@a level) tree + /// from the coarse (@a level+1) tree using linear interpolation + /// This transforms multiple values of the tree in parallel + void prolongateActiveVoxels(size_t destlevel, size_t grainSize = 1); + + ////////////////////////////////////////////////////////////////////// + + /// Populate a coordinate location in @a level (coarse) tree + /// from the @a level-1 (fine) tree using trilinear interpolation + /// input coords are relative to the mTree[level] (coarse) + /// @note Restriction means remapping from fine -> coarse + ValueType restrictVoxel(Coord ijk, const size_t level, bool useInjection = false) const; + + /// (fine->coarse) Populates all the active voxel values in the coarse (@a level) tree + /// from the fine (@a level-1) tree using trilinear interpolation. + /// For cell-centered data, this is equivalent to an average + /// For vertex-centered data this is equivalent to transferring the data + /// from the fine vertex directly above the coarse vertex. + /// This transforms multiple values of the tree in parallel + void restrictActiveVoxels(size_t destlevel, size_t grainSize = 1); + + /// Output a human-readable description of this MultiResGrid + void print(std::ostream& = std::cout, int verboseLevel = 1) const; + + /// @brief Return a string with the name of this MultiResGrid + std::string getName() const + { + if (Metadata::ConstPtr meta = (*this)[GridBase::META_GRID_NAME]) return meta->str(); + return ""; + } + + /// @brief Set the name of this MultiResGrid + void setName(const std::string& name) + { + this->removeMeta(GridBase::META_GRID_NAME); + this->insertMeta(GridBase::META_GRID_NAME, StringMetadata(name)); + } + + /// Return the class of volumetric data (level set, fog volume, etc.) stored in this grid. + GridClass getGridClass() const + { + typename StringMetadata::ConstPtr s = + this->getMetadata(GridBase::META_GRID_CLASS); + return s ? GridBase::stringToGridClass(s->value()) : GRID_UNKNOWN; + } + + /// Specify the class of volumetric data (level set, fog volume, etc.) stored in this grid. + void setGridClass(GridClass cls) + { + this->insertMeta(GridBase::META_GRID_CLASS, StringMetadata(GridBase::gridClassToString(cls))); + } + + /// Remove the setting specifying the class of this grid's volumetric data. + void clearGridClass() { this->removeMeta(GridBase::META_GRID_CLASS); } + +private: + + MultiResGrid(const MultiResGrid& other);//disallow copy construction + MultiResGrid& operator=(const MultiResGrid& other);//disallow copy assignment + + // For optimal performance we disable registration of the ValueAccessor + using Accessor = tree::ValueAccessor; + using ConstAccessor = tree::ValueAccessor; + + void topDownRestrict(bool useInjection); + + inline void initMeta(); + + // Private struct that concurrently creates a mask of active voxel + // in a coarse tree from the active voxels in a fine tree + struct MaskOp; + + /// Private struct that performs multi-threaded restriction + struct RestrictOp; + + /// Private struct that performs multi-threaded prolongation + struct ProlongateOp; + + // Private struct that performs multi-threaded computation of grids a fraction levels + template + struct FractionOp; + + /// Private template struct that performs the actual multi-threading + template struct CookOp; + + // Array of shared pointer to trees, level 0 has the highest resolution. + std::vector mTrees; + // Shared pointer to a transform associated with the finest level grid + typename math::Transform::Ptr mTransform; +};// MultiResGrid + +template +MultiResGrid:: +MultiResGrid(size_t levels, ValueType background, double voxelSize) + : mTrees(levels) + , mTransform(math::Transform::createLinearTransform( voxelSize )) +{ + this->initMeta(); + for (size_t i=0; i +MultiResGrid:: +MultiResGrid(size_t levels, const Grid &grid, bool useInjection) + : MetaMap(grid) + , mTrees(levels) + , mTransform( grid.transform().copy() ) +{ + this->initMeta(); + mTrees[0].reset( new TreeType( grid.tree() ) );// deep copy input tree + mTrees[0]->voxelizeActiveTiles(); + this->topDownRestrict(useInjection); +} + +template +MultiResGrid:: +MultiResGrid(size_t levels, GridPtr grid, bool useInjection) + : MetaMap(*grid) + , mTrees(levels) + , mTransform( grid->transform().copy() ) +{ + this->initMeta(); + mTrees[0] = grid->treePtr();// steal tree from input grid + mTrees[0]->voxelizeActiveTiles(); + grid->newTree(); + this->topDownRestrict(useInjection); +} + +template +inline TreeType& MultiResGrid:: +tree(size_t level) +{ + assert( level < mTrees.size() ); + return *mTrees[level]; +} + +template +inline const TreeType& MultiResGrid:: +constTree(size_t level) const +{ + assert( level < mTrees.size() ); + return *mTrees[level]; +} + +template +inline typename TreeType::Ptr MultiResGrid:: +treePtr(size_t level) +{ + assert( level < mTrees.size() ); + return mTrees[level]; +} + +template +inline typename TreeType::ConstPtr MultiResGrid:: +constTreePtr(size_t level) const +{ + assert( level < mTrees.size() ); + return mTrees[level]; +} + +template +typename Grid::Ptr MultiResGrid:: +grid(size_t level) +{ + typename Grid::Ptr grid = Grid::create(this->treePtr(level)); + math::Transform::Ptr xform = mTransform->copy(); + if (level>0) xform->preScale( Real(1 << level) ); + grid->setTransform( xform ); + grid->insertMeta( *this->copyMeta() ); + grid->insertMeta( "MultiResGrid_Level", Int64Metadata(level)); + std::stringstream ss; + ss << this->getName() << "_level_" << level; + grid->setName( ss.str() ); + return grid; +} + +template +inline typename Grid::ConstPtr MultiResGrid:: +grid(size_t level) const +{ + return const_cast(this)->grid(level); +} + +template +template +typename Grid::Ptr MultiResGrid:: +createGrid(float level, size_t grainSize) const +{ + assert( level >= 0.0f && level <= float(mTrees.size()-1) ); + + typename Grid::Ptr grid(new Grid(this->constTree(0).background())); + math::Transform::Ptr xform = mTransform->copy(); + xform->preScale( math::Pow(2.0f, level) ); + grid->setTransform( xform ); + grid->insertMeta( *(this->copyMeta()) ); + grid->insertMeta( "MultiResGrid_Level", FloatMetadata(level) ); + std::stringstream ss; + ss << this->getName() << "_level_" << level; + grid->setName( ss.str() ); + + if ( size_t(floorf(level)) == size_t(ceilf(level)) ) { + grid->setTree( this->constTree( size_t(floorf(level))).copy() ); + } else { + FractionOp tmp(*this, grid->tree(), level, grainSize); + if ( grid->getGridClass() == GRID_LEVEL_SET ) { + signedFloodFill( grid->tree() ); + pruneLevelSet( grid->tree() );//only creates inactive tiles + } + } + + return grid; +} + +template +GridPtrVecPtr MultiResGrid:: +grids() +{ + GridPtrVecPtr grids( new GridPtrVec ); + for (size_t level=0; levelpush_back(this->grid(level)); + return grids; +} + +template +GridCPtrVecPtr MultiResGrid:: +grids() const +{ + GridCPtrVecPtr grids( new GridCPtrVec ); + for (size_t level=0; levelpush_back(this->grid(level)); + return grids; +} + +template +Vec3R MultiResGrid:: +xyz(const Coord& in_ijk, size_t in_level, size_t out_level) +{ + return Vec3R( in_ijk.data() ) * Real(1 << in_level) / Real(1 << out_level); +} + +template +Vec3R MultiResGrid:: +xyz(const Vec3R& in_xyz, size_t in_level, size_t out_level) +{ + return in_xyz * Real(1 << in_level) / Real(1 << out_level); +} + +template +Vec3R MultiResGrid:: +xyz(const Vec3R& in_xyz, double in_level, double out_level) +{ + return in_xyz * math::Pow(2.0, in_level - out_level); + +} + +template +template +typename TreeType::ValueType MultiResGrid:: +sampleValue(const Coord& in_ijk, size_t in_level, size_t out_level) const +{ + assert( in_level >= 0 && in_level < mTrees.size() ); + assert( out_level >= 0 && out_level < mTrees.size() ); + const ConstAccessor acc(*mTrees[out_level]);// has disabled registration! + return tools::Sampler::sample( acc, this->xyz(in_ijk, in_level, out_level) ); +} + +template +template +typename TreeType::ValueType MultiResGrid:: +sampleValue(const Vec3R& in_xyz, size_t in_level, size_t out_level) const +{ + assert( in_level >= 0 && in_level < mTrees.size() ); + assert( out_level >= 0 && out_level < mTrees.size() ); + const ConstAccessor acc(*mTrees[out_level]);// has disabled registration! + return tools::Sampler::sample( acc, this->xyz(in_xyz, in_level, out_level) ); +} + +template +template +typename TreeType::ValueType MultiResGrid:: +sampleValue(const Coord& ijk, double level) const +{ + assert( level >= 0.0 && level <= double(mTrees.size()-1) ); + const size_t level0 = size_t(floor(level)), level1 = size_t(ceil(level)); + const ValueType v0 = this->template sampleValue( ijk, 0, level0 ); + if ( level0 == level1 ) return v0; + assert( level1 - level0 == 1 ); + const ValueType v1 = this->template sampleValue( ijk, 0, level1 ); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const ValueType a = ValueType(level1 - level); + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return a * v0 + (ValueType(1) - a) * v1; +} + +template +template +typename TreeType::ValueType MultiResGrid:: +sampleValue(const Vec3R& xyz, double level) const +{ + assert( level >= 0.0 && level <= double(mTrees.size()-1) ); + const size_t level0 = size_t(floor(level)), level1 = size_t(ceil(level)); + const ValueType v0 = this->template sampleValue( xyz, 0, level0 ); + if ( level0 == level1 ) return v0; + assert( level1 - level0 == 1 ); + const ValueType v1 = this->template sampleValue( xyz, 0, level1 ); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const ValueType a = ValueType(level1 - level); + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + return a * v0 + (ValueType(1) - a) * v1; +} + +template +typename TreeType::ValueType MultiResGrid:: +prolongateVoxel(const Coord& ijk, const size_t level) const +{ + assert( level+1 < mTrees.size() ); + const ConstAccessor acc(*mTrees[level + 1]);// has disabled registration! + return ProlongateOp::run(ijk, acc); +} + +template +void MultiResGrid:: +prolongateActiveVoxels(size_t destlevel, size_t grainSize) +{ + assert( destlevel < mTrees.size()-1 ); + TreeType &fineTree = *mTrees[ destlevel ]; + const TreeType &coarseTree = *mTrees[ destlevel+1 ]; + CookOp tmp( coarseTree, fineTree, grainSize ); +} + +template +typename TreeType::ValueType MultiResGrid:: +restrictVoxel(Coord ijk, const size_t destlevel, bool useInjection) const +{ + assert( destlevel > 0 && destlevel < mTrees.size() ); + const TreeType &fineTree = *mTrees[ destlevel-1 ]; + if ( useInjection ) return fineTree.getValue(ijk<<1); + const ConstAccessor acc( fineTree );// has disabled registration! + return RestrictOp::run( ijk, acc); +} + +template +void MultiResGrid:: +restrictActiveVoxels(size_t destlevel, size_t grainSize) +{ + assert( destlevel > 0 && destlevel < mTrees.size() ); + const TreeType &fineTree = *mTrees[ destlevel-1 ]; + TreeType &coarseTree = *mTrees[ destlevel ]; + CookOp tmp( fineTree, coarseTree, grainSize ); +} + +template +void MultiResGrid:: +print(std::ostream& os, int verboseLevel) const +{ + os << "MultiResGrid with " << mTrees.size() << " levels\n"; + for (size_t i=0; iprint(os, verboseLevel); + } + + if ( MetaMap::metaCount() > 0) { + os << "Additional metadata:" << std::endl; + for (ConstMetaIterator it = beginMeta(), end = endMeta(); it != end; ++it) { + os << " " << it->first; + if (it->second) { + const std::string value = it->second->str(); + if (!value.empty()) os << ": " << value; + } + os << "\n"; + } + } + + os << "Transform:" << std::endl; + transform().print(os, /*indent=*/" "); + os << std::endl; +} + +template +void MultiResGrid:: +initMeta() +{ + const size_t levels = this->numLevels(); + if (levels < 2) { + OPENVDB_THROW(ValueError, "MultiResGrid: at least two levels are required"); + } + this->insertMeta("MultiResGrid_Levels", Int64Metadata( levels ) ); +} + +template +void MultiResGrid:: +topDownRestrict(bool useInjection) +{ + const bool isLevelSet = this->getGridClass() == GRID_LEVEL_SET; + for (size_t n=1; n> 1, *it ); + } + } else {// Restriction by full-weighting + MaskOp tmp(fineTree, coarseTree, 128); + this->restrictActiveVoxels(n, 64); + } + if ( isLevelSet ) { + tools::signedFloodFill( coarseTree ); + tools::pruneLevelSet( coarseTree );//only creates inactive tiles + } + }// loop over grid levels +} + +template +struct MultiResGrid::MaskOp +{ + using MaskT = typename TreeType::template ValueConverter::Type; + using PoolType = tbb::enumerable_thread_specific; + using ManagerT = tree::LeafManager; + using RangeT = typename ManagerT::LeafRange; + using VoxelIterT = typename ManagerT::LeafNodeType::ValueOnCIter; + + MaskOp(const TreeType& fineTree, TreeType& coarseTree, size_t grainSize = 1) + : mPool(new PoolType( coarseTree ) )// empty coarse tree acts as examplar + { + assert( coarseTree.empty() ); + + // Create Mask of restruction performed on fineTree + MaskT mask(fineTree, false, true, TopologyCopy() ); + + // Muli-threaded dilation which also linearizes the tree to leaf nodes + tools::dilateActiveValues(mask, 1, NN_FACE_EDGE_VERTEX, EXPAND_TILES); + + // Restriction by injection using thread-local storage of coarse tree masks + ManagerT leafs( mask ); + tbb::parallel_for(leafs.leafRange( grainSize ), *this); + + // multithreaded union of thread-local coarse tree masks with the coarse tree + using IterT = typename PoolType::const_iterator; + for (IterT it=mPool->begin(); it!=mPool->end(); ++it) coarseTree.topologyUnion( *it ); + delete mPool; + } + void operator()(const RangeT& range) const + { + Accessor coarseAcc( mPool->local() );// disabled registration + for (typename RangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + Coord ijk = voxelIter.getCoord(); + if ( (ijk[2] & 1) || (ijk[1] & 1) || (ijk[0] & 1) ) continue;//no overlap + coarseAcc.setValueOn( ijk >> 1 );//injection from fine to coarse level + }//loop over active voxels in the fine tree + }// loop over leaf nodes in the fine tree + } + PoolType* mPool; +};// MaskOp + +template +template +struct MultiResGrid::FractionOp +{ + using MaskT = typename TreeType::template ValueConverter::Type; + using PoolType = tbb::enumerable_thread_specific; + using PoolIterT = typename PoolType::iterator; + using Manager1 = tree::LeafManager; + using Manager2 = tree::LeafManager; + using Range1 = typename Manager1::LeafRange; + using Range2 = typename Manager2::LeafRange; + + FractionOp(const MultiResGrid& parent, + TreeType& midTree, + float level, + size_t grainSize = 1) + : mLevel( level ) + , mPool(nullptr) + , mTree0( &*(parent.mTrees[size_t(floorf(level))]) )//high-resolution + , mTree1( &*(parent.mTrees[size_t(ceilf(level))]) ) //low-resolution + { + assert( midTree.empty() ); + assert( mTree0 != mTree1 ); + + // Create a pool of thread-local masks + MaskT examplar( false ); + mPool = new PoolType( examplar ); + + {// create mask from re-mapping coarse tree to mid-level tree + tree::LeafManager manager( *mTree1 ); + tbb::parallel_for( manager.leafRange(grainSize), *this ); + } + + // Multi-threaded dilation of mask + tbb::parallel_for(tbb::blocked_range(mPool->begin(),mPool->end(),1), *this); + + // Union thread-local coarse tree masks into the coarse tree + for (PoolIterT it=mPool->begin(); it!=mPool->end(); ++it) midTree.topologyUnion( *it ); + delete mPool; + + {// Interpolate values into the static mid level tree + Manager2 manager( midTree ); + tbb::parallel_for(manager.leafRange(grainSize), *this); + } + } + void operator()(const Range1& range) const + { + using VoxelIter = typename Manager1::LeafNodeType::ValueOnCIter; + // Let mLevel = level + frac, where + // level is integer part of mLevel and frac is the fractional part + // low-res voxel size in world units = dx1 = 2^(level + 1) + // mid-res voxel size in world units = dx = 2^(mLevel) = 2^(level + frac) + // low-res index -> world: ijk * dx1 + // world -> mid-res index: world / dx + // low-res index -> mid-res index: (ijk * dx1) / dx = ijk * scale where + // scale = dx1/dx = 2^(level+1)/2^(level+frac) = 2^(1-frac) + const float scale = math::Pow(2.0f, 1.0f - math::FractionalPart(mLevel)); + tree::ValueAccessor acc( mPool->local() );// disabled registration + for (typename Range1::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + for (VoxelIter voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + Coord ijk = voxelIter.getCoord(); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto value0 = ijk[0] * scale; + const auto value1 = ijk[1] * scale; + const auto value2 = ijk[2] * scale; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + ijk[0] = int(math::Round(value0)); + ijk[1] = int(math::Round(value1)); + ijk[2] = int(math::Round(value2)); + + acc.setValueOn( ijk ); + }//loop over active voxels in the fine tree + }// loop over leaf nodes in the fine tree + } + void operator()(const tbb::blocked_range& range) const + { + for (PoolIterT it=range.begin(); it!=range.end(); ++it) { + tools::dilateVoxels( *it, 1, NN_FACE_EDGE_VERTEX); + } + } + void operator()(const Range2 &r) const + { + using VoxelIter = typename TreeType::LeafNodeType::ValueOnIter; + // Let mLevel = level + frac, where + // level is integer part of mLevel and frac is the fractional part + // high-res voxel size in world units = dx0 = 2^(level) + // low-res voxel size in world units = dx1 = 2^(level+1) + // mid-res voxel size in world units = dx = 2^(mLevel) = 2^(level + frac) + // mid-res index -> world: ijk * dx + // world -> high-res index: world / dx0 + // world -> low-res index: world / dx1 + // mid-res index -> high-res index: (ijk * dx) / dx0 = ijk * scale0 where + // scale0 = dx/dx0 = 2^(level+frac)/2^(level) = 2^(frac) + // mid-res index -> low-res index: (ijk * dx) / dx1 = ijk * scale1 where + // scale1 = dx/dx1 = 2^(level+frac)/2^(level+1) = 2^(frac-1) + const float b = math::FractionalPart(mLevel), a = 1.0f - b; + const float scale0 = math::Pow( 2.0f, b ); + const float scale1 = math::Pow( 2.0f,-a ); + ConstAccessor acc0( *mTree0 ), acc1( *mTree1 ); + for (typename Range2::Iterator leafIter = r.begin(); leafIter; ++leafIter) { + for (VoxelIter voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Vec3R xyz = Vec3R( voxelIter.getCoord().data() );// mid level coord + const ValueType v0 = tools::Sampler::sample( acc0, xyz * scale0 ); + const ValueType v1 = tools::Sampler::sample( acc1, xyz * scale1 ); + OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + const auto value0 = a*v0; + const auto value1 = b*v1; + OPENVDB_NO_TYPE_CONVERSION_WARNING_END + voxelIter.setValue( ValueType(value0 + value1) ); + } + } + } + const float mLevel; + PoolType* mPool; + const TreeType *mTree0, *mTree1; +};// FractionOp + + +template +template +struct MultiResGrid::CookOp +{ + using ManagerT = tree::LeafManager; + using RangeT = typename ManagerT::LeafRange; + + CookOp(const TreeType& srcTree, TreeType& dstTree, size_t grainSize): acc(srcTree) + { + ManagerT leafs(dstTree); + tbb::parallel_for(leafs.leafRange(grainSize), *this); + } + CookOp(const CookOp &other): acc(other.acc.tree()) {} + + void operator()(const RangeT& range) const + { + for (auto leafIt = range.begin(); leafIt; ++leafIt) { + auto& phi = leafIt.buffer(0); + for (auto voxelIt = leafIt->beginValueOn(); voxelIt; ++voxelIt) { + phi.setValue(voxelIt.pos(), OperatorType::run(voxelIt.getCoord(), acc)); + } + } + } + + const ConstAccessor acc; +};// CookOp + + +template +struct MultiResGrid::RestrictOp +{ + /// @brief Static method that performs restriction by full weighting + /// @param ijk Coordinate location on the coarse tree + /// @param acc ValueAccessor to the fine tree + static ValueType run(Coord ijk, const ConstAccessor &acc) + { + ijk <<= 1; + // Overlapping grid point + ValueType v = 8*acc.getValue(ijk); + // neighbors in one axial direction + v += 4*(acc.getValue(ijk.offsetBy(-1, 0, 0)) + acc.getValue(ijk.offsetBy( 1, 0, 0)) +// x + acc.getValue(ijk.offsetBy( 0,-1, 0)) + acc.getValue(ijk.offsetBy( 0, 1, 0)) +// y + acc.getValue(ijk.offsetBy( 0, 0,-1)) + acc.getValue(ijk.offsetBy( 0, 0, 1)));// z + // neighbors in two axial directions + v += 2*(acc.getValue(ijk.offsetBy(-1,-1, 0)) + acc.getValue(ijk.offsetBy(-1, 1, 0)) +// xy + acc.getValue(ijk.offsetBy( 1,-1, 0)) + acc.getValue(ijk.offsetBy( 1, 1, 0)) +// xy + acc.getValue(ijk.offsetBy(-1, 0,-1)) + acc.getValue(ijk.offsetBy(-1, 0, 1)) +// xz + acc.getValue(ijk.offsetBy( 1, 0,-1)) + acc.getValue(ijk.offsetBy( 1, 0, 1)) +// xz + acc.getValue(ijk.offsetBy( 0,-1,-1)) + acc.getValue(ijk.offsetBy( 0,-1, 1)) +// yz + acc.getValue(ijk.offsetBy( 0, 1,-1)) + acc.getValue(ijk.offsetBy( 0, 1, 1)));// yz + // neighbors in three axial directions + for (int i=-1; i<=1; i+=2) { + for (int j=-1; j<=1; j+=2) { + for (int k=-1; k<=1; k+=2) v += acc.getValue(ijk.offsetBy(i,j,k));// xyz + } + } + v *= ValueType(1.0f/64.0f); + return v; + } +};// RestrictOp + +template +struct MultiResGrid::ProlongateOp +{ + /// @brief Interpolate values from a coarse grid (acc) into the index space (ijk) of a fine grid + /// @param ijk Coordinate location on the fine tree + /// @param acc ValueAccessor to the coarse tree + static ValueType run(const Coord& ijk, const ConstAccessor &acc) + { + switch ( (ijk[0] & 1) | ((ijk[1] & 1) << 1) | ((ijk[2] & 1) << 2) ) { + case 0:// all even + return acc.getValue(ijk>>1); + case 1:// x is odd + return ValueType(0.5)*(acc.getValue(ijk.offsetBy(-1,0,0)>>1) + + acc.getValue(ijk.offsetBy( 1,0,0)>>1)); + case 2:// y is odd + return ValueType(0.5)*(acc.getValue(ijk.offsetBy(0,-1,0)>>1) + + acc.getValue(ijk.offsetBy(0, 1,0)>>1)); + case 3:// x&y are odd + return ValueType(0.25)*(acc.getValue(ijk.offsetBy(-1,-1,0)>>1) + + acc.getValue(ijk.offsetBy(-1, 1,0)>>1) + + acc.getValue(ijk.offsetBy( 1,-1,0)>>1) + + acc.getValue(ijk.offsetBy( 1, 1,0)>>1)); + case 4:// z is odd + return ValueType(0.5)*(acc.getValue(ijk.offsetBy(0,0,-1)>>1) + + acc.getValue(ijk.offsetBy(0,0, 1)>>1)); + case 5:// x&z are odd + return ValueType(0.25)*(acc.getValue(ijk.offsetBy(-1,0,-1)>>1) + + acc.getValue(ijk.offsetBy(-1,0, 1)>>1) + + acc.getValue(ijk.offsetBy( 1,0,-1)>>1) + + acc.getValue(ijk.offsetBy( 1,0, 1)>>1)); + case 6:// y&z are odd + return ValueType(0.25)*(acc.getValue(ijk.offsetBy(0,-1,-1)>>1) + + acc.getValue(ijk.offsetBy(0,-1, 1)>>1) + + acc.getValue(ijk.offsetBy(0, 1,-1)>>1) + + acc.getValue(ijk.offsetBy(0, 1, 1)>>1)); + } + // all are odd + ValueType v = zeroVal(); + for (int i=-1; i<=1; i+=2) { + for (int j=-1; j<=1; j+=2) { + for (int k=-1; k<=1; k+=2) v += acc.getValue(ijk.offsetBy(i,j,k)>>1);// xyz + } + } + return ValueType(0.125) * v; + } +};// ProlongateOp + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MULTIRESGRID_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/ParticleAtlas.h b/openvdb/tools/ParticleAtlas.h new file mode 100644 index 00000000..07338fea --- /dev/null +++ b/openvdb/tools/ParticleAtlas.h @@ -0,0 +1,1033 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file ParticleAtlas.h +/// +/// @brief Space-partitioning acceleration structure for particles, points with +/// radius. Partitions particle indices into voxels to accelerate range +/// and nearest neighbor searches. +/// +/// @note   This acceleration structure only stores integer offsets into an external particle +/// data structure that conforms to the ParticleArray interface.  +/// +/// @details Constructs and maintains a sequence of @c PointIndexGrids each of progressively +/// lower resolution. Particles are uniquely assigned to a particular resolution +/// level based on their radius. This strategy has proven efficient for accelerating +/// spatial queries on particle data sets with varying radii. +/// +/// @details The data structure automatically detects and adapts to particle data sets with +/// uniform radii. The construction is simplified and spatial queries pre-cache the +/// uniform particle radius to avoid redundant access calls to the +/// ParticleArray::getRadius method. +/// +/// @author Mihai Alden + +#ifndef OPENVDB_TOOLS_PARTICLE_ATLAS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_PARTICLE_ATLAS_HAS_BEEN_INCLUDED + +#include "PointIndexGrid.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include // for std::min(), std::max() +#include // for std::sqrt() +#include +#include +#include +#include // for std::pair +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +//////////////////////////////////////// + + +/// @brief Partition particles and performs range and nearest-neighbor searches. +/// +/// @interface ParticleArray +/// Expected interface for the ParticleArray container: +/// @code +/// template +/// struct ParticleArray +/// { +/// // The type used to represent world-space positions +/// using PosType = VectorType; +/// using ScalarType = typename PosType::value_type; +/// +/// // Return the number of particles in the array +/// size_t size() const; +/// +/// // Return the world-space position for the nth particle. +/// void getPos(size_t n, PosType& xyz) const; +/// +/// // Return the world-space radius for the nth particle. +/// void getRadius(size_t n, ScalarType& radius) const; +/// }; +/// @endcode +/// +/// @details Constructs a collection of @c PointIndexGrids of different resolutions +/// to accelerate spatial searches for particles with varying radius. +template +struct ParticleAtlas +{ + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + using PointIndexGridPtr = typename PointIndexGridType::Ptr; + using IndexType = typename PointIndexGridType::ValueType; + + struct Iterator; + + ////////// + + ParticleAtlas() : mIndexGridArray(), mMinRadiusArray(), mMaxRadiusArray() {} + + /// @brief Partitions particle indices + /// + /// @param particles container conforming to the ParticleArray interface + /// @param minVoxelSize minimum voxel size limit + /// @param maxLevels maximum number of resolution levels + template + void construct(const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels = 50); + + /// @brief Create a new @c ParticleAtlas from the given @a particles. + /// + /// @param particles container conforming to the ParticleArray interface + /// @param minVoxelSize minimum voxel size limit + /// @param maxLevels maximum number of resolution levels + template + static Ptr create(const ParticleArrayType& particles, + double minVoxelSize, size_t maxLevels = 50); + + /// @brief Returns the number of resolution levels. + size_t levels() const { return mIndexGridArray.size(); } + /// @brief true if the container size is 0, false otherwise. + bool empty() const { return mIndexGridArray.empty(); } + + /// @brief Returns minimum particle radius for level @a n. + double minRadius(size_t n) const { return mMinRadiusArray[n]; } + /// @brief Returns maximum particle radius for level @a n. + double maxRadius(size_t n) const { return mMaxRadiusArray[n]; } + + /// @brief Returns the @c PointIndexGrid that represents the given level @a n. + PointIndexGridType& pointIndexGrid(size_t n) { return *mIndexGridArray[n]; } + /// @brief Returns the @c PointIndexGrid that represents the given level @a n. + const PointIndexGridType& pointIndexGrid(size_t n) const { return *mIndexGridArray[n]; } + +private: + // Disallow copying + ParticleAtlas(const ParticleAtlas&); + ParticleAtlas& operator=(const ParticleAtlas&); + + std::vector mIndexGridArray; + std::vector mMinRadiusArray, mMaxRadiusArray; +}; // struct ParticleAtlas + + +using ParticleIndexAtlas = ParticleAtlas; + + +//////////////////////////////////////// + + +/// @brief Provides accelerated range and nearest-neighbor searches for +/// particles that are partitioned using the ParticleAtlas. +/// +/// @note Prefer to construct the iterator object once and reuse it +/// for subsequent queries. +template +struct ParticleAtlas::Iterator +{ + using TreeType = typename PointIndexGridType::TreeType; + using ConstAccessor = tree::ValueAccessor; + using ConstAccessorPtr = std::unique_ptr; + + ///// + + /// @brief Construct an iterator from the given @a atlas. + explicit Iterator(const ParticleAtlas& atlas); + + /// @brief Clear the iterator and update it with the result of the given + /// world-space radial query. + /// @param center world-space center + /// @param radius world-space search radius + /// @param particles container conforming to the ParticleArray interface + template + void worldSpaceSearchAndUpdate(const Vec3d& center, double radius, + const ParticleArrayType& particles); + + /// @brief Clear the iterator and update it with the result of the given + /// world-space radial query. + /// @param bbox world-space bounding box + /// @param particles container conforming to the ParticleArray interface + template + void worldSpaceSearchAndUpdate(const BBoxd& bbox, const ParticleArrayType& particles); + + /// @brief Returns the total number of resolution levels. + size_t levels() const { return mAtlas->levels(); } + + /// @brief Clear the iterator and update it with all particles that reside + /// at the given resolution @a level. + void updateFromLevel(size_t level); + + /// Reset the iterator to point to the first item. + void reset(); + + /// Return a const reference to the item to which this iterator is pointing. + const IndexType& operator*() const { return *mRange.first; } + + /// @{ + /// @brief Return @c true if this iterator is not yet exhausted. + bool test() const { return mRange.first < mRange.second || mIter != mRangeList.end(); } + operator bool() const { return this->test(); } + /// @} + + /// Advance iterator to next item. + void increment(); + + /// Advance iterator to next item. + void operator++() { this->increment(); } + + /// @brief Advance iterator to next item. + /// @return @c true if this iterator is not yet exhausted. + bool next(); + + /// Return the number of point indices in the iterator range. + size_t size() const; + + /// Return @c true if both iterators point to the same element. + bool operator==(const Iterator& p) const { return mRange.first == p.mRange.first; } + bool operator!=(const Iterator& p) const { return !this->operator==(p); } + +private: + Iterator(const Iterator& rhs); + Iterator& operator=(const Iterator& rhs); + + void clear(); + + using Range = std::pair; + using RangeDeque = std::deque; + using RangeDequeCIter = typename RangeDeque::const_iterator; + using IndexArray = std::unique_ptr; + + ParticleAtlas const * const mAtlas; + std::unique_ptr mAccessorList; + + // Primary index collection + Range mRange; + RangeDeque mRangeList; + RangeDequeCIter mIter; + // Secondary index collection + IndexArray mIndexArray; + size_t mIndexArraySize, mAccessorListSize; +}; // struct ParticleAtlas::Iterator + + +//////////////////////////////////////// + +// Internal operators and implementation details + + +namespace particle_atlas_internal { + + +template +struct ComputeExtremas +{ + using PosType = typename ParticleArrayT::PosType; + using ScalarType = typename PosType::value_type; + + ComputeExtremas(const ParticleArrayT& particles) + : particleArray(&particles) + , minRadius(std::numeric_limits::max()) + , maxRadius(-std::numeric_limits::max()) + { + } + + ComputeExtremas(ComputeExtremas& rhs, tbb::split) + : particleArray(rhs.particleArray) + , minRadius(std::numeric_limits::max()) + , maxRadius(-std::numeric_limits::max()) + { + } + + void operator()(const tbb::blocked_range& range) { + + ScalarType radius, tmpMin = minRadius, tmpMax = maxRadius; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + particleArray->getRadius(n, radius); + tmpMin = std::min(radius, tmpMin); + tmpMax = std::max(radius, tmpMax); + } + + minRadius = std::min(minRadius, tmpMin); + maxRadius = std::max(maxRadius, tmpMax); + } + + void join(const ComputeExtremas& rhs) { + minRadius = std::min(minRadius, rhs.minRadius); + maxRadius = std::max(maxRadius, rhs.maxRadius); + } + + ParticleArrayT const * const particleArray; + ScalarType minRadius, maxRadius; +}; // struct ComputeExtremas + + +template +struct SplittableParticleArray +{ + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + using ParticleArray = ParticleArrayT; + + using PosType = typename ParticleArray::PosType; + using ScalarType = typename PosType::value_type; + + SplittableParticleArray(const ParticleArrayT& particles) + : mIndexMap(), mParticleArray(&particles), mSize(particles.size()) + { + updateExtremas(); + } + + SplittableParticleArray(const ParticleArrayT& particles, double minR, double maxR) + : mIndexMap(), mParticleArray(&particles), mSize(particles.size()) + { + mMinRadius = ScalarType(minR); + mMaxRadius = ScalarType(maxR); + } + + const ParticleArrayT& particleArray() const { return *mParticleArray; } + + size_t size() const { return mSize; } + + void getPos(size_t n, PosType& xyz) const + { return mParticleArray->getPos(getGlobalIndex(n), xyz); } + void getRadius(size_t n, ScalarType& radius) const + { return mParticleArray->getRadius(getGlobalIndex(n), radius); } + + ScalarType minRadius() const { return mMinRadius; } + ScalarType maxRadius() const { return mMaxRadius; } + + size_t getGlobalIndex(size_t n) const { return mIndexMap ? size_t(mIndexMap[n]) : n; } + + /// Move all particle indices that have a radius larger or equal to @a maxRadiusLimit + /// into a separate container. + Ptr split(ScalarType maxRadiusLimit) { + + if (mMaxRadius < maxRadiusLimit) return Ptr(); + + std::unique_ptr mask{new bool[mSize]}; + + tbb::parallel_for(tbb::blocked_range(0, mSize), + MaskParticles(*this, mask, maxRadiusLimit)); + + Ptr output(new SplittableParticleArray(*this, mask)); + if (output->size() == 0) return Ptr(); + + size_t newSize = 0; + for (size_t n = 0, N = mSize; n < N; ++n) { + newSize += size_t(!mask[n]); + } + + std::unique_ptr newIndexMap{new PointIndex[newSize]}; + + setIndexMap(newIndexMap, mask, false); + + mSize = newSize; + mIndexMap.swap(newIndexMap); + updateExtremas(); + + return output; + } + + +private: + // Disallow copying + SplittableParticleArray(const SplittableParticleArray&); + SplittableParticleArray& operator=(const SplittableParticleArray&); + + // Masked copy constructor + SplittableParticleArray(const SplittableParticleArray& other, + const std::unique_ptr& mask) + : mIndexMap(), mParticleArray(&other.particleArray()), mSize(0) + { + for (size_t n = 0, N = other.size(); n < N; ++n) { + mSize += size_t(mask[n]); + } + + if (mSize != 0) { + mIndexMap.reset(new PointIndex[mSize]); + other.setIndexMap(mIndexMap, mask, true); + } + + updateExtremas(); + } + + struct MaskParticles { + MaskParticles(const SplittableParticleArray& particles, + const std::unique_ptr& mask, ScalarType radius) + : particleArray(&particles) + , particleMask(mask.get()) + , radiusLimit(radius) + { + } + + void operator()(const tbb::blocked_range& range) const { + const ScalarType maxRadius = radiusLimit; + ScalarType radius; + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + particleArray->getRadius(n, radius); + particleMask[n] = !(radius < maxRadius); + } + } + + SplittableParticleArray const * const particleArray; + bool * const particleMask; + ScalarType const radiusLimit; + }; // struct MaskParticles + + inline void updateExtremas() { + ComputeExtremas op(*this); + tbb::parallel_reduce(tbb::blocked_range(0, mSize), op); + mMinRadius = op.minRadius; + mMaxRadius = op.maxRadius; + } + + void setIndexMap(std::unique_ptr& newIndexMap, + const std::unique_ptr& mask, bool maskValue) const + { + if (mIndexMap.get() != nullptr) { + const PointIndex* indices = mIndexMap.get(); + for (size_t idx = 0, n = 0, N = mSize; n < N; ++n) { + if (mask[n] == maskValue) newIndexMap[idx++] = indices[n]; + } + } else { + for (size_t idx = 0, n = 0, N = mSize; n < N; ++n) { + if (mask[n] == maskValue) { + newIndexMap[idx++] = PointIndex(static_cast(n)); + } + } + } + } + + + ////////// + + std::unique_ptr mIndexMap; + ParticleArrayT const * const mParticleArray; + size_t mSize; + ScalarType mMinRadius, mMaxRadius; +}; // struct SplittableParticleArray + + +template +struct RemapIndices { + + RemapIndices(const ParticleArrayType& particles, std::vector& nodes) + : mParticles(&particles) + , mNodes(nodes.empty() ? nullptr : &nodes.front()) + { + } + + void operator()(const tbb::blocked_range& range) const + { + using PointIndexType = typename PointIndexLeafNodeType::ValueType; + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + PointIndexLeafNodeType& node = *mNodes[n]; + const size_t numIndices = node.indices().size(); + + if (numIndices > 0) { + PointIndexType* begin = &node.indices().front(); + const PointIndexType* end = begin + numIndices; + + while (begin < end) { + *begin = PointIndexType(static_cast( + mParticles->getGlobalIndex(*begin))); + ++begin; + } + } + } + } + + ParticleArrayType const * const mParticles; + PointIndexLeafNodeType * const * const mNodes; +}; // struct RemapIndices + + +template +struct RadialRangeFilter +{ + using PosType = typename ParticleArrayType::PosType; + using ScalarType = typename PosType::value_type; + + using Range = std::pair; + using RangeDeque = std::deque; + using IndexDeque = std::deque; + + RadialRangeFilter(RangeDeque& ranges, IndexDeque& indices, const PosType& xyz, + ScalarType radius, const ParticleArrayType& particles, bool hasUniformRadius = false) + : mRanges(ranges) + , mIndices(indices) + , mCenter(xyz) + , mRadius(radius) + , mParticles(&particles) + , mHasUniformRadius(hasUniformRadius) + { + if (mHasUniformRadius) { + ScalarType uniformRadius; + mParticles->getRadius(0, uniformRadius); + mRadius = mRadius + uniformRadius; + mRadius *= mRadius; + } + } + + template + void filterLeafNode(const LeafNodeType& leaf) + { + const size_t numIndices = leaf.indices().size(); + if (numIndices > 0) { + const IndexT* begin = &leaf.indices().front(); + filterVoxel(leaf.origin(), begin, begin + numIndices); + } + } + + void filterVoxel(const Coord&, const IndexT* begin, const IndexT* end) + { + PosType pos; + + if (mHasUniformRadius) { + + const ScalarType searchRadiusSqr = mRadius; + + while (begin < end) { + mParticles->getPos(size_t(*begin), pos); + const ScalarType distSqr = (mCenter - pos).lengthSqr(); + if (distSqr < searchRadiusSqr) { + mIndices.push_back(*begin); + } + ++begin; + } + } else { + while (begin < end) { + const size_t idx = size_t(*begin); + mParticles->getPos(idx, pos); + + ScalarType radius; + mParticles->getRadius(idx, radius); + + ScalarType searchRadiusSqr = mRadius + radius; + searchRadiusSqr *= searchRadiusSqr; + + const ScalarType distSqr = (mCenter - pos).lengthSqr(); + + if (distSqr < searchRadiusSqr) { + mIndices.push_back(*begin); + } + + ++begin; + } + } + } + +private: + RadialRangeFilter(const RadialRangeFilter&); + RadialRangeFilter& operator=(const RadialRangeFilter&); + + RangeDeque& mRanges; + IndexDeque& mIndices; + PosType const mCenter; + ScalarType mRadius; + ParticleArrayType const * const mParticles; + bool const mHasUniformRadius; +}; // struct RadialRangeFilter + + +template +struct BBoxFilter +{ + using PosType = typename ParticleArrayType::PosType; + using ScalarType = typename PosType::value_type; + + using Range = std::pair; + using RangeDeque = std::deque; + using IndexDeque = std::deque; + + BBoxFilter(RangeDeque& ranges, IndexDeque& indices, + const BBoxd& bbox, const ParticleArrayType& particles, bool hasUniformRadius = false) + : mRanges(ranges) + , mIndices(indices) + , mBBox(PosType(bbox.min()), PosType(bbox.max())) + , mCenter(mBBox.getCenter()) + , mParticles(&particles) + , mHasUniformRadius(hasUniformRadius) + , mUniformRadiusSqr(ScalarType(0.0)) + { + if (mHasUniformRadius) { + mParticles->getRadius(0, mUniformRadiusSqr); + mUniformRadiusSqr *= mUniformRadiusSqr; + } + } + + template + void filterLeafNode(const LeafNodeType& leaf) + { + const size_t numIndices = leaf.indices().size(); + if (numIndices > 0) { + const IndexT* begin = &leaf.indices().front(); + filterVoxel(leaf.origin(), begin, begin + numIndices); + } + } + + void filterVoxel(const Coord&, const IndexT* begin, const IndexT* end) + { + PosType pos; + + if (mHasUniformRadius) { + const ScalarType radiusSqr = mUniformRadiusSqr; + + while (begin < end) { + + mParticles->getPos(size_t(*begin), pos); + if (mBBox.isInside(pos)) { + mIndices.push_back(*begin++); + continue; + } + + const ScalarType distSqr = pointToBBoxDistSqr(pos); + if (!(distSqr > radiusSqr)) { + mIndices.push_back(*begin); + } + + ++begin; + } + + } else { + while (begin < end) { + + const size_t idx = size_t(*begin); + mParticles->getPos(idx, pos); + if (mBBox.isInside(pos)) { + mIndices.push_back(*begin++); + continue; + } + + ScalarType radius; + mParticles->getRadius(idx, radius); + const ScalarType distSqr = pointToBBoxDistSqr(pos); + if (!(distSqr > (radius * radius))) { + mIndices.push_back(*begin); + } + + ++begin; + } + } + } + +private: + BBoxFilter(const BBoxFilter&); + BBoxFilter& operator=(const BBoxFilter&); + + ScalarType pointToBBoxDistSqr(const PosType& pos) const + { + ScalarType distSqr = ScalarType(0.0); + + for (int i = 0; i < 3; ++i) { + + const ScalarType a = pos[i]; + + ScalarType b = mBBox.min()[i]; + if (a < b) { + ScalarType delta = b - a; + distSqr += delta * delta; + } + + b = mBBox.max()[i]; + if (a > b) { + ScalarType delta = a - b; + distSqr += delta * delta; + } + } + + return distSqr; + } + + RangeDeque& mRanges; + IndexDeque& mIndices; + math::BBox const mBBox; + PosType const mCenter; + ParticleArrayType const * const mParticles; + bool const mHasUniformRadius; + ScalarType mUniformRadiusSqr; +}; // struct BBoxFilter + + +} // namespace particle_atlas_internal + + +//////////////////////////////////////// + + +template +template +inline void +ParticleAtlas::construct( + const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels) +{ + using SplittableParticleArray = + typename particle_atlas_internal::SplittableParticleArray; + using SplittableParticleArrayPtr = typename SplittableParticleArray::Ptr; + using ScalarType = typename ParticleArrayType::ScalarType; + + ///// + + particle_atlas_internal::ComputeExtremas extremas(particles); + tbb::parallel_reduce(tbb::blocked_range(0, particles.size()), extremas); + const double firstMin = extremas.minRadius; + const double firstMax = extremas.maxRadius; + const double firstVoxelSize = std::max(minVoxelSize, firstMin); + + if (!(firstMax < (firstVoxelSize * double(2.0))) && maxLevels > 1) { + + std::vector levels; + levels.push_back(SplittableParticleArrayPtr( + new SplittableParticleArray(particles, firstMin, firstMax))); + + std::vector voxelSizeArray; + voxelSizeArray.push_back(firstVoxelSize); + + for (size_t n = 0; n < maxLevels; ++n) { + + const double maxParticleRadius = double(levels.back()->maxRadius()); + const double particleRadiusLimit = voxelSizeArray.back() * double(2.0); + if (maxParticleRadius < particleRadiusLimit) break; + + SplittableParticleArrayPtr newLevel = + levels.back()->split(ScalarType(particleRadiusLimit)); + if (!newLevel) break; + + levels.push_back(newLevel); + voxelSizeArray.push_back(double(newLevel->minRadius())); + } + + size_t numPoints = 0; + + using PointIndexTreeType = typename PointIndexGridType::TreeType; + using PointIndexLeafNodeType = typename PointIndexTreeType::LeafNodeType; + + std::vector nodes; + + for (size_t n = 0, N = levels.size(); n < N; ++n) { + + const SplittableParticleArray& particleArray = *levels[n]; + + numPoints += particleArray.size(); + + mMinRadiusArray.push_back(double(particleArray.minRadius())); + mMaxRadiusArray.push_back(double(particleArray.maxRadius())); + + PointIndexGridPtr grid = + createPointIndexGrid(particleArray, voxelSizeArray[n]); + + nodes.clear(); + grid->tree().getNodes(nodes); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + particle_atlas_internal::RemapIndices(particleArray, nodes)); + + mIndexGridArray.push_back(grid); + } + + } else { + mMinRadiusArray.push_back(firstMin); + mMaxRadiusArray.push_back(firstMax); + mIndexGridArray.push_back( + createPointIndexGrid(particles, firstVoxelSize)); + } +} + + +template +template +inline typename ParticleAtlas::Ptr +ParticleAtlas::create( + const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels) +{ + Ptr ret(new ParticleAtlas()); + ret->construct(particles, minVoxelSize, maxLevels); + return ret; +} + + +//////////////////////////////////////// + +// ParticleAtlas::Iterator implementation + +template +inline +ParticleAtlas::Iterator::Iterator(const ParticleAtlas& atlas) + : mAtlas(&atlas) + , mAccessorList() + , mRange(static_cast(nullptr), static_cast(nullptr)) + , mRangeList() + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(0) + , mAccessorListSize(atlas.levels()) +{ + if (mAccessorListSize > 0) { + mAccessorList.reset(new ConstAccessorPtr[mAccessorListSize]); + for (size_t n = 0, N = mAccessorListSize; n < N; ++n) { + mAccessorList[n].reset(new ConstAccessor(atlas.pointIndexGrid(n).tree())); + } + } +} + + +template +inline void +ParticleAtlas::Iterator::reset() +{ + mIter = mRangeList.begin(); + if (!mRangeList.empty()) { + mRange = mRangeList.front(); + } else if (mIndexArray) { + mRange.first = mIndexArray.get(); + mRange.second = mRange.first + mIndexArraySize; + } else { + mRange.first = static_cast(nullptr); + mRange.second = static_cast(nullptr); + } +} + + +template +inline void +ParticleAtlas::Iterator::increment() +{ + ++mRange.first; + if (mRange.first >= mRange.second && mIter != mRangeList.end()) { + ++mIter; + if (mIter != mRangeList.end()) { + mRange = *mIter; + } else if (mIndexArray) { + mRange.first = mIndexArray.get(); + mRange.second = mRange.first + mIndexArraySize; + } + } +} + + +template +inline bool +ParticleAtlas::Iterator::next() +{ + if (!this->test()) return false; + this->increment(); + return this->test(); +} + + +template +inline size_t +ParticleAtlas::Iterator::size() const +{ + size_t count = 0; + typename RangeDeque::const_iterator it = + mRangeList.begin(), end = mRangeList.end(); + + for ( ; it != end; ++it) { + count += it->second - it->first; + } + + return count + mIndexArraySize; +} + + +template +inline void +ParticleAtlas::Iterator::clear() +{ + mRange.first = static_cast(nullptr); + mRange.second = static_cast(nullptr); + mRangeList.clear(); + mIter = mRangeList.end(); + mIndexArray.reset(); + mIndexArraySize = 0; +} + + +template +inline void +ParticleAtlas::Iterator::updateFromLevel(size_t level) +{ + using TreeT = typename PointIndexGridType::TreeType; + using LeafNodeType = typename TreeType::LeafNodeType; + + this->clear(); + + if (mAccessorListSize > 0) { + const size_t levelIdx = std::min(mAccessorListSize - 1, level); + + const TreeT& tree = mAtlas->pointIndexGrid(levelIdx).tree(); + + std::vector nodes; + tree.getNodes(nodes); + + for (size_t n = 0, N = nodes.size(); n < N; ++n) { + + const LeafNodeType& node = *nodes[n]; + const size_t numIndices = node.indices().size(); + + if (numIndices > 0) { + const IndexType* begin = &node.indices().front(); + mRangeList.push_back(Range(begin, (begin + numIndices))); + } + } + } + + this->reset(); +} + + +template +template +inline void +ParticleAtlas::Iterator::worldSpaceSearchAndUpdate( + const Vec3d& center, double radius, const ParticleArrayType& particles) +{ + using PosType = typename ParticleArrayType::PosType; + using ScalarType = typename ParticleArrayType::ScalarType; + + ///// + + this->clear(); + + std::deque filteredIndices; + std::vector searchRegions; + + const double iRadius = radius * double(1.0 / std::sqrt(3.0)); + + const Vec3d ibMin(center[0] - iRadius, center[1] - iRadius, center[2] - iRadius); + const Vec3d ibMax(center[0] + iRadius, center[1] + iRadius, center[2] + iRadius); + + const Vec3d bMin(center[0] - radius, center[1] - radius, center[2] - radius); + const Vec3d bMax(center[0] + radius, center[1] + radius, center[2] + radius); + + const PosType pos = PosType(center); + const ScalarType dist = ScalarType(radius); + + for (size_t n = 0, N = mAccessorListSize; n < N; ++n) { + + const double maxRadius = mAtlas->maxRadius(n); + const bool uniformRadius = math::isApproxEqual(mAtlas->minRadius(n), maxRadius); + + const openvdb::math::Transform& xform = mAtlas->pointIndexGrid(n).transform(); + + ConstAccessor& acc = *mAccessorList[n]; + + openvdb::CoordBBox inscribedRegion( + xform.worldToIndexCellCentered(ibMin), + xform.worldToIndexCellCentered(ibMax)); + + inscribedRegion.expand(-1); // erode by one voxel + + // collect indices that don't need to be tested + point_index_grid_internal::pointIndexSearch(mRangeList, acc, inscribedRegion); + + searchRegions.clear(); + + const openvdb::CoordBBox region( + xform.worldToIndexCellCentered(bMin - maxRadius), + xform.worldToIndexCellCentered(bMax + maxRadius)); + + inscribedRegion.expand(1); + point_index_grid_internal::constructExclusiveRegions( + searchRegions, region, inscribedRegion); + + using FilterType = particle_atlas_internal::RadialRangeFilter; + FilterType filter(mRangeList, filteredIndices, pos, dist, particles, uniformRadius); + + for (size_t i = 0, I = searchRegions.size(); i < I; ++i) { + point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[i]); + } + } + + point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); + + this->reset(); +} + + +template +template +inline void +ParticleAtlas::Iterator::worldSpaceSearchAndUpdate( + const BBoxd& bbox, const ParticleArrayType& particles) +{ + this->clear(); + + std::deque filteredIndices; + std::vector searchRegions; + + for (size_t n = 0, N = mAccessorListSize; n < N; ++n) { + + const double maxRadius = mAtlas->maxRadius(n); + const bool uniformRadius = math::isApproxEqual(mAtlas->minRadius(n), maxRadius); + const openvdb::math::Transform& xform = mAtlas->pointIndexGrid(n).transform(); + + ConstAccessor& acc = *mAccessorList[n]; + + openvdb::CoordBBox inscribedRegion( + xform.worldToIndexCellCentered(bbox.min()), + xform.worldToIndexCellCentered(bbox.max())); + + inscribedRegion.expand(-1); // erode by one voxel + + // collect indices that don't need to be tested + point_index_grid_internal::pointIndexSearch(mRangeList, acc, inscribedRegion); + + searchRegions.clear(); + + const openvdb::CoordBBox region( + xform.worldToIndexCellCentered(bbox.min() - maxRadius), + xform.worldToIndexCellCentered(bbox.max() + maxRadius)); + + inscribedRegion.expand(1); + point_index_grid_internal::constructExclusiveRegions( + searchRegions, region, inscribedRegion); + + using FilterType = particle_atlas_internal::BBoxFilter; + FilterType filter(mRangeList, filteredIndices, bbox, particles, uniformRadius); + + for (size_t i = 0, I = searchRegions.size(); i < I; ++i) { + point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[i]); + } + } + + point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); + + this->reset(); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_PARTICLE_ATLAS_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/ParticlesToLevelSet.h b/openvdb/tools/ParticlesToLevelSet.h new file mode 100644 index 00000000..ed0c28b3 --- /dev/null +++ b/openvdb/tools/ParticlesToLevelSet.h @@ -0,0 +1,1032 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Ken Museth +/// +/// @file tools/ParticlesToLevelSet.h +/// +/// @brief Rasterize particles with position, radius and velocity +/// into either a boolean mask grid or a narrow-band level set grid. +/// +/// @details Optionally, arbitrary attributes on the particles can be transferred, +/// resulting in additional output grids with the same topology as the main grid. +/// +/// @note Particle to level set conversion is intended to be combined with +/// some kind of surface postprocessing, using +/// @vdblink::tools::LevelSetFilter LevelSetFilter@endlink, for example. +/// Without such postprocessing the generated surface is typically too noisy and blobby. +/// However, it serves as a great and fast starting point for subsequent +/// level set surface processing and convolution. +/// +/// @details For particle access, any class with the following interface may be used +/// (see the unit test or the From Particles Houdini SOP for practical examples): +/// @code +/// struct ParticleList +/// { +/// // Return the total number of particles in the list. +/// // Always required! +/// size_t size() const; +/// +/// // Get the world-space position of the nth particle. +/// // Required by rasterizeSpheres(). +/// void getPos(size_t n, Vec3R& xyz) const; +/// +/// // Get the world-space position and radius of the nth particle. +/// // Required by rasterizeSpheres(). +/// void getPosRad(size_t n, Vec3R& xyz, Real& radius) const; +/// +/// // Get the world-space position, radius and velocity of the nth particle. +/// // Required by rasterizeTrails(). +/// void getPosRadVel(size_t n, Vec3R& xyz, Real& radius, Vec3R& velocity) const; +/// +/// // Get the value of the nth particle's user-defined attribute (of type @c AttributeType). +/// // Required only if attribute transfer is enabled in ParticlesToLevelSet. +/// void getAtt(size_t n, AttributeType& att) const; +/// }; +/// @endcode +/// +/// Some functions accept an interrupter argument. This refers to any class +/// with the following interface: +/// @code +/// struct Interrupter +/// { +/// void start(const char* name = nullptr) // called when computations begin +/// void end() // called when computations end +/// bool wasInterrupted(int percent=-1) // return true to abort computation +/// }; +/// @endcode +/// +/// The default interrupter is @vdblink::util::NullInterrupter NullInterrupter@endlink, +/// for which all calls are no-ops that incur no computational overhead. + +#ifndef OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Composite.h" // for csgUnion() +#include "PointPartitioner.h" +#include "Prune.h" +#include "SignedFloodFill.h" +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Populate a scalar, floating-point grid with CSG-unioned level set spheres +/// described by the given particle positions and radii. +/// @details For more control over the output, including attribute transfer, +/// use the ParticlesToLevelSet class directly. +template +inline void particlesToSdf(const ParticleListT&, GridT&, InterrupterT* = nullptr); + +/// @brief Populate a scalar, floating-point grid with fixed-size, CSG-unioned +/// level set spheres described by the given particle positions and the specified radius. +/// @details For more control over the output, including attribute transfer, +/// use the ParticlesToLevelSet class directly. +template +inline void particlesToSdf(const ParticleListT&, GridT&, Real radius, InterrupterT* = nullptr); + +/// @brief Populate a scalar, floating-point grid with CSG-unioned trails +/// of level set spheres with decreasing radius, where the starting position and radius +/// and the direction of each trail is given by particle attributes. +/// @details For more control over the output, including attribute transfer, +/// use the ParticlesToLevelSet class directly. +/// @note The @a delta parameter controls the distance between spheres in a trail. +/// Be careful not to use too small a value. +template +inline void particleTrailsToSdf(const ParticleListT&, GridT&, Real delta=1, InterrupterT* =nullptr); + +/// @brief Activate a boolean grid wherever it intersects the spheres +/// described by the given particle positions and radii. +/// @details For more control over the output, including attribute transfer, +/// use the ParticlesToLevelSet class directly. +template +inline void particlesToMask(const ParticleListT&, GridT&, InterrupterT* = nullptr); + +/// @brief Activate a boolean grid wherever it intersects the fixed-size spheres +/// described by the given particle positions and the specified radius. +/// @details For more control over the output, including attribute transfer, +/// use the ParticlesToLevelSet class directly. +template +inline void particlesToMask(const ParticleListT&, GridT&, Real radius, InterrupterT* = nullptr); + +/// @brief Activate a boolean grid wherever it intersects trails of spheres +/// with decreasing radius, where the starting position and radius and the direction +/// of each trail is given by particle attributes. +/// @details For more control over the output, including attribute transfer, +/// use the ParticlesToLevelSet class directly. +/// @note The @a delta parameter controls the distance between spheres in a trail. +/// Be careful not to use too small a value. +template +inline void particleTrailsToMask(const ParticleListT&, GridT&,Real delta=1,InterrupterT* =nullptr); + + +//////////////////////////////////////// + + +namespace p2ls_internal { +// This is a simple type that combines a distance value and a particle +// attribute. It's required for attribute transfer which is performed +// in the ParticlesToLevelSet::Raster member class defined below. +/// @private +template class BlindData; +} + + +template +class ParticlesToLevelSet +{ +public: + using DisableT = typename std::is_void::type; + using InterrupterType = InterrupterT; + + using SdfGridType = SdfGridT; + using SdfType = typename SdfGridT::ValueType; + + using AttType = typename std::conditional::type; + using AttGridType = typename SdfGridT::template ValueConverter::Type; + + static const bool OutputIsMask = std::is_same::value; + + /// @brief Constructor using an existing boolean or narrow-band level set grid + /// + /// @param grid grid into which particles are rasterized + /// @param interrupt callback to interrupt a long-running process + /// + /// @details If the input grid is already populated with signed distances, + /// particles are unioned onto the existing level set surface. + /// + /// @details The width in voxel units of the generated narrow band level set + /// is given by 2×background/dx, where @a background + /// is the background value stored in the grid and @a dx is the voxel size + /// derived from the transform associated with the grid. + /// Also note that −background corresponds to the constant value + /// inside the generated narrow-band level set. + /// + /// @note If attribute transfer is enabled, i.e., if @c AttributeT is not @c void, + /// attributes are generated only for voxels that overlap with particles, + /// not for any other preexisting voxels (for which no attributes exist!). + explicit ParticlesToLevelSet(SdfGridT& grid, InterrupterT* interrupt = nullptr); + + ~ParticlesToLevelSet() { delete mBlindGrid; } + + /// @brief This method syncs up the level set and attribute grids + /// and therefore needs to be called before any of those grids are + /// used and after the last call to any of the rasterizer methods. + /// @details It has no effect or overhead if attribute transfer is disabled + /// (i.e., if @c AttributeT is @c void) and @a prune is @c false. + /// + /// @note Avoid calling this method more than once, and call it only after + /// all the particles have been rasterized. + void finalize(bool prune = false); + + /// @brief Return a pointer to the grid containing the optional user-defined attribute. + /// @warning If attribute transfer is disabled (i.e., if @c AttributeT is @c void) + /// or if @link finalize() finalize@endlink is not called, the pointer will be null. + typename AttGridType::Ptr attributeGrid() { return mAttGrid; } + + /// @brief Return the size of a voxel in world units. + Real getVoxelSize() const { return mDx; } + + /// @brief Return the half-width of the narrow band in voxel units. + Real getHalfWidth() const { return mHalfWidth; } + + /// @brief Return the smallest radius allowed in voxel units. + Real getRmin() const { return mRmin; } + /// @brief Set the smallest radius allowed in voxel units. + void setRmin(Real Rmin) { mRmin = math::Max(Real(0),Rmin); } + + /// @brief Return the largest radius allowed in voxel units. + Real getRmax() const { return mRmax; } + /// @brief Set the largest radius allowed in voxel units. + void setRmax(Real Rmax) { mRmax = math::Max(mRmin,Rmax); } + + /// @brief Return @c true if any particles were ignored due to their size. + bool ignoredParticles() const { return mMinCount>0 || mMaxCount>0; } + /// @brief Return the number of particles that were ignored because they were + /// smaller than the minimum radius. + size_t getMinCount() const { return mMinCount; } + /// @brief Return the number of particles that were ignored because they were + /// larger than the maximum radius. + size_t getMaxCount() const { return mMaxCount; } + + /// @brief Return the grain size used for threading + int getGrainSize() const { return mGrainSize; } + /// @brief Set the grain size used for threading. + /// @note A grain size of zero or less disables threading. + void setGrainSize(int grainSize) { mGrainSize = grainSize; } + + /// @brief Rasterize each particle as a sphere with the particle's position and radius. + /// @details For level set output, all spheres are CSG-unioned. + template + void rasterizeSpheres(const ParticleListT& pa); + + /// @brief Rasterize each particle as a sphere with the particle's position + /// and a fixed radius. + /// @details For level set output, all spheres are CSG-unioned. + /// + /// @param pa particles with positions + /// @param radius fixed sphere radius in world units. + template + void rasterizeSpheres(const ParticleListT& pa, Real radius); + + /// @brief Rasterize each particle as a trail comprising the CSG union + /// of spheres of decreasing radius. + /// + /// @param pa particles with position, radius and velocity. + /// @param delta controls the distance between sphere instances + /// + /// @warning Be careful not to use too small values for @a delta, + /// since this can lead to excessive computation per trail (which the + /// interrupter can't stop). + /// + /// @note The direction of a trail is opposite to that of the velocity vector, + /// and its length is given by the magnitude of the velocity. + /// The radius at the head of the trail is given by the radius of the particle, + /// and the radius at the tail is @a Rmin voxel units, which has + /// a default value of 1.5 corresponding to the Nyquist frequency! + template + void rasterizeTrails(const ParticleListT& pa, Real delta=1.0); + +private: + using BlindType = p2ls_internal::BlindData; + using BlindGridType = typename SdfGridT::template ValueConverter::Type; + + /// Class with multi-threaded implementation of particle rasterization + template struct Raster; + + SdfGridType* mSdfGrid; + typename AttGridType::Ptr mAttGrid; + BlindGridType* mBlindGrid; + InterrupterT* mInterrupter; + Real mDx, mHalfWidth; + Real mRmin, mRmax; // ignore particles outside this range of radii in voxel + size_t mMinCount, mMaxCount; // counters for ignored particles + int mGrainSize; +}; // class ParticlesToLevelSet + + +template +inline ParticlesToLevelSet:: +ParticlesToLevelSet(SdfGridT& grid, InterrupterT* interrupter) : + mSdfGrid(&grid), + mBlindGrid(nullptr), + mInterrupter(interrupter), + mDx(grid.voxelSize()[0]), + mHalfWidth(grid.background()/mDx), + mRmin(1.5),// corresponds to the Nyquist grid sampling frequency + mRmax(100.0),// corresponds to a huge particle (probably too large!) + mMinCount(0), + mMaxCount(0), + mGrainSize(1) +{ + if (!mSdfGrid->hasUniformVoxels()) { + OPENVDB_THROW(RuntimeError, "ParticlesToLevelSet only supports uniform voxels!"); + } + if (!DisableT::value) { + mBlindGrid = new BlindGridType(BlindType(grid.background())); + mBlindGrid->setTransform(mSdfGrid->transform().copy()); + } +} + +template +template +inline void ParticlesToLevelSet:: +rasterizeSpheres(const ParticleListT& pa) +{ + if (DisableT::value) { + Raster r(*this, mSdfGrid, pa); + r.rasterizeSpheres(); + } else { + Raster r(*this, mBlindGrid, pa); + r.rasterizeSpheres(); + } +} + +template +template +inline void ParticlesToLevelSet:: +rasterizeSpheres(const ParticleListT& pa, Real radius) +{ + if (DisableT::value) { + Raster r(*this, mSdfGrid, pa); + r.rasterizeSpheres(radius/mDx); + } else { + Raster r(*this, mBlindGrid, pa); + r.rasterizeSpheres(radius/mDx); + } +} + +template +template +inline void ParticlesToLevelSet:: +rasterizeTrails(const ParticleListT& pa, Real delta) +{ + if (DisableT::value) { + Raster r(*this, mSdfGrid, pa); + r.rasterizeTrails(delta); + } else { + Raster r(*this, mBlindGrid, pa); + r.rasterizeTrails(delta); + } +} + + +template +inline void +ParticlesToLevelSet::finalize(bool prune) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + if (!mBlindGrid) { + if (prune) { + if (OutputIsMask) { + tools::prune(mSdfGrid->tree()); + } else { + tools::pruneLevelSet(mSdfGrid->tree()); + } + } + return; + } + + if (prune) tools::prune(mBlindGrid->tree()); + + using AttTreeT = typename AttGridType::TreeType; + using AttLeafT = typename AttTreeT::LeafNodeType; + using BlindTreeT = typename BlindGridType::TreeType; + using BlindLeafIterT = typename BlindTreeT::LeafCIter; + using BlindLeafT = typename BlindTreeT::LeafNodeType; + using SdfTreeT = typename SdfGridType::TreeType; + using SdfLeafT = typename SdfTreeT::LeafNodeType; + + // Use topology copy constructors since output grids have the same topology as mBlindDataGrid + const BlindTreeT& blindTree = mBlindGrid->tree(); + + // Create the output attribute grid. + typename AttTreeT::Ptr attTree(new AttTreeT( + blindTree, blindTree.background().blind(), openvdb::TopologyCopy())); + // Note this overwrites any existing attribute grids! + mAttGrid = typename AttGridType::Ptr(new AttGridType(attTree)); + mAttGrid->setTransform(mBlindGrid->transform().copy()); + + typename SdfTreeT::Ptr sdfTree; // the output mask or level set tree + + // Extract the attribute grid and the mask or level set grid from mBlindDataGrid. + if (OutputIsMask) { + sdfTree.reset(new SdfTreeT(blindTree, + /*off=*/SdfType(0), /*on=*/SdfType(1), TopologyCopy())); + + // Copy leaf voxels in parallel. + tree::LeafManager leafNodes(*attTree); + leafNodes.foreach([&](AttLeafT& attLeaf, size_t /*leafIndex*/) { + if (const auto* blindLeaf = blindTree.probeConstLeaf(attLeaf.origin())) { + for (auto iter = attLeaf.beginValueOn(); iter; ++iter) { + const auto pos = iter.pos(); + attLeaf.setValueOnly(pos, blindLeaf->getValue(pos).blind()); + } + } + }); + // Copy tiles serially. + const auto blindAcc = mBlindGrid->getConstAccessor(); + auto iter = attTree->beginValueOn(); + iter.setMaxDepth(AttTreeT::ValueOnIter::LEAF_DEPTH - 1); + for ( ; iter; ++iter) { + iter.modifyValue([&](AttType& v) { v = blindAcc.getValue(iter.getCoord()).blind(); }); + } + } else { + // Here we exploit the fact that by design level sets have no active tiles. + // Only leaf voxels can be active. + sdfTree.reset(new SdfTreeT(blindTree, blindTree.background().visible(), TopologyCopy())); + for (BlindLeafIterT n = blindTree.cbeginLeaf(); n; ++n) { + const BlindLeafT& leaf = *n; + const openvdb::Coord xyz = leaf.origin(); + // Get leafnodes that were allocated during topology construction! + SdfLeafT* sdfLeaf = sdfTree->probeLeaf(xyz); + AttLeafT* attLeaf = attTree->probeLeaf(xyz); + // Use linear offset (vs coordinate) access for better performance! + typename BlindLeafT::ValueOnCIter m=leaf.cbeginValueOn(); + if (!m) {//no active values in leaf node so copy everything + for (openvdb::Index k = 0; k!=BlindLeafT::SIZE; ++k) { + const BlindType& v = leaf.getValue(k); + sdfLeaf->setValueOnly(k, v.visible()); + attLeaf->setValueOnly(k, v.blind()); + } + } else {//only copy active values (using flood fill for the inactive values) + for(; m; ++m) { + const openvdb::Index k = m.pos(); + const BlindType& v = *m; + sdfLeaf->setValueOnly(k, v.visible()); + attLeaf->setValueOnly(k, v.blind()); + } + } + } + tools::signedFloodFill(*sdfTree);//required since we only transferred active voxels! + } + + if (mSdfGrid->empty()) { + mSdfGrid->setTree(sdfTree); + } else { + if (OutputIsMask) { + mSdfGrid->tree().topologyUnion(*sdfTree); + tools::prune(mSdfGrid->tree()); + } else { + tools::csgUnion(mSdfGrid->tree(), *sdfTree, /*prune=*/true); + } + } + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +/////////////////////////////////////////////////////////// + + +template +template +struct ParticlesToLevelSet::Raster +{ + using DisableT = typename std::is_void::type; + using ParticlesToLevelSetT = ParticlesToLevelSet; + using SdfT = typename ParticlesToLevelSetT::SdfType; // type of signed distance values + using AttT = typename ParticlesToLevelSetT::AttType; // type of particle attribute + using ValueT = typename GridT::ValueType; + using AccessorT = typename GridT::Accessor; + using TreeT = typename GridT::TreeType; + using LeafNodeT = typename TreeT::LeafNodeType; + using PointPartitionerT = PointPartitioner; + + static const bool + OutputIsMask = std::is_same::value, + DoAttrXfer = !DisableT::value; + + /// @brief Main constructor + Raster(ParticlesToLevelSetT& parent, GridT* grid, const ParticleListT& particles) + : mParent(parent) + , mParticles(particles) + , mGrid(grid) + , mMap(*(mGrid->transform().baseMap())) + , mMinCount(0) + , mMaxCount(0) + , mIsCopy(false) + { + mPointPartitioner = new PointPartitionerT; + mPointPartitioner->construct(particles, mGrid->transform()); + } + + /// @brief Copy constructor called by tbb threads + Raster(Raster& other, tbb::split) + : mParent(other.mParent) + , mParticles(other.mParticles) + , mGrid(new GridT(*other.mGrid, openvdb::ShallowCopy())) + , mMap(other.mMap) + , mMinCount(0) + , mMaxCount(0) + , mTask(other.mTask) + , mIsCopy(true) + , mPointPartitioner(other.mPointPartitioner) + { + mGrid->newTree(); + } + + virtual ~Raster() + { + // Copy-constructed Rasters own temporary grids that have to be deleted, + // while the original has ownership of the bucket array. + if (mIsCopy) { + delete mGrid; + } else { + delete mPointPartitioner; + } + } + + void rasterizeSpheres() + { + mMinCount = mMaxCount = 0; + if (mParent.mInterrupter) { + mParent.mInterrupter->start("Rasterizing particles to level set using spheres"); + } + mTask = std::bind(&Raster::rasterSpheres, std::placeholders::_1, std::placeholders::_2); + this->cook(); + if (mParent.mInterrupter) mParent.mInterrupter->end(); + } + + void rasterizeSpheres(Real radius) + { + mMinCount = radius < mParent.mRmin ? mParticles.size() : 0; + mMaxCount = radius > mParent.mRmax ? mParticles.size() : 0; + if (mMinCount>0 || mMaxCount>0) {//skipping all particles! + mParent.mMinCount = mMinCount; + mParent.mMaxCount = mMaxCount; + } else { + if (mParent.mInterrupter) { + mParent.mInterrupter->start( + "Rasterizing particles to level set using const spheres"); + } + mTask = std::bind(&Raster::rasterFixedSpheres, + std::placeholders::_1, std::placeholders::_2, radius); + this->cook(); + if (mParent.mInterrupter) mParent.mInterrupter->end(); + } + } + + void rasterizeTrails(Real delta=1.0) + { + mMinCount = mMaxCount = 0; + if (mParent.mInterrupter) { + mParent.mInterrupter->start("Rasterizing particles to level set using trails"); + } + mTask = std::bind(&Raster::rasterTrails, + std::placeholders::_1, std::placeholders::_2, delta); + this->cook(); + if (mParent.mInterrupter) mParent.mInterrupter->end(); + } + + /// @brief Kick off the optionally multithreaded computation. + void operator()(const tbb::blocked_range& r) + { + assert(mTask); + mTask(this, r); + mParent.mMinCount = mMinCount; + mParent.mMaxCount = mMaxCount; + } + + /// @brief Required by tbb::parallel_reduce + void join(Raster& other) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (OutputIsMask) { + if (DoAttrXfer) { + tools::compMax(*mGrid, *other.mGrid); + } else { + mGrid->topologyUnion(*other.mGrid); + } + } else { + tools::csgUnion(*mGrid, *other.mGrid, /*prune=*/true); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + mMinCount += other.mMinCount; + mMaxCount += other.mMaxCount; + } + +private: + /// Disallow assignment since some of the members are references + Raster& operator=(const Raster&) { return *this; } + + /// @return true if the particle is too small or too large + bool ignoreParticle(Real R) + { + if (R < mParent.mRmin) {// below the cutoff radius + ++mMinCount; + return true; + } + if (R > mParent.mRmax) {// above the cutoff radius + ++mMaxCount; + return true; + } + return false; + } + + /// @brief Threaded rasterization of particles as spheres with variable radius + /// @param r range of indices into the list of particles + void rasterSpheres(const tbb::blocked_range& r) + { + AccessorT acc = mGrid->getAccessor(); // local accessor + bool run = true; + const Real invDx = 1 / mParent.mDx; + AttT att; + Vec3R pos; + Real rad; + + // Loop over buckets + for (size_t n = r.begin(), N = r.end(); n < N; ++n) { + // Loop over particles in bucket n. + typename PointPartitionerT::IndexIterator iter = mPointPartitioner->indices(n); + for ( ; run && iter; ++iter) { + const Index32& id = *iter; + mParticles.getPosRad(id, pos, rad); + const Real R = invDx * rad;// in voxel units + if (this->ignoreParticle(R)) continue; + const Vec3R P = mMap.applyInverseMap(pos); + this->getAtt(id, att); + run = this->makeSphere(P, R, att, acc); + }//end loop over particles + }//end loop over buckets + } + + /// @brief Threaded rasterization of particles as spheres with a fixed radius + /// @param r range of indices into the list of particles + /// @param R radius of fixed-size spheres + void rasterFixedSpheres(const tbb::blocked_range& r, Real R) + { + AccessorT acc = mGrid->getAccessor(); // local accessor + AttT att; + Vec3R pos; + + // Loop over buckets + for (size_t n = r.begin(), N = r.end(); n < N; ++n) { + // Loop over particles in bucket n. + for (auto iter = mPointPartitioner->indices(n); iter; ++iter) { + const Index32& id = *iter; + this->getAtt(id, att); + mParticles.getPos(id, pos); + const Vec3R P = mMap.applyInverseMap(pos); + this->makeSphere(P, R, att, acc); + } + } + } + + /// @brief Threaded rasterization of particles as spheres with velocity trails + /// @param r range of indices into the list of particles + /// @param delta inter-sphere spacing + void rasterTrails(const tbb::blocked_range& r, Real delta) + { + AccessorT acc = mGrid->getAccessor(); // local accessor + bool run = true; + AttT att; + Vec3R pos, vel; + Real rad; + const Vec3R origin = mMap.applyInverseMap(Vec3R(0,0,0)); + const Real Rmin = mParent.mRmin, invDx = 1 / mParent.mDx; + + // Loop over buckets + for (size_t n = r.begin(), N = r.end(); n < N; ++n) { + // Loop over particles in bucket n. + typename PointPartitionerT::IndexIterator iter = mPointPartitioner->indices(n); + for ( ; run && iter; ++iter) { + const Index32& id = *iter; + mParticles.getPosRadVel(id, pos, rad, vel); + const Real R0 = invDx * rad; + if (this->ignoreParticle(R0)) continue; + this->getAtt(id, att); + const Vec3R P0 = mMap.applyInverseMap(pos); + const Vec3R V = mMap.applyInverseMap(vel) - origin; // exclude translation + const Real speed = V.length(), invSpeed = 1.0 / speed; + const Vec3R Nrml = -V * invSpeed; // inverse normalized direction + Vec3R P = P0; // local position of instance + Real R = R0, d = 0; // local radius and length of trail + for (size_t m = 0; run && d <= speed ; ++m) { + run = this->makeSphere(P, R, att, acc); + P += 0.5 * delta * R * Nrml; // adaptive offset along inverse velocity direction + d = (P - P0).length(); // current length of trail + R = R0 - (R0 - Rmin) * d * invSpeed; // R = R0 -> mRmin(e.g. 1.5) + }//end loop over sphere instances + }//end loop over particles + }//end loop over buckets + } + + void cook() + { + // parallelize over the point buckets + const Index32 bucketCount = Index32(mPointPartitioner->size()); + + if (mParent.mGrainSize>0) { + tbb::parallel_reduce( + tbb::blocked_range(0, bucketCount, mParent.mGrainSize), *this); + } else { + (*this)(tbb::blocked_range(0, bucketCount)); + } + } + + /// @brief Rasterize sphere at position P and radius R. + /// @return @c false if rasterization was interrupted + /// + /// @param P coordinates of the particle position in voxel units + /// @param R radius of particle in voxel units + /// @param att + /// @param acc grid accessor with a private copy of the grid + bool makeSphere(const Vec3R& P, Real R, const AttT& att, AccessorT& acc) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (OutputIsMask) { + return makeSphereMask(P, R, att, acc); + } else { + return makeNarrowBandSphere(P, R, att, acc); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + /// @brief Rasterize sphere at position P and radius R into + /// a narrow-band level set with half-width, mHalfWidth. + /// @return @c false if rasterization was interrupted + /// + /// @param P coordinates of the particle position in voxel units + /// @param R radius of particle in voxel units + /// @param att an optional user-defined attribute value to be associated with voxels + /// @param acc grid accessor with a private copy of the grid + /// + /// @note For best performance all computations are performed in voxel space, + /// with the important exception of the final level set value that is converted + /// to world units (the grid stores the closest Euclidean signed distances + /// measured in world units). Also note we use the convention of positive distances + /// outside the surface and negative distances inside the surface. + bool makeNarrowBandSphere(const Vec3R& P, Real R, const AttT& att, AccessorT& acc) + { + const Real + dx = mParent.mDx, + w = mParent.mHalfWidth, + max = R + w, // maximum distance in voxel units + max2 = math::Pow2(max), // square of maximum distance in voxel units + min2 = math::Pow2(math::Max(Real(0), R - w)); // square of minimum distance + // Bounding box of the sphere + const Coord + lo(math::Floor(P[0]-max),math::Floor(P[1]-max),math::Floor(P[2]-max)), + hi(math::Ceil( P[0]+max),math::Ceil( P[1]+max),math::Ceil( P[2]+max)); + const ValueT inside = -mGrid->background(); + + ValueT v; + size_t count = 0; + for (Coord c = lo; c.x() <= hi.x(); ++c.x()) { + //only check interrupter every 32'th scan in x + if (!(count++ & ((1<<5)-1)) && util::wasInterrupted(mParent.mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return false; + } + const Real x2 = math::Pow2(c.x() - P[0]); + for (c.y() = lo.y(); c.y() <= hi.y(); ++c.y()) { + const Real x2y2 = x2 + math::Pow2(c.y() - P[1]); + for (c.z() = lo.z(); c.z() <= hi.z(); ++c.z()) { + const Real x2y2z2 = x2y2 + math::Pow2(c.z()-P[2]); // squared dist from c to P +#if defined __INTEL_COMPILER + _Pragma("warning (push)") + _Pragma("warning (disable:186)") // "pointless comparison of unsigned integer with zero" +#endif + if (x2y2z2 >= max2 || (!acc.probeValue(c, v) && (v < ValueT(0)))) + continue;//outside narrow band of the particle or inside existing level set +#if defined __INTEL_COMPILER + _Pragma("warning (pop)") +#endif + if (x2y2z2 <= min2) {//inside narrow band of the particle. + acc.setValueOff(c, inside); + continue; + } + // convert signed distance from voxel units to world units + //const ValueT d=dx*(math::Sqrt(x2y2z2) - R); + const ValueT d = Merge(static_cast(dx*(math::Sqrt(x2y2z2)-R)), att); + if (d < v) acc.setValue(c, d);//CSG union + }//end loop over z + }//end loop over y + }//end loop over x + return true; + } + + /// @brief Rasterize a sphere of radius @a r at position @a p into a boolean mask grid. + /// @return @c false if rasterization was interrupted + bool makeSphereMask(const Vec3R& p, Real r, const AttT& att, AccessorT& acc) + { + const Real + rSquared = r * r, // sphere radius squared, in voxel units + inW = r / math::Sqrt(6.0); // half the width in voxel units of an inscribed cube + const Coord + // Bounding box of the sphere + outLo(math::Floor(p[0] - r), math::Floor(p[1] - r), math::Floor(p[2] - r)), + outHi(math::Ceil(p[0] + r), math::Ceil(p[1] + r), math::Ceil(p[2] + r)), + // Bounds of the inscribed cube + inLo(math::Ceil(p[0] - inW), math::Ceil(p[1] - inW), math::Ceil(p[2] - inW)), + inHi(math::Floor(p[0] + inW), math::Floor(p[1] + inW), math::Floor(p[2] + inW)); + // Bounding boxes of regions comprising out - in + /// @todo These could be divided further into sparsely- and densely-filled subregions. + const std::vector padding{ + CoordBBox(outLo.x(), outLo.y(), outLo.z(), inLo.x()-1, outHi.y(), outHi.z()), + CoordBBox(inHi.x()+1, outLo.y(), outLo.z(), outHi.x(), outHi.y(), outHi.z()), + CoordBBox(outLo.x(), outLo.y(), outLo.z(), outHi.x(), inLo.y()-1, outHi.z()), + CoordBBox(outLo.x(), inHi.y()+1, outLo.z(), outHi.x(), outHi.y(), outHi.z()), + CoordBBox(outLo.x(), outLo.y(), outLo.z(), outHi.x(), outHi.y(), inLo.z()-1), + CoordBBox(outLo.x(), outLo.y(), inHi.z()+1, outHi.x(), outHi.y(), outHi.z()), + }; + const ValueT onValue = Merge(SdfT(1), att); + + // Sparsely fill the inscribed cube. + /// @todo Use sparse fill only if 2r > leaf width? + acc.tree().sparseFill(CoordBBox(inLo, inHi), onValue); + + // Densely fill the remaining regions. + for (const auto& bbox: padding) { + if (util::wasInterrupted(mParent.mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return false; + } + const Coord &bmin = bbox.min(), &bmax = bbox.max(); + Coord c; + Real cx, cy, cz; + for (c = bmin, cx = c.x(); c.x() <= bmax.x(); ++c.x(), cx += 1) { + const Real x2 = math::Pow2(cx - p[0]); + for (c.y() = bmin.y(), cy = c.y(); c.y() <= bmax.y(); ++c.y(), cy += 1) { + const Real x2y2 = x2 + math::Pow2(cy - p[1]); + for (c.z() = bmin.z(), cz = c.z(); c.z() <= bmax.z(); ++c.z(), cz += 1) { + const Real x2y2z2 = x2y2 + math::Pow2(cz - p[2]); + if (x2y2z2 < rSquared) { + acc.setValue(c, onValue); + } + } + } + } + } + return true; + } + + using FuncType = typename std::function&)>; + + template + typename std::enable_if::type + getAtt(size_t, AttT&) const {} + + template + typename std::enable_if::type + getAtt(size_t n, AttT& a) const { mParticles.getAtt(n, a); } + + template + typename std::enable_if::value, ValueT>::type + Merge(T s, const AttT&) const { return s; } + + template + typename std::enable_if::value, ValueT>::type + Merge(T s, const AttT& a) const { return ValueT(s,a); } + + ParticlesToLevelSetT& mParent; + const ParticleListT& mParticles;//list of particles + GridT* mGrid; + const math::MapBase& mMap; + size_t mMinCount, mMaxCount;//counters for ignored particles! + FuncType mTask; + const bool mIsCopy; + PointPartitionerT* mPointPartitioner; +}; // struct ParticlesToLevelSet::Raster + + +///////////////////// YOU CAN SAFELY IGNORE THIS SECTION ///////////////////// + + +namespace p2ls_internal { + +// This is a simple type that combines a distance value and a particle +// attribute. It's required for attribute transfer which is defined in the +// Raster class above. +/// @private +template +class BlindData +{ +public: + using type = VisibleT; + using VisibleType = VisibleT; + using BlindType = BlindT; + + BlindData() {} + explicit BlindData(VisibleT v) : mVisible(v), mBlind(zeroVal()) {} + BlindData(VisibleT v, BlindT b) : mVisible(v), mBlind(b) {} + BlindData(const BlindData&) = default; + BlindData& operator=(const BlindData&) = default; + const VisibleT& visible() const { return mVisible; } + const BlindT& blind() const { return mBlind; } + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + bool operator==(const BlindData& rhs) const { return mVisible == rhs.mVisible; } + OPENVDB_NO_FP_EQUALITY_WARNING_END + bool operator< (const BlindData& rhs) const { return mVisible < rhs.mVisible; } + bool operator> (const BlindData& rhs) const { return mVisible > rhs.mVisible; } + BlindData operator+(const BlindData& rhs) const { return BlindData(mVisible + rhs.mVisible); } + BlindData operator-(const BlindData& rhs) const { return BlindData(mVisible - rhs.mVisible); } + BlindData operator-() const { return BlindData(-mVisible, mBlind); } + +protected: + VisibleT mVisible; + BlindT mBlind; +}; + +/// @private +// Required by several of the tree nodes +template +inline std::ostream& operator<<(std::ostream& ostr, const BlindData& rhs) +{ + ostr << rhs.visible(); + return ostr; +} + +/// @private +// Required by math::Abs +template +inline BlindData Abs(const BlindData& x) +{ + return BlindData(math::Abs(x.visible()), x.blind()); +} + +/// @private +// Required to support the (zeroVal() + val) idiom. +template +inline BlindData +operator+(const BlindData& x, const T& rhs) +{ + return BlindData(x.visible() + static_cast(rhs), x.blind()); +} + +} // namespace p2ls_internal + + +////////////////////////////////////////////////////////////////////////////// + + +// The following are convenience functions for common use cases. + +template +inline void +particlesToSdf(const ParticleListT& plist, GridT& grid, InterrupterT* interrupt) +{ + static_assert(std::is_floating_point::value, + "particlesToSdf requires an SDF grid with floating-point values"); + + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_LOG_WARN("particlesToSdf requires a level set grid;" + " try Grid::setGridClass(openvdb::GRID_LEVEL_SET)"); + } + + ParticlesToLevelSet p2ls(grid, interrupt); + p2ls.rasterizeSpheres(plist); + tools::pruneLevelSet(grid.tree()); +} + +template +inline void +particlesToSdf(const ParticleListT& plist, GridT& grid, Real radius, InterrupterT* interrupt) +{ + static_assert(std::is_floating_point::value, + "particlesToSdf requires an SDF grid with floating-point values"); + + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_LOG_WARN("particlesToSdf requires a level set grid;" + " try Grid::setGridClass(openvdb::GRID_LEVEL_SET)"); + } + + ParticlesToLevelSet p2ls(grid, interrupt); + p2ls.rasterizeSpheres(plist, radius); + tools::pruneLevelSet(grid.tree()); +} + +template +inline void +particleTrailsToSdf(const ParticleListT& plist, GridT& grid, Real delta, InterrupterT* interrupt) +{ + static_assert(std::is_floating_point::value, + "particleTrailsToSdf requires an SDF grid with floating-point values"); + + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_LOG_WARN("particlesToSdf requires a level set grid;" + " try Grid::setGridClass(openvdb::GRID_LEVEL_SET)"); + } + + ParticlesToLevelSet p2ls(grid, interrupt); + p2ls.rasterizeTrails(plist, delta); + tools::pruneLevelSet(grid.tree()); +} + +template +inline void +particlesToMask(const ParticleListT& plist, GridT& grid, InterrupterT* interrupt) +{ + static_assert(std::is_same::value, + "particlesToMask requires a boolean-valued grid"); + ParticlesToLevelSet p2ls(grid, interrupt); + p2ls.rasterizeSpheres(plist); + tools::prune(grid.tree()); +} + +template +inline void +particlesToMask(const ParticleListT& plist, GridT& grid, Real radius, InterrupterT* interrupt) +{ + static_assert(std::is_same::value, + "particlesToMask requires a boolean-valued grid"); + ParticlesToLevelSet p2ls(grid, interrupt); + p2ls.rasterizeSpheres(plist, radius); + tools::prune(grid.tree()); +} + +template +inline void +particleTrailsToMask(const ParticleListT& plist, GridT& grid, Real delta, InterrupterT* interrupt) +{ + static_assert(std::is_same::value, + "particleTrailsToMask requires a boolean-valued grid"); + ParticlesToLevelSet p2ls(grid, interrupt); + p2ls.rasterizeTrails(plist, delta); + tools::prune(grid.tree()); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/PointAdvect.h b/openvdb/tools/PointAdvect.h new file mode 100644 index 00000000..12b1177a --- /dev/null +++ b/openvdb/tools/PointAdvect.h @@ -0,0 +1,393 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @author Ken Museth, D.J. Hill (openvdb port, added staggered grid support) +/// +/// @file tools/PointAdvect.h +/// +/// @brief Class PointAdvect advects points (with position) in a static velocity field + +#ifndef OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED + +#include +#include // min +#include // Vec3 types and version number +#include // grid +#include +#include "Interpolation.h" // sampling +#include "VelocityFields.h" // VelocityIntegrator +#include // threading +#include // threading +#include // for cancel +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// Class that holds a Vec3 grid, to be interpreted as the closest point to a constraint +/// surface. Supports a method to allow a point to be projected onto the closest point +/// on the constraint surface. Uses Caching. +template +class ClosestPointProjector +{ +public: + using CptGridType = CptGridT; + using CptAccessor = typename CptGridType::ConstAccessor; + using CptValueType = typename CptGridType::ValueType; + + ClosestPointProjector(): + mCptIterations(0) + { + } + ClosestPointProjector(const CptGridType& cptGrid, int n): + mCptGrid(&cptGrid), + mCptAccessor(cptGrid.getAccessor()), + mCptIterations(n) + { + } + ClosestPointProjector(const ClosestPointProjector &other): + mCptGrid(other.mCptGrid), + mCptAccessor(mCptGrid->getAccessor()), + mCptIterations(other.mCptIterations) + { + } + void setConstraintIterations(unsigned int cptIterations) { mCptIterations = cptIterations; } + unsigned int numIterations() { return mCptIterations; } + + // point constraint + template + inline void projectToConstraintSurface(LocationType& W) const + { + /// Entries in the CPT tree are the closest point to the constraint surface. + /// The interpolation step in sample introduces error so that the result + /// of a single sample may not lie exactly on the surface. The iterations + /// in the loop exist to minimize this error. + CptValueType result(W[0], W[1],W[2]); + for (unsigned int i = 0; i < mCptIterations; ++i) { + const Vec3R location = mCptGrid->worldToIndex(Vec3R(result[0], result[1], result[2])); + BoxSampler::sample(mCptAccessor, location, result); + } + W[0] = result[0]; + W[1] = result[1]; + W[2] = result[2]; + } + +private: + const CptGridType* mCptGrid; // Closest-Point-Transform vector field + CptAccessor mCptAccessor; + unsigned int mCptIterations; +};// end of ClosestPointProjector class + +//////////////////////////////////////// + + +/// Performs passive or constrained advection of points in a velocity field +/// represented by an OpenVDB grid and an optional closest-point-transform (CPT) +/// represented in another OpenVDB grid. Note the CPT is assumed to be +/// in world coordinates and NOT index coordinates! +/// Supports both collocated velocity grids and staggered velocity grids +/// +/// The @c PointListT template argument refers to any class with the following +/// interface (e.g., std::vector): +/// @code +/// class PointList { +/// ... +/// public: +/// using value_type = internal_vector3_type; // must support [] component access +/// openvdb::Index size() const; // number of points in list +/// value_type& operator[](int n); // world space position of nth point +/// }; +/// @endcode +/// +/// @note All methods (except size) are assumed to be thread-safe and +/// the positions are returned as non-const references since the +/// advection method needs to modify them! +template, + bool StaggeredVelocity = false, + typename InterrupterType = util::NullInterrupter> +class PointAdvect +{ +public: + using GridType = GridT; + using PointListType = PointListT; + using LocationType = typename PointListT::value_type; + using VelocityFieldIntegrator = VelocityIntegrator; + + PointAdvect(const GridT& velGrid, InterrupterType* interrupter = nullptr): + mVelGrid(&velGrid), + mPoints(nullptr), + mIntegrationOrder(1), + mThreaded(true), + mInterrupter(interrupter) + { + } + PointAdvect(const PointAdvect &other) : + mVelGrid(other.mVelGrid), + mPoints(other.mPoints), + mDt(other.mDt), + mAdvIterations(other.mAdvIterations), + mIntegrationOrder(other.mIntegrationOrder), + mThreaded(other.mThreaded), + mInterrupter(other.mInterrupter) + { + } + virtual ~PointAdvect() + { + } + /// If the order of the integration is set to zero no advection is performed + bool earlyOut() const { return (mIntegrationOrder==0);} + /// get & set + void setThreaded(bool threaded) { mThreaded = threaded; } + bool getThreaded() { return mThreaded; } + void setIntegrationOrder(unsigned int order) {mIntegrationOrder = order;} + + /// Constrained advection of a list of points over a time = dt * advIterations + void advect(PointListT& points, float dt, unsigned int advIterations = 1) + { + if (this->earlyOut()) return; // nothing to do! + mPoints = &points; + mDt = dt; + mAdvIterations = advIterations; + + if (mInterrupter) mInterrupter->start("Advecting points by OpenVDB velocity field: "); + if (mThreaded) { + tbb::parallel_for(tbb::blocked_range(0, mPoints->size()), *this); + } else { + (*this)(tbb::blocked_range(0, mPoints->size())); + } + if (mInterrupter) mInterrupter->end(); + } + + /// Never call this method directly - it is use by TBB and has to be public! + void operator() (const tbb::blocked_range &range) const + { + if (mInterrupter && mInterrupter->wasInterrupted()) { + tbb::task::self().cancel_group_execution(); + } + + VelocityFieldIntegrator velField(*mVelGrid); + switch (mIntegrationOrder) { + case 1: + { + for (size_t n = range.begin(); n != range.end(); ++n) { + LocationType& X0 = (*mPoints)[n]; + // loop over number of time steps + for (unsigned int i = 0; i < mAdvIterations; ++i) { + velField.template rungeKutta<1>(mDt, X0); + } + } + } + break; + case 2: + { + for (size_t n = range.begin(); n != range.end(); ++n) { + LocationType& X0 = (*mPoints)[n]; + // loop over number of time steps + for (unsigned int i = 0; i < mAdvIterations; ++i) { + velField.template rungeKutta<2>(mDt, X0); + } + } + } + break; + case 3: + { + for (size_t n = range.begin(); n != range.end(); ++n) { + LocationType& X0 = (*mPoints)[n]; + // loop over number of time steps + for (unsigned int i = 0; i < mAdvIterations; ++i) { + velField.template rungeKutta<3>(mDt, X0); + } + } + } + break; + case 4: + { + for (size_t n = range.begin(); n != range.end(); ++n) { + LocationType& X0 = (*mPoints)[n]; + // loop over number of time steps + for (unsigned int i = 0; i < mAdvIterations; ++i) { + velField.template rungeKutta<4>(mDt, X0); + } + } + } + break; + } + } + +private: + // the velocity field + const GridType* mVelGrid; + + // vertex list of all the points + PointListT* mPoints; + + // time integration parameters + float mDt; // time step + unsigned int mAdvIterations; // number of time steps + unsigned int mIntegrationOrder; + + // operational parameters + bool mThreaded; + InterrupterType* mInterrupter; + +};//end of PointAdvect class + + +template, + bool StaggeredVelocity = false, + typename CptGridType = GridT, + typename InterrupterType = util::NullInterrupter> +class ConstrainedPointAdvect +{ +public: + using GridType = GridT; + using LocationType = typename PointListT::value_type; + using VelocityIntegratorType = VelocityIntegrator; + using ClosestPointProjectorType = ClosestPointProjector; + using PointListType = PointListT; + + ConstrainedPointAdvect(const GridType& velGrid, + const GridType& cptGrid, int cptn, InterrupterType* interrupter = nullptr): + mVelGrid(&velGrid), + mCptGrid(&cptGrid), + mCptIter(cptn), + mInterrupter(interrupter) + { + } + ConstrainedPointAdvect(const ConstrainedPointAdvect& other): + mVelGrid(other.mVelGrid), + mCptGrid(other.mCptGrid), + mCptIter(other.mCptIter), + mPoints(other.mPoints), + mDt(other.mDt), + mAdvIterations(other.mAdvIterations), + mIntegrationOrder(other.mIntegrationOrder), + mThreaded(other.mThreaded), + mInterrupter(other.mInterrupter) + { + } + virtual ~ConstrainedPointAdvect(){} + + void setConstraintIterations(unsigned int cptIter) {mCptIter = cptIter;} + void setIntegrationOrder(unsigned int order) {mIntegrationOrder = order;} + + void setThreaded(bool threaded) { mThreaded = threaded; } + bool getThreaded() { return mThreaded; } + + /// Constrained Advection a list of points over a time = dt * advIterations + void advect(PointListT& points, float dt, unsigned int advIterations = 1) + { + mPoints = &points; + mDt = dt; + + if (mIntegrationOrder==0 && mCptIter == 0) { + return; // nothing to do! + } + (mIntegrationOrder>0) ? mAdvIterations = advIterations : mAdvIterations = 1; + + if (mInterrupter) mInterrupter->start("Advecting points by OpenVDB velocity field: "); + const size_t N = mPoints->size(); + + if (mThreaded) { + tbb::parallel_for(tbb::blocked_range(0, N), *this); + } else { + (*this)(tbb::blocked_range(0, N)); + } + if (mInterrupter) mInterrupter->end(); + } + + + /// Never call this method directly - it is use by TBB and has to be public! + void operator() (const tbb::blocked_range &range) const + { + if (mInterrupter && mInterrupter->wasInterrupted()) { + tbb::task::self().cancel_group_execution(); + } + + VelocityIntegratorType velField(*mVelGrid); + ClosestPointProjectorType cptField(*mCptGrid, mCptIter); + switch (mIntegrationOrder) { + case 0://pure CPT projection + { + for (size_t n = range.begin(); n != range.end(); ++n) { + LocationType& X0 = (*mPoints)[n]; + for (unsigned int i = 0; i < mAdvIterations; ++i) { + cptField.projectToConstraintSurface(X0); + } + } + } + break; + case 1://1'th order advection and CPT projection + { + for (size_t n = range.begin(); n != range.end(); ++n) { + LocationType& X0 = (*mPoints)[n]; + for (unsigned int i = 0; i < mAdvIterations; ++i) { + velField.template rungeKutta<1>(mDt, X0); + cptField.projectToConstraintSurface(X0); + } + } + } + break; + case 2://2'nd order advection and CPT projection + { + for (size_t n = range.begin(); n != range.end(); ++n) { + LocationType& X0 = (*mPoints)[n]; + for (unsigned int i = 0; i < mAdvIterations; ++i) { + velField.template rungeKutta<2>(mDt, X0); + cptField.projectToConstraintSurface(X0); + } + } + } + break; + + case 3://3'rd order advection and CPT projection + { + for (size_t n = range.begin(); n != range.end(); ++n) { + LocationType& X0 = (*mPoints)[n]; + for (unsigned int i = 0; i < mAdvIterations; ++i) { + velField.template rungeKutta<3>(mDt, X0); + cptField.projectToConstraintSurface(X0); + } + } + } + break; + case 4://4'th order advection and CPT projection + { + for (size_t n = range.begin(); n != range.end(); ++n) { + LocationType& X0 = (*mPoints)[n]; + for (unsigned int i = 0; i < mAdvIterations; ++i) { + velField.template rungeKutta<4>(mDt, X0); + cptField.projectToConstraintSurface(X0); + } + } + } + break; + } + } + +private: + const GridType* mVelGrid; // the velocity field + const GridType* mCptGrid; + int mCptIter; + PointListT* mPoints; // vertex list of all the points + + // time integration parameters + float mDt; // time step + unsigned int mAdvIterations; // number of time steps + unsigned int mIntegrationOrder; // order of Runge-Kutta integration + // operational parameters + bool mThreaded; + InterrupterType* mInterrupter; +};// end of ConstrainedPointAdvect class + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/PointIndexGrid.h b/openvdb/tools/PointIndexGrid.h new file mode 100644 index 00000000..a2e2a7c4 --- /dev/null +++ b/openvdb/tools/PointIndexGrid.h @@ -0,0 +1,1807 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file PointIndexGrid.h +/// +/// @brief Space-partitioning acceleration structure for points. Partitions +/// the points into voxels to accelerate range and nearest neighbor +/// searches. +/// +/// @note Leaf nodes store a single point-index array and the voxels are only +/// integer offsets into that array. The actual points are never stored +/// in the acceleration structure, only offsets into an external array. +/// +/// @author Mihai Alden + +#ifndef OPENVDB_TOOLS_POINT_INDEX_GRID_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_INDEX_GRID_HAS_BEEN_INCLUDED + +#include "PointPartitioner.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include // for std::min(), std::max() +#include // for std::sqrt() +#include +#include +#include // for std::is_same +#include // for std::pair +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +namespace tree { +template struct SameLeafConfig; // forward declaration +} + +namespace tools { + +template struct PointIndexLeafNode; // forward declaration + +/// Point index tree configured to match the default OpenVDB tree configuration +using PointIndexTree = tree::Tree, 4>, 5>>>; + +/// Point index grid +using PointIndexGrid = Grid; + + +//////////////////////////////////////// + + +/// @interface PointArray +/// Expected interface for the PointArray container: +/// @code +/// template +/// struct PointArray +/// { +/// // The type used to represent world-space point positions +/// using PosType = VectorType; +/// +/// // Return the number of points in the array +/// size_t size() const; +/// +/// // Return the world-space position of the nth point in the array. +/// void getPos(size_t n, PosType& xyz) const; +/// }; +/// @endcode + + +//////////////////////////////////////// + + +/// @brief Partition points into a point index grid to accelerate range and +/// nearest-neighbor searches. +/// +/// @param points world-space point array conforming to the PointArray interface +/// @param voxelSize voxel size in world units +template +inline typename GridT::Ptr +createPointIndexGrid(const PointArrayT& points, double voxelSize); + + +/// @brief Partition points into a point index grid to accelerate range and +/// nearest-neighbor searches. +/// +/// @param points world-space point array conforming to the PointArray interface +/// @param xform world-to-index-space transform +template +inline typename GridT::Ptr +createPointIndexGrid(const PointArrayT& points, const math::Transform& xform); + + +/// @brief Return @c true if the given point index grid represents a valid partitioning +/// of the given point array. +/// +/// @param points world-space point array conforming to the PointArray interface +/// @param grid point index grid to validate +template +inline bool +isValidPartition(const PointArrayT& points, const GridT& grid); + + +/// Repartition the @a points if needed, otherwise return the input @a grid. +template +inline typename GridT::ConstPtr +getValidPointIndexGrid(const PointArrayT& points, const typename GridT::ConstPtr& grid); + +/// Repartition the @a points if needed, otherwise return the input @a grid. +template +inline typename GridT::Ptr +getValidPointIndexGrid(const PointArrayT& points, const typename GridT::Ptr& grid); + + +//////////////////////////////////////// + + +/// Accelerated range and nearest-neighbor searches for point index grids +template +struct PointIndexIterator +{ + using ConstAccessor = tree::ValueAccessor; + using LeafNodeType = typename TreeType::LeafNodeType; + using ValueType = typename TreeType::ValueType; + + + PointIndexIterator(); + PointIndexIterator(const PointIndexIterator& rhs); + PointIndexIterator& operator=(const PointIndexIterator& rhs); + + + /// @brief Construct an iterator over the indices of the points contained in voxel (i, j, k). + /// @param ijk the voxel containing the points over which to iterate + /// @param acc an accessor for the grid or tree that holds the point indices + PointIndexIterator(const Coord& ijk, ConstAccessor& acc); + + + /// @brief Construct an iterator over the indices of the points contained in + /// the given bounding box. + /// @param bbox the bounding box of the voxels containing the points over which to iterate + /// @param acc an accessor for the grid or tree that holds the point indices + /// @note The range of the @a bbox is inclusive. Thus, a bounding box with + /// min = max is not empty but rather encloses a single voxel. + PointIndexIterator(const CoordBBox& bbox, ConstAccessor& acc); + + + /// @brief Clear the iterator and update it with the result of the given voxel query. + /// @param ijk the voxel containing the points over which to iterate + /// @param acc an accessor for the grid or tree that holds the point indices + void searchAndUpdate(const Coord& ijk, ConstAccessor& acc); + + + /// @brief Clear the iterator and update it with the result of the given voxel region query. + /// @param bbox the bounding box of the voxels containing the points over which to iterate + /// @param acc an accessor for the grid or tree that holds the point indices + /// @note The range of the @a bbox is inclusive. Thus, a bounding box with + /// min = max is not empty but rather encloses a single voxel. + void searchAndUpdate(const CoordBBox& bbox, ConstAccessor& acc); + + + /// @brief Clear the iterator and update it with the result of the given + /// index-space bounding box query. + /// @param bbox index-space bounding box + /// @param acc an accessor for the grid or tree that holds the point indices + /// @param points world-space point array conforming to the PointArray interface + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + template + void searchAndUpdate(const BBoxd& bbox, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform); + + + /// @brief Clear the iterator and update it with the result of the given + /// index-space radial query. + /// @param center index-space center + /// @param radius index-space radius + /// @param acc an accessor for the grid or tree that holds the point indices + /// @param points world-space point array conforming to the PointArray interface + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + /// @param subvoxelAccuracy if true, check individual points against the search region, + /// otherwise return all points that reside in voxels that are inside + /// or intersect the search region + template + void searchAndUpdate(const Vec3d& center, double radius, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform, bool subvoxelAccuracy = true); + + + /// @brief Clear the iterator and update it with the result of the given + /// world-space bounding box query. + /// @param bbox world-space bounding box + /// @param acc an accessor for the grid or tree that holds the point indices + /// @param points world-space point array conforming to the PointArray interface + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + template + void worldSpaceSearchAndUpdate(const BBoxd& bbox, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform); + + + /// @brief Clear the iterator and update it with the result of the given + /// world-space radial query. + /// @param center world-space center + /// @param radius world-space radius + /// @param acc an accessor for the grid or tree that holds the point indices + /// @param points world-space point array conforming to the PointArray interface + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + /// @param subvoxelAccuracy if true, check individual points against the search region, + /// otherwise return all points that reside in voxels that are inside + /// or intersect the search region + template + void worldSpaceSearchAndUpdate(const Vec3d& center, double radius, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform, bool subvoxelAccuracy = true); + + + /// Reset the iterator to point to the first item. + void reset(); + + /// Return a const reference to the item to which this iterator is pointing. + const ValueType& operator*() const { return *mRange.first; } + + /// @{ + /// @brief Return @c true if this iterator is not yet exhausted. + bool test() const { return mRange.first < mRange.second || mIter != mRangeList.end(); } + operator bool() const { return this->test(); } + /// @} + + /// Advance iterator to next item. + void increment(); + + /// Advance iterator to next item. + void operator++() { this->increment(); } + + + /// @brief Advance iterator to next item. + /// @return @c true if this iterator is not yet exhausted. + bool next(); + + /// Return the number of point indices in the iterator range. + size_t size() const; + + /// Return @c true if both iterators point to the same element. + bool operator==(const PointIndexIterator& p) const { return mRange.first == p.mRange.first; } + bool operator!=(const PointIndexIterator& p) const { return !this->operator==(p); } + + +private: + using Range = std::pair; + using RangeDeque = std::deque; + using RangeDequeCIter = typename RangeDeque::const_iterator; + using IndexArray = std::unique_ptr; + + void clear(); + + // Primary index collection + Range mRange; + RangeDeque mRangeList; + RangeDequeCIter mIter; + // Secondary index collection + IndexArray mIndexArray; + size_t mIndexArraySize; +}; // struct PointIndexIterator + + +/// @brief Selectively extract and filter point data using a custom filter operator. +/// +/// @par FilterType example: +/// @interface FilterType +/// @code +/// template +/// struct WeightedAverageAccumulator { +/// using ValueType = T; +/// +/// WeightedAverageAccumulator(T const * const array, const T radius) +/// : mValues(array), mInvRadius(1.0/radius), mWeightSum(0.0), mValueSum(0.0) {} +/// +/// void reset() { mWeightSum = mValueSum = T(0.0); } +/// +/// // the following method is invoked by the PointIndexFilter +/// void operator()(const T distSqr, const size_t pointIndex) { +/// const T weight = T(1.0) - openvdb::math::Sqrt(distSqr) * mInvRadius; +/// mWeightSum += weight; +/// mValueSum += weight * mValues[pointIndex]; +/// } +/// +/// T result() const { return mWeightSum > T(0.0) ? mValueSum / mWeightSum : T(0.0); } +/// +/// private: +/// T const * const mValues; +/// const T mInvRadius; +/// T mWeightSum, mValueSum; +/// }; // struct WeightedAverageAccumulator +/// @endcode +template +struct PointIndexFilter +{ + using PosType = typename PointArray::PosType; + using ScalarType = typename PosType::value_type; + using ConstAccessor = tree::ValueAccessor; + + /// @brief Constructor + /// @param points world-space point array conforming to the PointArray interface + /// @param tree a point index tree + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + PointIndexFilter(const PointArray& points, const TreeType& tree, const math::Transform& xform); + + /// Thread safe copy constructor + PointIndexFilter(const PointIndexFilter& rhs); + + /// @brief Perform a radial search query and apply the given filter + /// operator to the selected points. + /// @param center world-space center + /// @param radius world-space radius + /// @param op custom filter operator (see the FilterType example for interface details) + template + void searchAndApply(const PosType& center, ScalarType radius, FilterType& op); + +private: + PointArray const * const mPoints; + ConstAccessor mAcc; + const math::Transform mXform; + const ScalarType mInvVoxelSize; + PointIndexIterator mIter; +}; // struct PointIndexFilter + + +//////////////////////////////////////// + +// Internal operators and implementation details + + +namespace point_index_grid_internal { + +template +struct ValidPartitioningOp +{ + ValidPartitioningOp(tbb::atomic& hasChanged, + const PointArrayT& points, const math::Transform& xform) + : mPoints(&points) + , mTransform(&xform) + , mHasChanged(&hasChanged) + { + } + + template + void operator()(LeafT &leaf, size_t /*leafIndex*/) const + { + if ((*mHasChanged)) { + tbb::task::self().cancel_group_execution(); + return; + } + + using IndexArrayT = typename LeafT::IndexArray; + using IndexT = typename IndexArrayT::value_type; + using PosType = typename PointArrayT::PosType; + + typename LeafT::ValueOnCIter iter; + Coord voxelCoord; + PosType point; + + const IndexT + *begin = static_cast(nullptr), + *end = static_cast(nullptr); + + for (iter = leaf.cbeginValueOn(); iter; ++iter) { + + if ((*mHasChanged)) break; + + voxelCoord = iter.getCoord(); + leaf.getIndices(iter.pos(), begin, end); + + while (begin < end) { + + mPoints->getPos(*begin, point); + if (voxelCoord != mTransform->worldToIndexCellCentered(point)) { + mHasChanged->fetch_and_store(true); + break; + } + + ++begin; + } + } + } + +private: + PointArrayT const * const mPoints; + math::Transform const * const mTransform; + tbb::atomic * const mHasChanged; +}; + + +template +struct PopulateLeafNodesOp +{ + using IndexT = uint32_t; + using Partitioner = PointPartitioner; + + PopulateLeafNodesOp(std::unique_ptr& leafNodes, + const Partitioner& partitioner) + : mLeafNodes(leafNodes.get()) + , mPartitioner(&partitioner) + { + } + + void operator()(const tbb::blocked_range& range) const { + + using VoxelOffsetT = typename Partitioner::VoxelOffsetType; + + size_t maxPointCount = 0; + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + maxPointCount = std::max(maxPointCount, mPartitioner->indices(n).size()); + } + + const IndexT voxelCount = LeafNodeT::SIZE; + + // allocate histogram buffers + std::unique_ptr offsets{new VoxelOffsetT[maxPointCount]}; + std::unique_ptr histogram{new IndexT[voxelCount]}; + + VoxelOffsetT const * const voxelOffsets = mPartitioner->voxelOffsets().get(); + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + LeafNodeT* node = new LeafNodeT(); + node->setOrigin(mPartitioner->origin(n)); + + typename Partitioner::IndexIterator it = mPartitioner->indices(n); + + const size_t pointCount = it.size(); + IndexT const * const indices = &*it; + + // local copy of voxel offsets. + for (IndexT i = 0; i < pointCount; ++i) { + offsets[i] = voxelOffsets[ indices[i] ]; + } + + // compute voxel-offset histogram + memset(&histogram[0], 0, voxelCount * sizeof(IndexT)); + for (IndexT i = 0; i < pointCount; ++i) { + ++histogram[ offsets[i] ]; + } + + typename LeafNodeT::NodeMaskType& mask = node->getValueMask(); + typename LeafNodeT::Buffer& buffer = node->buffer(); + + // scan histogram (all-prefix-sums) + IndexT count = 0, startOffset; + for (int i = 0; i < int(voxelCount); ++i) { + if (histogram[i] > 0) { + startOffset = count; + count += histogram[i]; + histogram[i] = startOffset; + mask.setOn(i); + } + buffer.setValue(i, count); + } + + // allocate point-index array + node->indices().resize(pointCount); + typename LeafNodeT::ValueType * const orderedIndices = node->indices().data(); + + // rank and permute + for (IndexT i = 0; i < pointCount; ++i) { + orderedIndices[ histogram[ offsets[i] ]++ ] = indices[i]; + } + + mLeafNodes[n] = node; + } + } + + ////////// + + LeafNodeT* * const mLeafNodes; + Partitioner const * const mPartitioner; +}; + + +/// Construct a @c PointIndexTree +template +inline void +constructPointTree(TreeType& tree, const math::Transform& xform, const PointArray& points) +{ + using LeafType = typename TreeType::LeafNodeType; + + std::unique_ptr leafNodes; + size_t leafNodeCount = 0; + + { + // Important: Do not disable the cell-centered transform in the PointPartitioner. + // This interpretation is assumed in the PointIndexGrid and all related + // search algorithms. + PointPartitioner partitioner; + partitioner.construct(points, xform, /*voxelOrder=*/false, /*recordVoxelOffsets=*/true); + + if (!partitioner.usingCellCenteredTransform()) { + OPENVDB_THROW(LookupError, "The PointIndexGrid requires a " + "cell-centered transform."); + } + + leafNodeCount = partitioner.size(); + leafNodes.reset(new LeafType*[leafNodeCount]); + + const tbb::blocked_range range(0, leafNodeCount); + tbb::parallel_for(range, PopulateLeafNodesOp(leafNodes, partitioner)); + } + + tree::ValueAccessor acc(tree); + for (size_t n = 0; n < leafNodeCount; ++n) { + acc.addLeaf(leafNodes[n]); + } +} + + +//////////////////////////////////////// + + +template +inline void +dequeToArray(const std::deque& d, std::unique_ptr& a, size_t& size) +{ + size = d.size(); + a.reset(new T[size]); + typename std::deque::const_iterator it = d.begin(), itEnd = d.end(); + T* item = a.get(); + for ( ; it != itEnd; ++it, ++item) *item = *it; +} + + +inline void +constructExclusiveRegions(std::vector& regions, + const CoordBBox& bbox, const CoordBBox& ibox) +{ + regions.clear(); + regions.reserve(6); + Coord cmin = ibox.min(); + Coord cmax = ibox.max(); + + // left-face bbox + regions.push_back(bbox); + regions.back().max().z() = cmin.z(); + + // right-face bbox + regions.push_back(bbox); + regions.back().min().z() = cmax.z(); + + --cmax.z(); // accounting for cell centered bucketing. + ++cmin.z(); + + // front-face bbox + regions.push_back(bbox); + CoordBBox* lastRegion = ®ions.back(); + lastRegion->min().z() = cmin.z(); + lastRegion->max().z() = cmax.z(); + lastRegion->max().x() = cmin.x(); + + // back-face bbox + regions.push_back(*lastRegion); + lastRegion = ®ions.back(); + lastRegion->min().x() = cmax.x(); + lastRegion->max().x() = bbox.max().x(); + + --cmax.x(); + ++cmin.x(); + + // bottom-face bbox + regions.push_back(*lastRegion); + lastRegion = ®ions.back(); + lastRegion->min().x() = cmin.x(); + lastRegion->max().x() = cmax.x(); + lastRegion->max().y() = cmin.y(); + + // top-face bbox + regions.push_back(*lastRegion); + lastRegion = ®ions.back(); + lastRegion->min().y() = cmax.y(); + lastRegion->max().y() = bbox.max().y(); +} + + +template +struct BBoxFilter +{ + using PosType = typename PointArray::PosType; + using ScalarType = typename PosType::value_type; + using Range = std::pair; + using RangeDeque = std::deque; + using IndexDeque = std::deque; + + BBoxFilter(RangeDeque& ranges, IndexDeque& indices, const BBoxd& bbox, + const PointArray& points, const math::Transform& xform) + : mRanges(ranges) + , mIndices(indices) + , mRegion(bbox) + , mPoints(points) + , mMap(*xform.baseMap()) + { + } + + template + void filterLeafNode(const LeafNodeType& leaf) + { + typename LeafNodeType::ValueOnCIter iter; + const IndexT + *begin = static_cast(nullptr), + *end = static_cast(nullptr); + for (iter = leaf.cbeginValueOn(); iter; ++iter) { + leaf.getIndices(iter.pos(), begin, end); + filterVoxel(iter.getCoord(), begin, end); + } + } + + void filterVoxel(const Coord&, const IndexT* begin, const IndexT* end) + { + PosType vec; + + for (; begin < end; ++begin) { + mPoints.getPos(*begin, vec); + + if (mRegion.isInside(mMap.applyInverseMap(vec))) { + mIndices.push_back(*begin); + } + } + } + +private: + RangeDeque& mRanges; + IndexDeque& mIndices; + const BBoxd mRegion; + const PointArray& mPoints; + const math::MapBase& mMap; +}; + + +template +struct RadialRangeFilter +{ + using PosType = typename PointArray::PosType; + using ScalarType = typename PosType::value_type; + using Range = std::pair; + using RangeDeque = std::deque; + using IndexDeque = std::deque; + + RadialRangeFilter(RangeDeque& ranges, IndexDeque& indices, const Vec3d& xyz, double radius, + const PointArray& points, const math::Transform& xform, + const double leafNodeDim, const bool subvoxelAccuracy) + : mRanges(ranges) + , mIndices(indices) + , mCenter(xyz) + , mWSCenter(xform.indexToWorld(xyz)) + , mVoxelDist1(ScalarType(0.0)) + , mVoxelDist2(ScalarType(0.0)) + , mLeafNodeDist1(ScalarType(0.0)) + , mLeafNodeDist2(ScalarType(0.0)) + , mWSRadiusSqr(ScalarType(radius * xform.voxelSize()[0])) + , mPoints(points) + , mSubvoxelAccuracy(subvoxelAccuracy) + { + const ScalarType voxelRadius = ScalarType(std::sqrt(3.0) * 0.5); + mVoxelDist1 = voxelRadius + ScalarType(radius); + mVoxelDist1 *= mVoxelDist1; + + if (radius > voxelRadius) { + mVoxelDist2 = ScalarType(radius) - voxelRadius; + mVoxelDist2 *= mVoxelDist2; + } + + const ScalarType leafNodeRadius = ScalarType(leafNodeDim * std::sqrt(3.0) * 0.5); + mLeafNodeDist1 = leafNodeRadius + ScalarType(radius); + mLeafNodeDist1 *= mLeafNodeDist1; + + if (radius > leafNodeRadius) { + mLeafNodeDist2 = ScalarType(radius) - leafNodeRadius; + mLeafNodeDist2 *= mLeafNodeDist2; + } + + mWSRadiusSqr *= mWSRadiusSqr; + } + + template + void filterLeafNode(const LeafNodeType& leaf) + { + { + const Coord& ijk = leaf.origin(); + PosType vec; + vec[0] = ScalarType(ijk[0]); + vec[1] = ScalarType(ijk[1]); + vec[2] = ScalarType(ijk[2]); + vec += ScalarType(LeafNodeType::DIM - 1) * 0.5; + vec -= mCenter; + + const ScalarType dist = vec.lengthSqr(); + if (dist > mLeafNodeDist1) return; + + if (mLeafNodeDist2 > 0.0 && dist < mLeafNodeDist2) { + const IndexT* begin = &leaf.indices().front(); + mRanges.push_back(Range(begin, begin + leaf.indices().size())); + return; + } + } + + typename LeafNodeType::ValueOnCIter iter; + const IndexT + *begin = static_cast(nullptr), + *end = static_cast(nullptr); + for (iter = leaf.cbeginValueOn(); iter; ++iter) { + leaf.getIndices(iter.pos(), begin, end); + filterVoxel(iter.getCoord(), begin, end); + } + } + + void filterVoxel(const Coord& ijk, const IndexT* begin, const IndexT* end) + { + PosType vec; + + { + vec[0] = mCenter[0] - ScalarType(ijk[0]); + vec[1] = mCenter[1] - ScalarType(ijk[1]); + vec[2] = mCenter[2] - ScalarType(ijk[2]); + + const ScalarType dist = vec.lengthSqr(); + if (dist > mVoxelDist1) return; + + if (!mSubvoxelAccuracy || (mVoxelDist2 > 0.0 && dist < mVoxelDist2)) { + if (!mRanges.empty() && mRanges.back().second == begin) { + mRanges.back().second = end; + } else { + mRanges.push_back(Range(begin, end)); + } + return; + } + } + + + while (begin < end) { + mPoints.getPos(*begin, vec); + vec = mWSCenter - vec; + + if (vec.lengthSqr() < mWSRadiusSqr) { + mIndices.push_back(*begin); + } + ++begin; + } + } + +private: + RangeDeque& mRanges; + IndexDeque& mIndices; + const PosType mCenter, mWSCenter; + ScalarType mVoxelDist1, mVoxelDist2, mLeafNodeDist1, mLeafNodeDist2, mWSRadiusSqr; + const PointArray& mPoints; + const bool mSubvoxelAccuracy; +}; // struct RadialRangeFilter + + +//////////////////////////////////////// + + +template +inline void +filteredPointIndexSearchVoxels(RangeFilterType& filter, + const LeafNodeType& leaf, const Coord& min, const Coord& max) +{ + using PointIndexT = typename LeafNodeType::ValueType; + Index xPos(0), yPos(0), pos(0); + Coord ijk(0); + + const PointIndexT* dataPtr = &leaf.indices().front(); + PointIndexT beginOffset, endOffset; + + for (ijk[0] = min[0]; ijk[0] <= max[0]; ++ijk[0]) { + xPos = (ijk[0] & (LeafNodeType::DIM - 1u)) << (2 * LeafNodeType::LOG2DIM); + for (ijk[1] = min[1]; ijk[1] <= max[1]; ++ijk[1]) { + yPos = xPos + ((ijk[1] & (LeafNodeType::DIM - 1u)) << LeafNodeType::LOG2DIM); + for (ijk[2] = min[2]; ijk[2] <= max[2]; ++ijk[2]) { + pos = yPos + (ijk[2] & (LeafNodeType::DIM - 1u)); + + beginOffset = (pos == 0 ? PointIndexT(0) : leaf.getValue(pos - 1)); + endOffset = leaf.getValue(pos); + + if (endOffset > beginOffset) { + filter.filterVoxel(ijk, dataPtr + beginOffset, dataPtr + endOffset); + } + } + } + } +} + + +template +inline void +filteredPointIndexSearch(RangeFilterType& filter, ConstAccessor& acc, const CoordBBox& bbox) +{ + using LeafNodeType = typename ConstAccessor::TreeType::LeafNodeType; + Coord ijk(0), ijkMax(0), ijkA(0), ijkB(0); + const Coord leafMin = bbox.min() & ~(LeafNodeType::DIM - 1); + const Coord leafMax = bbox.max() & ~(LeafNodeType::DIM - 1); + + for (ijk[0] = leafMin[0]; ijk[0] <= leafMax[0]; ijk[0] += LeafNodeType::DIM) { + for (ijk[1] = leafMin[1]; ijk[1] <= leafMax[1]; ijk[1] += LeafNodeType::DIM) { + for (ijk[2] = leafMin[2]; ijk[2] <= leafMax[2]; ijk[2] += LeafNodeType::DIM) { + + if (const LeafNodeType* leaf = acc.probeConstLeaf(ijk)) { + ijkMax = ijk; + ijkMax.offset(LeafNodeType::DIM - 1); + + // intersect leaf bbox with search region. + ijkA = Coord::maxComponent(bbox.min(), ijk); + ijkB = Coord::minComponent(bbox.max(), ijkMax); + + if (ijkA != ijk || ijkB != ijkMax) { + filteredPointIndexSearchVoxels(filter, *leaf, ijkA, ijkB); + } else { // leaf bbox is inside the search region + filter.filterLeafNode(*leaf); + } + } + } + } + } +} + + +//////////////////////////////////////// + + +template +inline void +pointIndexSearchVoxels(RangeDeque& rangeList, + const LeafNodeType& leaf, const Coord& min, const Coord& max) +{ + using PointIndexT = typename LeafNodeType::ValueType; + using IntT = typename PointIndexT::IntType; + using Range = typename RangeDeque::value_type; + + Index xPos(0), pos(0), zStride = Index(max[2] - min[2]); + const PointIndexT* dataPtr = &leaf.indices().front(); + PointIndexT beginOffset(0), endOffset(0), + previousOffset(static_cast(leaf.indices().size() + 1u)); + Coord ijk(0); + + for (ijk[0] = min[0]; ijk[0] <= max[0]; ++ijk[0]) { + xPos = (ijk[0] & (LeafNodeType::DIM - 1u)) << (2 * LeafNodeType::LOG2DIM); + + for (ijk[1] = min[1]; ijk[1] <= max[1]; ++ijk[1]) { + pos = xPos + ((ijk[1] & (LeafNodeType::DIM - 1u)) << LeafNodeType::LOG2DIM); + pos += (min[2] & (LeafNodeType::DIM - 1u)); + + beginOffset = (pos == 0 ? PointIndexT(0) : leaf.getValue(pos - 1)); + endOffset = leaf.getValue(pos+zStride); + + if (endOffset > beginOffset) { + + if (beginOffset == previousOffset) { + rangeList.back().second = dataPtr + endOffset; + } else { + rangeList.push_back(Range(dataPtr + beginOffset, dataPtr + endOffset)); + } + + previousOffset = endOffset; + } + } + } +} + + +template +inline void +pointIndexSearch(RangeDeque& rangeList, ConstAccessor& acc, const CoordBBox& bbox) +{ + using LeafNodeType = typename ConstAccessor::TreeType::LeafNodeType; + using PointIndexT = typename LeafNodeType::ValueType; + using Range = typename RangeDeque::value_type; + + Coord ijk(0), ijkMax(0), ijkA(0), ijkB(0); + const Coord leafMin = bbox.min() & ~(LeafNodeType::DIM - 1); + const Coord leafMax = bbox.max() & ~(LeafNodeType::DIM - 1); + + for (ijk[0] = leafMin[0]; ijk[0] <= leafMax[0]; ijk[0] += LeafNodeType::DIM) { + for (ijk[1] = leafMin[1]; ijk[1] <= leafMax[1]; ijk[1] += LeafNodeType::DIM) { + for (ijk[2] = leafMin[2]; ijk[2] <= leafMax[2]; ijk[2] += LeafNodeType::DIM) { + + if (const LeafNodeType* leaf = acc.probeConstLeaf(ijk)) { + ijkMax = ijk; + ijkMax.offset(LeafNodeType::DIM - 1); + + // intersect leaf bbox with search region. + ijkA = Coord::maxComponent(bbox.min(), ijk); + ijkB = Coord::minComponent(bbox.max(), ijkMax); + + if (ijkA != ijk || ijkB != ijkMax) { + pointIndexSearchVoxels(rangeList, *leaf, ijkA, ijkB); + } else { + // leaf bbox is inside the search region, add all indices. + const PointIndexT* begin = &leaf->indices().front(); + rangeList.push_back(Range(begin, (begin + leaf->indices().size()))); + } + } + } + } + } +} + + +} // namespace point_index_grid_internal + + +// PointIndexIterator implementation + +template +inline +PointIndexIterator::PointIndexIterator() + : mRange(static_cast(nullptr), static_cast(nullptr)) + , mRangeList() + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(0) +{ +} + + +template +inline +PointIndexIterator::PointIndexIterator(const PointIndexIterator& rhs) + : mRange(rhs.mRange) + , mRangeList(rhs.mRangeList) + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(rhs.mIndexArraySize) +{ + if (rhs.mIndexArray) { + mIndexArray.reset(new ValueType[mIndexArraySize]); + memcpy(mIndexArray.get(), rhs.mIndexArray.get(), mIndexArraySize * sizeof(ValueType)); + } +} + + +template +inline PointIndexIterator& +PointIndexIterator::operator=(const PointIndexIterator& rhs) +{ + if (&rhs != this) { + mRange = rhs.mRange; + mRangeList = rhs.mRangeList; + mIter = mRangeList.begin(); + mIndexArray.reset(); + mIndexArraySize = rhs.mIndexArraySize; + + if (rhs.mIndexArray) { + mIndexArray.reset(new ValueType[mIndexArraySize]); + memcpy(mIndexArray.get(), rhs.mIndexArray.get(), mIndexArraySize * sizeof(ValueType)); + } + } + return *this; +} + + +template +inline +PointIndexIterator::PointIndexIterator(const Coord& ijk, ConstAccessor& acc) + : mRange(static_cast(nullptr), static_cast(nullptr)) + , mRangeList() + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(0) +{ + const LeafNodeType* leaf = acc.probeConstLeaf(ijk); + if (leaf && leaf->getIndices(ijk, mRange.first, mRange.second)) { + mRangeList.push_back(mRange); + mIter = mRangeList.begin(); + } +} + + +template +inline +PointIndexIterator::PointIndexIterator(const CoordBBox& bbox, ConstAccessor& acc) + : mRange(static_cast(nullptr), static_cast(nullptr)) + , mRangeList() + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(0) +{ + point_index_grid_internal::pointIndexSearch(mRangeList, acc, bbox); + + if (!mRangeList.empty()) { + mIter = mRangeList.begin(); + mRange = mRangeList.front(); + } +} + + +template +inline void +PointIndexIterator::reset() +{ + mIter = mRangeList.begin(); + if (!mRangeList.empty()) { + mRange = mRangeList.front(); + } else if (mIndexArray) { + mRange.first = mIndexArray.get(); + mRange.second = mRange.first + mIndexArraySize; + } else { + mRange.first = static_cast(nullptr); + mRange.second = static_cast(nullptr); + } +} + + +template +inline void +PointIndexIterator::increment() +{ + ++mRange.first; + if (mRange.first >= mRange.second && mIter != mRangeList.end()) { + ++mIter; + if (mIter != mRangeList.end()) { + mRange = *mIter; + } else if (mIndexArray) { + mRange.first = mIndexArray.get(); + mRange.second = mRange.first + mIndexArraySize; + } + } +} + + +template +inline bool +PointIndexIterator::next() +{ + if (!this->test()) return false; + this->increment(); + return this->test(); +} + + +template +inline size_t +PointIndexIterator::size() const +{ + size_t count = 0; + typename RangeDeque::const_iterator it = mRangeList.begin(); + + for ( ; it != mRangeList.end(); ++it) { + count += it->second - it->first; + } + + return count + mIndexArraySize; +} + + +template +inline void +PointIndexIterator::clear() +{ + mRange.first = static_cast(nullptr); + mRange.second = static_cast(nullptr); + mRangeList.clear(); + mIter = mRangeList.end(); + mIndexArray.reset(); + mIndexArraySize = 0; +} + + +template +inline void +PointIndexIterator::searchAndUpdate(const Coord& ijk, ConstAccessor& acc) +{ + this->clear(); + const LeafNodeType* leaf = acc.probeConstLeaf(ijk); + if (leaf && leaf->getIndices(ijk, mRange.first, mRange.second)) { + mRangeList.push_back(mRange); + mIter = mRangeList.begin(); + } +} + + +template +inline void +PointIndexIterator::searchAndUpdate(const CoordBBox& bbox, ConstAccessor& acc) +{ + this->clear(); + point_index_grid_internal::pointIndexSearch(mRangeList, acc, bbox); + + if (!mRangeList.empty()) { + mIter = mRangeList.begin(); + mRange = mRangeList.front(); + } +} + + +template +template +inline void +PointIndexIterator::searchAndUpdate(const BBoxd& bbox, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform) +{ + this->clear(); + + std::vector searchRegions; + CoordBBox region(Coord::round(bbox.min()), Coord::round(bbox.max())); + + const Coord dim = region.dim(); + const int minExtent = std::min(dim[0], std::min(dim[1], dim[2])); + + if (minExtent > 2) { + // collect indices that don't need to be tested + CoordBBox ibox = region; + ibox.expand(-1); + + point_index_grid_internal::pointIndexSearch(mRangeList, acc, ibox); + + // define regions for the filtered search + ibox.expand(1); + point_index_grid_internal::constructExclusiveRegions(searchRegions, region, ibox); + } else { + searchRegions.push_back(region); + } + + // filtered search + std::deque filteredIndices; + point_index_grid_internal::BBoxFilter + filter(mRangeList, filteredIndices, bbox, points, xform); + + for (size_t n = 0, N = searchRegions.size(); n < N; ++n) { + point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[n]); + } + + point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); + + this->reset(); +} + + +template +template +inline void +PointIndexIterator::searchAndUpdate(const Vec3d& center, double radius, + ConstAccessor& acc, const PointArray& points, const math::Transform& xform, + bool subvoxelAccuracy) +{ + this->clear(); + std::vector searchRegions; + + // bounding box + CoordBBox bbox( + Coord::round(Vec3d(center[0] - radius, center[1] - radius, center[2] - radius)), + Coord::round(Vec3d(center[0] + radius, center[1] + radius, center[2] + radius))); + bbox.expand(1); + + const double iRadius = radius * double(1.0 / std::sqrt(3.0)); + if (iRadius > 2.0) { + // inscribed box + CoordBBox ibox( + Coord::round(Vec3d(center[0] - iRadius, center[1] - iRadius, center[2] - iRadius)), + Coord::round(Vec3d(center[0] + iRadius, center[1] + iRadius, center[2] + iRadius))); + ibox.expand(-1); + + // collect indices that don't need to be tested + point_index_grid_internal::pointIndexSearch(mRangeList, acc, ibox); + + ibox.expand(1); + point_index_grid_internal::constructExclusiveRegions(searchRegions, bbox, ibox); + } else { + searchRegions.push_back(bbox); + } + + // filtered search + std::deque filteredIndices; + const double leafNodeDim = double(TreeType::LeafNodeType::DIM); + + using FilterT = point_index_grid_internal::RadialRangeFilter; + + FilterT filter(mRangeList, filteredIndices, + center, radius, points, xform, leafNodeDim, subvoxelAccuracy); + + for (size_t n = 0, N = searchRegions.size(); n < N; ++n) { + point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[n]); + } + + point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); + + this->reset(); +} + + +template +template +inline void +PointIndexIterator::worldSpaceSearchAndUpdate(const BBoxd& bbox, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform) +{ + this->searchAndUpdate( + BBoxd(xform.worldToIndex(bbox.min()), xform.worldToIndex(bbox.max())), acc, points, xform); +} + + +template +template +inline void +PointIndexIterator::worldSpaceSearchAndUpdate(const Vec3d& center, double radius, + ConstAccessor& acc, const PointArray& points, const math::Transform& xform, + bool subvoxelAccuracy) +{ + this->searchAndUpdate(xform.worldToIndex(center), + (radius / xform.voxelSize()[0]), acc, points, xform, subvoxelAccuracy); +} + + +//////////////////////////////////////// + +// PointIndexFilter implementation + +template +inline +PointIndexFilter::PointIndexFilter( + const PointArray& points, const TreeType& tree, const math::Transform& xform) + : mPoints(&points), mAcc(tree), mXform(xform), mInvVoxelSize(1.0/xform.voxelSize()[0]) +{ +} + + +template +inline +PointIndexFilter::PointIndexFilter(const PointIndexFilter& rhs) + : mPoints(rhs.mPoints) + , mAcc(rhs.mAcc.tree()) + , mXform(rhs.mXform) + , mInvVoxelSize(rhs.mInvVoxelSize) +{ +} + + +template +template +inline void +PointIndexFilter::searchAndApply( + const PosType& center, ScalarType radius, FilterType& op) +{ + if (radius * mInvVoxelSize < ScalarType(8.0)) { + mIter.searchAndUpdate(openvdb::CoordBBox( + mXform.worldToIndexCellCentered(center - radius), + mXform.worldToIndexCellCentered(center + radius)), mAcc); + } else { + mIter.worldSpaceSearchAndUpdate( + center, radius, mAcc, *mPoints, mXform, /*subvoxelAccuracy=*/false); + } + + const ScalarType radiusSqr = radius * radius; + ScalarType distSqr = 0.0; + PosType pos; + for (; mIter; ++mIter) { + mPoints->getPos(*mIter, pos); + pos -= center; + distSqr = pos.lengthSqr(); + + if (distSqr < radiusSqr) { + op(distSqr, *mIter); + } + } +} + + +//////////////////////////////////////// + + +template +inline typename GridT::Ptr +createPointIndexGrid(const PointArrayT& points, const math::Transform& xform) +{ + typename GridT::Ptr grid = GridT::create(typename GridT::ValueType(0)); + grid->setTransform(xform.copy()); + + if (points.size() > 0) { + point_index_grid_internal::constructPointTree( + grid->tree(), grid->transform(), points); + } + + return grid; +} + + +template +inline typename GridT::Ptr +createPointIndexGrid(const PointArrayT& points, double voxelSize) +{ + math::Transform::Ptr xform = math::Transform::createLinearTransform(voxelSize); + return createPointIndexGrid(points, *xform); +} + + +template +inline bool +isValidPartition(const PointArrayT& points, const GridT& grid) +{ + tree::LeafManager leafs(grid.tree()); + + size_t pointCount = 0; + for (size_t n = 0, N = leafs.leafCount(); n < N; ++n) { + pointCount += leafs.leaf(n).indices().size(); + } + + if (points.size() != pointCount) { + return false; + } + + tbb::atomic changed; + changed = false; + + point_index_grid_internal::ValidPartitioningOp + op(changed, points, grid.transform()); + + leafs.foreach(op); + + return !bool(changed); +} + + +template +inline typename GridT::ConstPtr +getValidPointIndexGrid(const PointArrayT& points, const typename GridT::ConstPtr& grid) +{ + if (isValidPartition(points, *grid)) { + return grid; + } + + return createPointIndexGrid(points, grid->transform()); +} + + +template +inline typename GridT::Ptr +getValidPointIndexGrid(const PointArrayT& points, const typename GridT::Ptr& grid) +{ + if (isValidPartition(points, *grid)) { + return grid; + } + + return createPointIndexGrid(points, grid->transform()); +} + + +//////////////////////////////////////// + + +template +struct PointIndexLeafNode : public tree::LeafNode +{ + using LeafNodeType = PointIndexLeafNode; + using Ptr = SharedPtr; + + using ValueType = T; + using IndexArray = std::vector; + + + IndexArray& indices() { return mIndices; } + const IndexArray& indices() const { return mIndices; } + + bool getIndices(const Coord& ijk, const ValueType*& begin, const ValueType*& end) const; + bool getIndices(Index offset, const ValueType*& begin, const ValueType*& end) const; + + void setOffsetOn(Index offset, const ValueType& val); + void setOffsetOnly(Index offset, const ValueType& val); + + bool isEmpty(const CoordBBox& bbox) const; + +private: + IndexArray mIndices; + + //////////////////////////////////////// + + // The following methods had to be copied from the LeafNode class + // to make the derived PointIndexLeafNode class compatible with the tree structure. + +public: + using BaseLeaf = tree::LeafNode; + using NodeMaskType = util::NodeMask; + + using BaseLeaf::LOG2DIM; + using BaseLeaf::TOTAL; + using BaseLeaf::DIM; + using BaseLeaf::NUM_VALUES; + using BaseLeaf::NUM_VOXELS; + using BaseLeaf::SIZE; + using BaseLeaf::LEVEL; + + /// Default constructor + PointIndexLeafNode() : BaseLeaf(), mIndices() {} + + explicit + PointIndexLeafNode(const Coord& coords, const T& value = zeroVal(), bool active = false) + : BaseLeaf(coords, value, active) + , mIndices() + { + } + + PointIndexLeafNode(PartialCreate, const Coord& coords, + const T& value = zeroVal(), bool active = false) + : BaseLeaf(PartialCreate(), coords, value, active) + , mIndices() + { + } + + /// Deep copy constructor + PointIndexLeafNode(const PointIndexLeafNode& rhs) : BaseLeaf(rhs), mIndices(rhs.mIndices) {} + + /// @brief Return @c true if the given node (which may have a different @c ValueType + /// than this node) has the same active value topology as this node. + template + bool hasSameTopology(const PointIndexLeafNode* other) const { + return BaseLeaf::hasSameTopology(other); + } + + /// Check for buffer, state and origin equivalence. + bool operator==(const PointIndexLeafNode& other) const { return BaseLeaf::operator==(other); } + + bool operator!=(const PointIndexLeafNode& other) const { return !(other == *this); } + + template void merge(const PointIndexLeafNode& rhs) { + BaseLeaf::merge(rhs); + } + template void merge(const ValueType& tileValue, bool tileActive) { + BaseLeaf::template merge(tileValue, tileActive); + } + + template + void merge(const PointIndexLeafNode& other, + const ValueType& /*bg*/, const ValueType& /*otherBG*/) + { + BaseLeaf::template merge(other); + } + + void addLeaf(PointIndexLeafNode*) {} + template + void addLeafAndCache(PointIndexLeafNode*, AccessorT&) {} + + //@{ + /// @brief Return a pointer to this node. + PointIndexLeafNode* touchLeaf(const Coord&) { return this; } + template + PointIndexLeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + PointIndexLeafNode* probeLeaf(const Coord&) { return this; } + template + PointIndexLeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + //@} + + //@{ + /// @brief Return a @const pointer to this node. + const PointIndexLeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const PointIndexLeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const {return this;} + template + const PointIndexLeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const PointIndexLeafNode* probeLeaf(const Coord&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + + // I/O methods + + void readBuffers(std::istream& is, bool fromHalf = false); + void readBuffers(std::istream& is, const CoordBBox&, bool fromHalf = false); + void writeBuffers(std::ostream& os, bool toHalf = false) const; + + + Index64 memUsage() const; + + + //////////////////////////////////////// + + // Disable all write methods to avoid unintentional changes + // to the point-array offsets. + + void assertNonmodifiable() { + assert(false && "Cannot modify voxel values in a PointIndexTree."); + } + + void setActiveState(const Coord&, bool) { assertNonmodifiable(); } + void setActiveState(Index, bool) { assertNonmodifiable(); } + + void setValueOnly(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOnly(Index, const ValueType&) { assertNonmodifiable(); } + + void setValueOff(const Coord&) { assertNonmodifiable(); } + void setValueOff(Index) { assertNonmodifiable(); } + + void setValueOff(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOff(Index, const ValueType&) { assertNonmodifiable(); } + + void setValueOn(const Coord&) { assertNonmodifiable(); } + void setValueOn(Index) { assertNonmodifiable(); } + + void setValueOn(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOn(Index, const ValueType&) { assertNonmodifiable(); } + + void setValue(const Coord&, const ValueType&) { assertNonmodifiable(); } + + void setValuesOn() { assertNonmodifiable(); } + void setValuesOff() { assertNonmodifiable(); } + + template + void modifyValue(Index, const ModifyOp&) { assertNonmodifiable(); } + + template + void modifyValue(const Coord&, const ModifyOp&) { assertNonmodifiable(); } + + template + void modifyValueAndActiveState(const Coord&, const ModifyOp&) { assertNonmodifiable(); } + + void clip(const CoordBBox&, const ValueType&) { assertNonmodifiable(); } + + void fill(const CoordBBox&, const ValueType&, bool) { assertNonmodifiable(); } + void fill(const ValueType&) {} + void fill(const ValueType&, bool) { assertNonmodifiable(); } + + template + void setValueOnlyAndCache(const Coord&, const ValueType&, AccessorT&) {assertNonmodifiable();} + + template + void modifyValueAndActiveStateAndCache(const Coord&, const ModifyOp&, AccessorT&) { + assertNonmodifiable(); + } + + template + void setValueOffAndCache(const Coord&, const ValueType&, AccessorT&) { assertNonmodifiable(); } + + template + void setActiveStateAndCache(const Coord&, bool, AccessorT&) { assertNonmodifiable(); } + + void resetBackground(const ValueType&, const ValueType&) { assertNonmodifiable(); } + + void signedFloodFill(const ValueType&) { assertNonmodifiable(); } + void signedFloodFill(const ValueType&, const ValueType&) { assertNonmodifiable(); } + + void negate() { assertNonmodifiable(); } + +protected: + using ValueOn = typename BaseLeaf::ValueOn; + using ValueOff = typename BaseLeaf::ValueOff; + using ValueAll = typename BaseLeaf::ValueAll; + using ChildOn = typename BaseLeaf::ChildOn; + using ChildOff = typename BaseLeaf::ChildOff; + using ChildAll = typename BaseLeaf::ChildAll; + + using MaskOnIterator = typename NodeMaskType::OnIterator; + using MaskOffIterator = typename NodeMaskType::OffIterator; + using MaskDenseIterator = typename NodeMaskType::DenseIterator; + + // During topology-only construction, access is needed + // to protected/private members of other template instances. + template friend struct PointIndexLeafNode; + + friend class tree::IteratorBase; + friend class tree::IteratorBase; + friend class tree::IteratorBase; + +public: + using ValueOnIter = typename BaseLeaf::template ValueIter< + MaskOnIterator, PointIndexLeafNode, const ValueType, ValueOn>; + using ValueOnCIter = typename BaseLeaf::template ValueIter< + MaskOnIterator, const PointIndexLeafNode, const ValueType, ValueOn>; + using ValueOffIter = typename BaseLeaf::template ValueIter< + MaskOffIterator, PointIndexLeafNode, const ValueType, ValueOff>; + using ValueOffCIter = typename BaseLeaf::template ValueIter< + MaskOffIterator,const PointIndexLeafNode,const ValueType, ValueOff>; + using ValueAllIter = typename BaseLeaf::template ValueIter< + MaskDenseIterator, PointIndexLeafNode, const ValueType, ValueAll>; + using ValueAllCIter = typename BaseLeaf::template ValueIter< + MaskDenseIterator,const PointIndexLeafNode,const ValueType, ValueAll>; + using ChildOnIter = typename BaseLeaf::template ChildIter< + MaskOnIterator, PointIndexLeafNode, ChildOn>; + using ChildOnCIter = typename BaseLeaf::template ChildIter< + MaskOnIterator, const PointIndexLeafNode, ChildOn>; + using ChildOffIter = typename BaseLeaf::template ChildIter< + MaskOffIterator, PointIndexLeafNode, ChildOff>; + using ChildOffCIter = typename BaseLeaf::template ChildIter< + MaskOffIterator, const PointIndexLeafNode, ChildOff>; + using ChildAllIter = typename BaseLeaf::template DenseIter< + PointIndexLeafNode, ValueType, ChildAll>; + using ChildAllCIter = typename BaseLeaf::template DenseIter< + const PointIndexLeafNode, const ValueType, ChildAll>; + +#define VMASK_ this->getValueMask() + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(VMASK_.beginOn(), this); } + ValueOnCIter beginValueOn() const { return ValueOnCIter(VMASK_.beginOn(), this); } + ValueOnIter beginValueOn() { return ValueOnIter(VMASK_.beginOn(), this); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(VMASK_.beginOff(), this); } + ValueOffCIter beginValueOff() const { return ValueOffCIter(VMASK_.beginOff(), this); } + ValueOffIter beginValueOff() { return ValueOffIter(VMASK_.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(VMASK_.beginDense(), this); } + ValueAllCIter beginValueAll() const { return ValueAllCIter(VMASK_.beginDense(), this); } + ValueAllIter beginValueAll() { return ValueAllIter(VMASK_.beginDense(), this); } + + ValueOnCIter cendValueOn() const { return ValueOnCIter(VMASK_.endOn(), this); } + ValueOnCIter endValueOn() const { return ValueOnCIter(VMASK_.endOn(), this); } + ValueOnIter endValueOn() { return ValueOnIter(VMASK_.endOn(), this); } + ValueOffCIter cendValueOff() const { return ValueOffCIter(VMASK_.endOff(), this); } + ValueOffCIter endValueOff() const { return ValueOffCIter(VMASK_.endOff(), this); } + ValueOffIter endValueOff() { return ValueOffIter(VMASK_.endOff(), this); } + ValueAllCIter cendValueAll() const { return ValueAllCIter(VMASK_.endDense(), this); } + ValueAllCIter endValueAll() const { return ValueAllCIter(VMASK_.endDense(), this); } + ValueAllIter endValueAll() { return ValueAllIter(VMASK_.endDense(), this); } + + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnCIter beginChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnIter beginChildOn() { return ChildOnIter(VMASK_.endOn(), this); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffCIter beginChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffIter beginChildOff() { return ChildOffIter(VMASK_.endOff(), this); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(VMASK_.beginDense(), this); } + ChildAllCIter beginChildAll() const { return ChildAllCIter(VMASK_.beginDense(), this); } + ChildAllIter beginChildAll() { return ChildAllIter(VMASK_.beginDense(), this); } + + ChildOnCIter cendChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnCIter endChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnIter endChildOn() { return ChildOnIter(VMASK_.endOn(), this); } + ChildOffCIter cendChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffCIter endChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffIter endChildOff() { return ChildOffIter(VMASK_.endOff(), this); } + ChildAllCIter cendChildAll() const { return ChildAllCIter(VMASK_.endDense(), this); } + ChildAllCIter endChildAll() const { return ChildAllCIter(VMASK_.endDense(), this); } + ChildAllIter endChildAll() { return ChildAllIter(VMASK_.endDense(), this); } +#undef VMASK_ +}; // struct PointIndexLeafNode + + +template +inline bool +PointIndexLeafNode::getIndices(const Coord& ijk, + const ValueType*& begin, const ValueType*& end) const +{ + return getIndices(LeafNodeType::coordToOffset(ijk), begin, end); +} + + +template +inline bool +PointIndexLeafNode::getIndices(Index offset, + const ValueType*& begin, const ValueType*& end) const +{ + if (this->isValueMaskOn(offset)) { + const ValueType* dataPtr = &mIndices.front(); + begin = dataPtr + (offset == 0 ? ValueType(0) : this->buffer()[offset - 1]); + end = dataPtr + this->buffer()[offset]; + return true; + } + return false; +} + + +template +inline void +PointIndexLeafNode::setOffsetOn(Index offset, const ValueType& val) +{ + this->buffer().setValue(offset, val); + this->setValueMaskOn(offset); +} + + +template +inline void +PointIndexLeafNode::setOffsetOnly(Index offset, const ValueType& val) +{ + this->buffer().setValue(offset, val); +} + + +template +inline bool +PointIndexLeafNode::isEmpty(const CoordBBox& bbox) const +{ + Index xPos, pos, zStride = Index(bbox.max()[2] - bbox.min()[2]); + Coord ijk; + + for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) { + xPos = (ijk[0] & (DIM - 1u)) << (2 * LOG2DIM); + + for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) { + pos = xPos + ((ijk[1] & (DIM - 1u)) << LOG2DIM); + pos += (bbox.min()[2] & (DIM - 1u)); + + if (this->buffer()[pos+zStride] > (pos == 0 ? T(0) : this->buffer()[pos - 1])) { + return false; + } + } + } + + return true; +} + + +template +inline void +PointIndexLeafNode::readBuffers(std::istream& is, bool fromHalf) +{ + BaseLeaf::readBuffers(is, fromHalf); + + Index64 numIndices = Index64(0); + is.read(reinterpret_cast(&numIndices), sizeof(Index64)); + + mIndices.resize(size_t(numIndices)); + is.read(reinterpret_cast(mIndices.data()), numIndices * sizeof(T)); +} + + +template +inline void +PointIndexLeafNode::readBuffers(std::istream& is, const CoordBBox& bbox, bool fromHalf) +{ + // Read and clip voxel values. + BaseLeaf::readBuffers(is, bbox, fromHalf); + + Index64 numIndices = Index64(0); + is.read(reinterpret_cast(&numIndices), sizeof(Index64)); + + const Index64 numBytes = numIndices * sizeof(T); + + if (bbox.hasOverlap(this->getNodeBoundingBox())) { + mIndices.resize(size_t(numIndices)); + is.read(reinterpret_cast(mIndices.data()), numBytes); + + /// @todo If any voxels were deactivated as a result of clipping in the call to + /// BaseLeaf::readBuffers(), the point index list will need to be regenerated. + } else { + // Read and discard voxel values. + std::unique_ptr buf{new char[numBytes]}; + is.read(buf.get(), numBytes); + } + + // Reserved for future use + Index64 auxDataBytes = Index64(0); + is.read(reinterpret_cast(&auxDataBytes), sizeof(Index64)); + if (auxDataBytes > 0) { + // For now, read and discard any auxiliary data. + std::unique_ptr auxData{new char[auxDataBytes]}; + is.read(auxData.get(), auxDataBytes); + } +} + + +template +inline void +PointIndexLeafNode::writeBuffers(std::ostream& os, bool toHalf) const +{ + BaseLeaf::writeBuffers(os, toHalf); + + Index64 numIndices = Index64(mIndices.size()); + os.write(reinterpret_cast(&numIndices), sizeof(Index64)); + os.write(reinterpret_cast(mIndices.data()), numIndices * sizeof(T)); + + // Reserved for future use + const Index64 auxDataBytes = Index64(0); + os.write(reinterpret_cast(&auxDataBytes), sizeof(Index64)); +} + + +template +inline Index64 +PointIndexLeafNode::memUsage() const +{ + return BaseLeaf::memUsage() + Index64((sizeof(T)*mIndices.capacity()) + sizeof(mIndices)); +} + +} // namespace tools + + +//////////////////////////////////////// + + +namespace tree { + +/// Helper metafunction used to implement LeafNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameLeafConfig > +{ + static const bool value = true; +}; + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_POINT_INDEX_GRID_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/PointPartitioner.h b/openvdb/tools/PointPartitioner.h new file mode 100644 index 00000000..ee5eccee --- /dev/null +++ b/openvdb/tools/PointPartitioner.h @@ -0,0 +1,1049 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file PointPartitioner.h +/// +/// @brief Spatially partitions points using a parallel radix-based +/// sorting algorithm. +/// +/// @details Performs a stable deterministic sort; partitioning the same +/// point sequence will produce the same result each time. +/// @details The algorithm is unbounded meaning that points may be +/// distributed anywhere in index space. +/// @details The actual points are never stored in the tool, only +/// offsets into an external array. +/// +/// @author Mihai Alden + +#ifndef OPENVDB_TOOLS_POINT_PARTITIONER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_PARTITIONER_HAS_BEEN_INCLUDED + +#include +#include + +#include // boost::int_t::least +#include + +#include +#include +#include + +#include +#include // for std::isfinite() +#include +#include +#include +#include // std::pair +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +//////////////////////////////////////// + + +/// @brief Partitions points into @c BucketLog2Dim aligned buckets +/// using a parallel radix-based sorting algorithm. +/// +/// @interface PointArray +/// Expected interface for the PointArray container: +/// @code +/// template +/// struct PointArray +/// { +/// // The type used to represent world-space point positions +/// using PosType = VectorType; +/// +/// // Return the number of points in the array +/// size_t size() const; +/// +/// // Return the world-space position of the nth point in the array. +/// void getPos(size_t n, PosType& xyz) const; +/// }; +/// @endcode +/// +/// @details Performs a stable deterministic sort; partitioning the same +/// point sequence will produce the same result each time. +/// @details The algorithm is unbounded meaning that points may be +/// distributed anywhere in index space. +/// @details The actual points are never stored in the tool, only +/// offsets into an external array. +/// @details @c BucketLog2Dim defines the bucket coordinate dimensions, +/// i.e. BucketLog2Dim = 3 corresponds to a bucket that spans +/// a (2^3)^3 = 8^3 voxel region. +template +class PointPartitioner +{ +public: + enum { LOG2DIM = BucketLog2Dim }; + + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + using IndexType = PointIndexType; + using VoxelOffsetType = typename boost::int_t<1 + (3 * BucketLog2Dim)>::least; + using VoxelOffsetArray = boost::scoped_array; + + class IndexIterator; + + ////////// + + PointPartitioner(); + + /// @brief Partitions point indices into @c BucketLog2Dim aligned buckets. + /// + /// @param points list of world space points. + /// @param xform world to index space transform. + /// @param voxelOrder sort point indices by local voxel offsets. + /// @param recordVoxelOffsets construct local voxel offsets + /// @param cellCenteredTransform toggle the cell-centered interpretation that imagines world + /// space as divided into discrete cells (e.g., cubes) centered + /// on the image of the index-space lattice points. + template + void construct(const PointArray& points, const math::Transform& xform, + bool voxelOrder = false, bool recordVoxelOffsets = false, + bool cellCenteredTransform = true); + + + /// @brief Partitions point indices into @c BucketLog2Dim aligned buckets. + /// + /// @param points list of world space points. + /// @param xform world to index space transform. + /// @param voxelOrder sort point indices by local voxel offsets. + /// @param recordVoxelOffsets construct local voxel offsets + /// @param cellCenteredTransform toggle the cell-centered interpretation that imagines world + /// space as divided into discrete cells (e.g., cubes) centered + /// on the image of the index-space lattice points. + template + static Ptr create(const PointArray& points, const math::Transform& xform, + bool voxelOrder = false, bool recordVoxelOffsets = false, + bool cellCenteredTransform = true); + + + /// @brief Returns the number of buckets. + size_t size() const { return mPageCount; } + + /// @brief true if the container size is 0, false otherwise. + bool empty() const { return mPageCount == 0; } + + /// @brief Removes all data and frees up memory. + void clear(); + + /// @brief Exchanges the content of the container by another. + void swap(PointPartitioner&); + + /// @brief Returns the point indices for bucket @a n + IndexIterator indices(size_t n) const; + + /// @brief Returns the coordinate-aligned bounding box for bucket @a n + CoordBBox getBBox(size_t n) const { + return CoordBBox::createCube(mPageCoordinates[n], (1u << BucketLog2Dim)); + } + + /// @brief Returns the origin coordinate for bucket @a n + const Coord& origin(size_t n) const { return mPageCoordinates[n]; } + + /// @brief Returns a list of @c LeafNode voxel offsets for the points. + /// @note The list is optionally constructed. + const VoxelOffsetArray& voxelOffsets() const { return mVoxelOffsets; } + + /// @brief Returns @c true if this point partitioning was constructed + /// using a cell-centered transform. + /// @note Cell-centered interpretation is the default behavior. + bool usingCellCenteredTransform() const { return mUsingCellCenteredTransform; } + +private: + // Disallow copying + PointPartitioner(const PointPartitioner&); + PointPartitioner& operator=(const PointPartitioner&); + + boost::scoped_array mPointIndices; + VoxelOffsetArray mVoxelOffsets; + + boost::scoped_array mPageOffsets; + boost::scoped_array mPageCoordinates; + IndexType mPageCount; + bool mUsingCellCenteredTransform; +}; // class PointPartitioner + + +using UInt32PointPartitioner = PointPartitioner; + + +template +class PointPartitioner::IndexIterator +{ +public: + using IndexType = PointIndexType; + + IndexIterator(IndexType* begin = nullptr, IndexType* end = nullptr) + : mBegin(begin), mEnd(end), mItem(begin) {} + + /// @brief Rewind to first item. + void reset() { mItem = mBegin; } + + /// @brief Number of point indices in the iterator range. + size_t size() const { return mEnd - mBegin; } + + /// @brief Returns the item to which this iterator is currently pointing. + IndexType& operator*() { assert(mItem != nullptr); return *mItem; } + const IndexType& operator*() const { assert(mItem != nullptr); return *mItem; } + + /// @brief Return @c true if this iterator is not yet exhausted. + operator bool() const { return mItem < mEnd; } + bool test() const { return mItem < mEnd; } + + /// @brief Advance to the next item. + IndexIterator& operator++() { assert(this->test()); ++mItem; return *this; } + + /// @brief Advance to the next item. + bool next() { this->operator++(); return this->test(); } + bool increment() { this->next(); return this->test(); } + + /// @brief Equality operators + bool operator==(const IndexIterator& other) const { return mItem == other.mItem; } + bool operator!=(const IndexIterator& other) const { return !this->operator==(other); } + +private: + IndexType * const mBegin, * const mEnd; + IndexType * mItem; +}; // class PointPartitioner::IndexIterator + + +//////////////////////////////////////// +//////////////////////////////////////// + +// Implementation details + + +namespace point_partitioner_internal { + + +template +struct ComputePointOrderOp +{ + ComputePointOrderOp(PointIndexType* pointOrder, + const PointIndexType* bucketCounters, const PointIndexType* bucketOffsets) + : mPointOrder(pointOrder) + , mBucketCounters(bucketCounters) + , mBucketOffsets(bucketOffsets) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + mPointOrder[n] += mBucketCounters[mBucketOffsets[n]]; + } + } + + PointIndexType * const mPointOrder; + PointIndexType const * const mBucketCounters; + PointIndexType const * const mBucketOffsets; +}; // struct ComputePointOrderOp + + +template +struct CreateOrderedPointIndexArrayOp +{ + CreateOrderedPointIndexArrayOp(PointIndexType* orderedIndexArray, + const PointIndexType* pointOrder, const PointIndexType* indices) + : mOrderedIndexArray(orderedIndexArray) + , mPointOrder(pointOrder) + , mIndices(indices) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + mOrderedIndexArray[mPointOrder[n]] = mIndices[n]; + } + } + + PointIndexType * const mOrderedIndexArray; + PointIndexType const * const mPointOrder; + PointIndexType const * const mIndices; +}; // struct CreateOrderedPointIndexArrayOp + + +template +struct VoxelOrderOp +{ + using VoxelOffsetType = typename boost::int_t<1 + (3 * BucketLog2Dim)>::least; + using VoxelOffsetArray = boost::scoped_array; + using IndexArray = boost::scoped_array; + + VoxelOrderOp(IndexArray& indices, const IndexArray& pages,const VoxelOffsetArray& offsets) + : mIndices(indices.get()) + , mPages(pages.get()) + , mVoxelOffsets(offsets.get()) + { + } + + void operator()(const tbb::blocked_range& range) const { + + PointIndexType pointCount = 0; + for (size_t n(range.begin()), N(range.end()); n != N; ++n) { + pointCount = std::max(pointCount, (mPages[n + 1] - mPages[n])); + } + + const PointIndexType voxelCount = 1 << (3 * BucketLog2Dim); + + // allocate histogram buffers + boost::scoped_array offsets(new VoxelOffsetType[pointCount]); + boost::scoped_array sortedIndices(new PointIndexType[pointCount]); + boost::scoped_array histogram(new PointIndexType[voxelCount]); + + for (size_t n(range.begin()), N(range.end()); n != N; ++n) { + + PointIndexType * const indices = mIndices + mPages[n]; + pointCount = mPages[n + 1] - mPages[n]; + + // local copy of voxel offsets. + for (PointIndexType i = 0; i < pointCount; ++i) { + offsets[i] = mVoxelOffsets[ indices[i] ]; + } + + // reset histogram + memset(&histogram[0], 0, voxelCount * sizeof(PointIndexType)); + + // compute histogram + for (PointIndexType i = 0; i < pointCount; ++i) { + ++histogram[ offsets[i] ]; + } + + PointIndexType count = 0, startOffset; + for (int i = 0; i < int(voxelCount); ++i) { + if (histogram[i] > 0) { + startOffset = count; + count += histogram[i]; + histogram[i] = startOffset; + } + } + + // sort indices based on voxel offset + for (PointIndexType i = 0; i < pointCount; ++i) { + sortedIndices[ histogram[ offsets[i] ]++ ] = indices[i]; + } + + memcpy(&indices[0], &sortedIndices[0], sizeof(PointIndexType) * pointCount); + } + } + + PointIndexType * const mIndices; + PointIndexType const * const mPages; + VoxelOffsetType const * const mVoxelOffsets; +}; // struct VoxelOrderOp + + +template +struct LeafNodeOriginOp +{ + using IndexArray = boost::scoped_array; + using CoordArray = boost::scoped_array; + + LeafNodeOriginOp(CoordArray& coordinates, + const IndexArray& indices, const IndexArray& pages, + const PointArray& points, const math::Transform& m, int log2dim, bool cellCenteredTransform) + : mCoordinates(coordinates.get()) + , mIndices(indices.get()) + , mPages(pages.get()) + , mPoints(&points) + , mXForm(m) + , mLog2Dim(log2dim) + , mCellCenteredTransform(cellCenteredTransform) + { + } + + void operator()(const tbb::blocked_range& range) const { + + using PosType = typename PointArray::PosType; + + const bool cellCentered = mCellCenteredTransform; + const int mask = ~((1 << mLog2Dim) - 1); + Coord ijk; + PosType pos; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + mPoints->getPos(mIndices[mPages[n]], pos); + + if (std::isfinite(pos[0]) && std::isfinite(pos[1]) && std::isfinite(pos[2])) { + ijk = cellCentered ? mXForm.worldToIndexCellCentered(pos) : + mXForm.worldToIndexNodeCentered(pos); + + ijk[0] &= mask; + ijk[1] &= mask; + ijk[2] &= mask; + + mCoordinates[n] = ijk; + } + } + } + + Coord * const mCoordinates; + PointIndexType const * const mIndices; + PointIndexType const * const mPages; + PointArray const * const mPoints; + math::Transform const mXForm; + int const mLog2Dim; + bool const mCellCenteredTransform; +}; // struct LeafNodeOriginOp + + +//////////////////////////////////////// + + +template +struct Array +{ + using Ptr = SharedPtr; + + Array(size_t size) : mSize(size), mData(new T[size]) { } + + size_t size() const { return mSize; } + + T* data() { return mData.get(); } + const T* data() const { return mData.get(); } + + void clear() { mSize = 0; mData.reset(); } + +private: + size_t mSize; + boost::scoped_array mData; +}; // struct Array + + +template +struct MoveSegmentDataOp +{ + using Segment = Array; + using SegmentPtr = typename Segment::Ptr; + + MoveSegmentDataOp(std::vector& indexLists, SegmentPtr* segments) + : mIndexLists(&indexLists[0]), mSegments(segments) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n(range.begin()), N(range.end()); n != N; ++n) { + PointIndexType* indices = mIndexLists[n]; + SegmentPtr& segment = mSegments[n]; + + tbb::parallel_for(tbb::blocked_range(0, segment->size()), + CopyData(indices, segment->data())); + + segment.reset(); // clear data + } + } + +private: + + struct CopyData + { + CopyData(PointIndexType* lhs, const PointIndexType* rhs) : mLhs(lhs), mRhs(rhs) { } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + mLhs[n] = mRhs[n]; + } + } + + PointIndexType * const mLhs; + PointIndexType const * const mRhs; + }; + + PointIndexType * const * const mIndexLists; + SegmentPtr * const mSegments; +}; // struct MoveSegmentDataOp + + +template +struct MergeBinsOp +{ + using Segment = Array; + using SegmentPtr = typename Segment::Ptr; + + using IndexPair = std::pair; + using IndexPairList = std::deque; + using IndexPairListPtr = SharedPtr; + using IndexPairListMap = std::map; + using IndexPairListMapPtr = SharedPtr; + + MergeBinsOp(IndexPairListMapPtr* bins, + SegmentPtr* indexSegments, + SegmentPtr* offsetSegments, + Coord* coords, + size_t numSegments) + : mBins(bins) + , mIndexSegments(indexSegments) + , mOffsetSegments(offsetSegments) + , mCoords(coords) + , mNumSegments(numSegments) + { + } + + void operator()(const tbb::blocked_range& range) const { + + std::vector data; + std::vector arrayOffsets; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + const Coord& ijk = mCoords[n]; + size_t numIndices = 0; + + data.clear(); + + for (size_t i = 0, I = mNumSegments; i < I; ++i) { + + IndexPairListMap& idxMap = *mBins[i]; + typename IndexPairListMap::iterator iter = idxMap.find(ijk); + + if (iter != idxMap.end() && iter->second) { + IndexPairListPtr& idxListPtr = iter->second; + + data.push_back(&idxListPtr); + numIndices += idxListPtr->size(); + } + } + + if (data.empty() || numIndices == 0) continue; + + SegmentPtr& indexSegment = mIndexSegments[n]; + SegmentPtr& offsetSegment = mOffsetSegments[n]; + + indexSegment.reset(new Segment(numIndices)); + offsetSegment.reset(new Segment(numIndices)); + + arrayOffsets.clear(); + arrayOffsets.reserve(data.size()); + + for (size_t i = 0, count = 0, I = data.size(); i < I; ++i) { + arrayOffsets.push_back(PointIndexType(count)); + count += (*data[i])->size(); + } + + tbb::parallel_for(tbb::blocked_range(0, data.size()), + CopyData(&data[0], &arrayOffsets[0], indexSegment->data(), offsetSegment->data())); + } + } + +private: + + struct CopyData + { + CopyData(IndexPairListPtr** indexLists, + const PointIndexType* arrayOffsets, + PointIndexType* indices, + PointIndexType* offsets) + : mIndexLists(indexLists) + , mArrayOffsets(arrayOffsets) + , mIndices(indices) + , mOffsets(offsets) + { + } + + void operator()(const tbb::blocked_range& range) const { + + using CIter = typename IndexPairList::const_iterator; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + const PointIndexType arrayOffset = mArrayOffsets[n]; + PointIndexType* indexPtr = &mIndices[arrayOffset]; + PointIndexType* offsetPtr = &mOffsets[arrayOffset]; + + IndexPairListPtr& list = *mIndexLists[n]; + + for (CIter it = list->begin(), end = list->end(); it != end; ++it) { + const IndexPair& data = *it; + *indexPtr++ = data.first; + *offsetPtr++ = data.second; + } + + list.reset(); // clear data + } + } + + IndexPairListPtr * const * const mIndexLists; + PointIndexType const * const mArrayOffsets; + PointIndexType * const mIndices; + PointIndexType * const mOffsets; + }; // struct CopyData + + IndexPairListMapPtr * const mBins; + SegmentPtr * const mIndexSegments; + SegmentPtr * const mOffsetSegments; + Coord const * const mCoords; + size_t const mNumSegments; +}; // struct MergeBinsOp + + +template +struct BinPointIndicesOp +{ + using PosType = typename PointArray::PosType; + using IndexPair = std::pair; + using IndexPairList = std::deque; + using IndexPairListPtr = SharedPtr; + using IndexPairListMap = std::map; + using IndexPairListMapPtr = SharedPtr; + + BinPointIndicesOp(IndexPairListMapPtr* data, + const PointArray& points, + VoxelOffsetType* voxelOffsets, + const math::Transform& m, + Index binLog2Dim, + Index bucketLog2Dim, + size_t numSegments, + bool cellCenteredTransform) + : mData(data) + , mPoints(&points) + , mVoxelOffsets(voxelOffsets) + , mXForm(m) + , mBinLog2Dim(binLog2Dim) + , mBucketLog2Dim(bucketLog2Dim) + , mNumSegments(numSegments) + , mCellCenteredTransform(cellCenteredTransform) + { + } + + void operator()(const tbb::blocked_range& range) const { + + const Index log2dim = mBucketLog2Dim; + const Index log2dim2 = 2 * log2dim; + const Index bucketMask = (1u << log2dim) - 1u; + + const Index binLog2dim = mBinLog2Dim; + const Index binLog2dim2 = 2 * binLog2dim; + + const Index binMask = (1u << (log2dim + binLog2dim)) - 1u; + const Index invBinMask = ~binMask; + + IndexPairList * idxList = nullptr; + Coord ijk(0, 0, 0), loc(0, 0, 0), binCoord(0, 0, 0), lastBinCoord(1, 2, 3); + PosType pos; + + PointIndexType bucketOffset = 0; + VoxelOffsetType voxelOffset = 0; + + const bool cellCentered = mCellCenteredTransform; + + const size_t numPoints = mPoints->size(); + const size_t segmentSize = numPoints / mNumSegments; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + IndexPairListMapPtr& dataPtr = mData[n]; + if (!dataPtr) dataPtr.reset(new IndexPairListMap()); + IndexPairListMap& idxMap = *dataPtr; + + const bool isLastSegment = (n + 1) >= mNumSegments; + + const size_t start = n * segmentSize; + const size_t end = isLastSegment ? numPoints : (start + segmentSize); + + for (size_t i = start; i != end; ++i) { + + mPoints->getPos(i, pos); + + if (std::isfinite(pos[0]) && std::isfinite(pos[1]) && std::isfinite(pos[2])) { + ijk = cellCentered ? mXForm.worldToIndexCellCentered(pos) : + mXForm.worldToIndexNodeCentered(pos); + + if (mVoxelOffsets) { + loc[0] = ijk[0] & bucketMask; + loc[1] = ijk[1] & bucketMask; + loc[2] = ijk[2] & bucketMask; + voxelOffset = VoxelOffsetType( + (loc[0] << log2dim2) + (loc[1] << log2dim) + loc[2]); + } + + binCoord[0] = ijk[0] & invBinMask; + binCoord[1] = ijk[1] & invBinMask; + binCoord[2] = ijk[2] & invBinMask; + + ijk[0] &= binMask; + ijk[1] &= binMask; + ijk[2] &= binMask; + + ijk[0] >>= log2dim; + ijk[1] >>= log2dim; + ijk[2] >>= log2dim; + + bucketOffset = PointIndexType( + (ijk[0] << binLog2dim2) + (ijk[1] << binLog2dim) + ijk[2]); + + if (lastBinCoord != binCoord) { + lastBinCoord = binCoord; + IndexPairListPtr& idxListPtr = idxMap[lastBinCoord]; + if (!idxListPtr) idxListPtr.reset(new IndexPairList()); + idxList = idxListPtr.get(); + } + + idxList->push_back(IndexPair(PointIndexType(i), bucketOffset)); + if (mVoxelOffsets) mVoxelOffsets[i] = voxelOffset; + } + } + } + } + + IndexPairListMapPtr * const mData; + PointArray const * const mPoints; + VoxelOffsetType * const mVoxelOffsets; + math::Transform const mXForm; + Index const mBinLog2Dim; + Index const mBucketLog2Dim; + size_t const mNumSegments; + bool const mCellCenteredTransform; +}; // struct BinPointIndicesOp + + +template +struct OrderSegmentsOp +{ + using IndexArray = boost::scoped_array; + using SegmentPtr = typename Array::Ptr; + + OrderSegmentsOp(SegmentPtr* indexSegments, SegmentPtr* offestSegments, + IndexArray* pageOffsetArrays, Index binVolume) + : mIndexSegments(indexSegments) + , mOffsetSegments(offestSegments) + , mPageOffsetArrays(pageOffsetArrays) + , mBinVolume(binVolume) + { + } + + void operator()(const tbb::blocked_range& range) const { + + const size_t bucketCountersSize = size_t(mBinVolume); + IndexArray bucketCounters(new PointIndexType[bucketCountersSize]); + + size_t maxSegmentSize = 0; + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + maxSegmentSize = std::max(maxSegmentSize, mIndexSegments[n]->size()); + } + + IndexArray bucketIndices(new PointIndexType[maxSegmentSize]); + + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + memset(bucketCounters.get(), 0, sizeof(PointIndexType) * bucketCountersSize); + + const size_t segmentSize = mOffsetSegments[n]->size(); + PointIndexType* offsets = mOffsetSegments[n]->data(); + + // Count the number of points per bucket and assign a local bucket index + // to each point. + for (size_t i = 0; i < segmentSize; ++i) { + bucketIndices[i] = bucketCounters[offsets[i]]++; + } + + PointIndexType nonemptyBucketCount = 0; + for (size_t i = 0; i < bucketCountersSize; ++i) { + nonemptyBucketCount += static_cast(bucketCounters[i] != 0); + } + + + IndexArray& pageOffsets = mPageOffsetArrays[n]; + pageOffsets.reset(new PointIndexType[nonemptyBucketCount + 1]); + pageOffsets[0] = nonemptyBucketCount + 1; // stores array size in first element + + // Compute bucket counter prefix sum + PointIndexType count = 0, idx = 1; + for (size_t i = 0; i < bucketCountersSize; ++i) { + if (bucketCounters[i] != 0) { + pageOffsets[idx] = bucketCounters[i]; + bucketCounters[i] = count; + count += pageOffsets[idx]; + ++idx; + } + } + + PointIndexType* indices = mIndexSegments[n]->data(); + const tbb::blocked_range segmentRange(0, segmentSize); + + // Compute final point order by incrementing the local bucket point index + // with the prefix sum offset. + tbb::parallel_for(segmentRange, ComputePointOrderOp( + bucketIndices.get(), bucketCounters.get(), offsets)); + + tbb::parallel_for(segmentRange, CreateOrderedPointIndexArrayOp( + offsets, bucketIndices.get(), indices)); + + mIndexSegments[n]->clear(); // clear data + } + } + + SegmentPtr * const mIndexSegments; + SegmentPtr * const mOffsetSegments; + IndexArray * const mPageOffsetArrays; + Index const mBinVolume; +}; // struct OrderSegmentsOp + + +//////////////////////////////////////// + + +/// @brief Segment points using one level of least significant digit radix bins. +template +inline void binAndSegment( + const PointArray& points, + const math::Transform& xform, + boost::scoped_array::Ptr>& indexSegments, + boost::scoped_array::Ptr>& offsetSegments, + size_t& segmentCount, + const Index binLog2Dim, + const Index bucketLog2Dim, + VoxelOffsetType* voxelOffsets = nullptr, + bool cellCenteredTransform = true) +{ + using IndexPair = std::pair; + using IndexPairList = std::deque; + using IndexPairListPtr = SharedPtr; + using IndexPairListMap = std::map; + using IndexPairListMapPtr = SharedPtr; + + size_t numTasks = 1, numThreads = size_t(tbb::task_scheduler_init::default_num_threads()); + if (points.size() > (numThreads * 2)) numTasks = numThreads * 2; + else if (points.size() > numThreads) numTasks = numThreads; + + boost::scoped_array bins(new IndexPairListMapPtr[numTasks]); + + using BinOp = BinPointIndicesOp; + + tbb::parallel_for(tbb::blocked_range(0, numTasks), + BinOp(bins.get(), points, voxelOffsets, xform, binLog2Dim, bucketLog2Dim, + numTasks, cellCenteredTransform)); + + std::set uniqueCoords; + + for (size_t i = 0; i < numTasks; ++i) { + IndexPairListMap& idxMap = *bins[i]; + for (typename IndexPairListMap::iterator it = idxMap.begin(); it != idxMap.end(); ++it) { + uniqueCoords.insert(it->first); + } + } + + std::vector coords(uniqueCoords.begin(), uniqueCoords.end()); + uniqueCoords.clear(); + + segmentCount = coords.size(); + + using SegmentPtr = typename Array::Ptr; + + indexSegments.reset(new SegmentPtr[segmentCount]); + offsetSegments.reset(new SegmentPtr[segmentCount]); + + using MergeOp = MergeBinsOp; + + tbb::parallel_for(tbb::blocked_range(0, segmentCount), + MergeOp(bins.get(), indexSegments.get(), offsetSegments.get(), &coords[0], numTasks)); +} + + +template +inline void partition( + const PointArray& points, + const math::Transform& xform, + const Index bucketLog2Dim, + boost::scoped_array& pointIndices, + boost::scoped_array& pageOffsets, + PointIndexType& pageCount, + boost::scoped_array& voxelOffsets, + bool recordVoxelOffsets, + bool cellCenteredTransform) +{ + if (recordVoxelOffsets) voxelOffsets.reset(new VoxelOffsetType[points.size()]); + else voxelOffsets.reset(); + + const Index binLog2Dim = 5u; + // note: Bins span a (2^(binLog2Dim + bucketLog2Dim))^3 voxel region, + // i.e. bucketLog2Dim = 3 and binLog2Dim = 5 corresponds to a + // (2^8)^3 = 256^3 voxel region. + + + size_t numSegments = 0; + + boost::scoped_array::Ptr> indexSegments; + boost::scoped_array::Ptr> offestSegments; + + binAndSegment(points, xform, + indexSegments, offestSegments, numSegments, binLog2Dim, bucketLog2Dim, + voxelOffsets.get(), cellCenteredTransform); + + const tbb::blocked_range segmentRange(0, numSegments); + + using IndexArray = boost::scoped_array; + boost::scoped_array pageOffsetArrays(new IndexArray[numSegments]); + + const Index binVolume = 1u << (3u * binLog2Dim); + + tbb::parallel_for(segmentRange, OrderSegmentsOp + (indexSegments.get(), offestSegments.get(), pageOffsetArrays.get(), binVolume)); + + indexSegments.reset(); + + pageCount = 0; + for (size_t n = 0; n < numSegments; ++n) { + pageCount += pageOffsetArrays[n][0] - 1; + } + + pageOffsets.reset(new PointIndexType[pageCount + 1]); + + PointIndexType count = 0; + for (size_t n = 0, idx = 0; n < numSegments; ++n) { + + PointIndexType* offsets = pageOffsetArrays[n].get(); + size_t size = size_t(offsets[0]); + + for (size_t i = 1; i < size; ++i) { + pageOffsets[idx++] = count; + count += offsets[i]; + } + } + + pageOffsets[pageCount] = count; + + pointIndices.reset(new PointIndexType[points.size()]); + + std::vector indexArray; + indexArray.reserve(numSegments); + + PointIndexType* index = pointIndices.get(); + for (size_t n = 0; n < numSegments; ++n) { + indexArray.push_back(index); + index += offestSegments[n]->size(); + } + + tbb::parallel_for(segmentRange, + MoveSegmentDataOp(indexArray, offestSegments.get())); +} + + +} // namespace point_partitioner_internal + + +//////////////////////////////////////// + + +template +inline PointPartitioner::PointPartitioner() + : mPointIndices(nullptr) + , mVoxelOffsets(nullptr) + , mPageOffsets(nullptr) + , mPageCoordinates(nullptr) + , mPageCount(0) + , mUsingCellCenteredTransform(true) +{ +} + + +template +inline void +PointPartitioner::clear() +{ + mPageCount = 0; + mUsingCellCenteredTransform = true; + mPointIndices.reset(); + mVoxelOffsets.reset(); + mPageOffsets.reset(); + mPageCoordinates.reset(); +} + + +template +inline void +PointPartitioner::swap(PointPartitioner& rhs) +{ + const IndexType tmpLhsPageCount = mPageCount; + mPageCount = rhs.mPageCount; + rhs.mPageCount = tmpLhsPageCount; + + mPointIndices.swap(rhs.mPointIndices); + mVoxelOffsets.swap(rhs.mVoxelOffsets); + mPageOffsets.swap(rhs.mPageOffsets); + mPageCoordinates.swap(rhs.mPageCoordinates); + + bool lhsCellCenteredTransform = mUsingCellCenteredTransform; + mUsingCellCenteredTransform = rhs.mUsingCellCenteredTransform; + rhs.mUsingCellCenteredTransform = lhsCellCenteredTransform; +} + + +template +inline typename PointPartitioner::IndexIterator +PointPartitioner::indices(size_t n) const +{ + assert(bool(mPointIndices) && bool(mPageCount)); + return IndexIterator( + mPointIndices.get() + mPageOffsets[n], + mPointIndices.get() + mPageOffsets[n + 1]); +} + + +template +template +inline void +PointPartitioner::construct( + const PointArray& points, + const math::Transform& xform, + bool voxelOrder, + bool recordVoxelOffsets, + bool cellCenteredTransform) +{ + mUsingCellCenteredTransform = cellCenteredTransform; + + point_partitioner_internal::partition(points, xform, BucketLog2Dim, + mPointIndices, mPageOffsets, mPageCount, mVoxelOffsets, + (voxelOrder || recordVoxelOffsets), cellCenteredTransform); + + const tbb::blocked_range pageRange(0, mPageCount); + mPageCoordinates.reset(new Coord[mPageCount]); + + tbb::parallel_for(pageRange, + point_partitioner_internal::LeafNodeOriginOp + (mPageCoordinates, mPointIndices, mPageOffsets, points, xform, + BucketLog2Dim, cellCenteredTransform)); + + if (mVoxelOffsets && voxelOrder) { + tbb::parallel_for(pageRange, point_partitioner_internal::VoxelOrderOp< + IndexType, BucketLog2Dim>(mPointIndices, mPageOffsets, mVoxelOffsets)); + } + + if (mVoxelOffsets && !recordVoxelOffsets) { + mVoxelOffsets.reset(); + } +} + + +template +template +inline typename PointPartitioner::Ptr +PointPartitioner::create( + const PointArray& points, + const math::Transform& xform, + bool voxelOrder, + bool recordVoxelOffsets, + bool cellCenteredTransform) +{ + Ptr ret(new PointPartitioner()); + ret->construct(points, xform, voxelOrder, recordVoxelOffsets, cellCenteredTransform); + return ret; +} + + +//////////////////////////////////////// + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_POINT_PARTITIONER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/PointScatter.h b/openvdb/tools/PointScatter.h new file mode 100644 index 00000000..7ad03433 --- /dev/null +++ b/openvdb/tools/PointScatter.h @@ -0,0 +1,421 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @author Ken Museth +/// +/// @file tools/PointScatter.h +/// +/// @brief We offer three different algorithms (each in its own class) +/// for scattering of points in active voxels: +/// +/// 1) UniformPointScatter. Has two modes: Either randomly distributes +/// a fixed number of points into the active voxels, or the user can +/// specify a fixed probability of having a points per unit of volume. +/// +/// 2) DenseUniformPointScatter. Randomly distributes points into active +/// voxels using a fixed number of points per voxel. +/// +/// 3) NonIniformPointScatter. Define the local probability of having +/// a point in a voxel as the product of a global density and the +/// value of the voxel itself. + +#ifndef OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// Forward declaration of base class +template +class BasePointScatter; + +/// @brief The two point scatters UniformPointScatter and +/// NonUniformPointScatter depend on the following two classes: +/// +/// The @c PointAccessorType template argument below refers to any class +/// with the following interface: +/// @code +/// class PointAccessor { +/// ... +/// public: +/// void add(const openvdb::Vec3R &pos);// appends point with world positions pos +/// }; +/// @endcode +/// +/// +/// The @c InterruptType template argument below refers to any class +/// with the following interface: +/// @code +/// class Interrupter { +/// ... +/// public: +/// void start(const char* name = nullptr) // called when computations begin +/// void end() // called when computations end +/// bool wasInterrupted(int percent=-1) // return true to break computation +///}; +/// @endcode +/// +/// @note If no template argument is provided for this InterruptType +/// the util::NullInterrupter is used which implies that all +/// interrupter calls are no-ops (i.e. incurs no computational overhead). + + +/// @brief Uniformly scatters points in the active voxels. +/// The point count is either explicitly defined or implicitly +/// through the specification of a global density (=points-per-volume) +/// +/// @note This uniform scattering technique assumes that the number of +/// points is generally smaller than the number of active voxels +/// (including virtual active voxels in active tiles). +template +class UniformPointScatter : public BasePointScatter +{ +public: + using BaseT = BasePointScatter; + + UniformPointScatter(PointAccessorType& points, + Index64 pointCount, + RandomGenerator& randGen, + double spread = 1.0, + InterruptType* interrupt = nullptr) + : BaseT(points, randGen, spread, interrupt) + , mTargetPointCount(pointCount) + , mPointsPerVolume(0.0f) + { + } + UniformPointScatter(PointAccessorType& points, + float pointsPerVolume, + RandomGenerator& randGen, + double spread = 1.0, + InterruptType* interrupt = nullptr) + : BaseT(points, randGen, spread, interrupt) + , mTargetPointCount(0) + , mPointsPerVolume(pointsPerVolume) + { + } + + /// This is the main functor method implementing the actual scattering of points. + template + bool operator()(const GridT& grid) + { + mVoxelCount = grid.activeVoxelCount(); + if (mVoxelCount == 0) return false; + + const auto voxelVolume = grid.transform().voxelVolume(); + if (mPointsPerVolume > 0) { + BaseT::start("Uniform scattering with fixed point density"); + mTargetPointCount = Index64(mPointsPerVolume * voxelVolume * double(mVoxelCount)); + } else if (mTargetPointCount > 0) { + BaseT::start("Uniform scattering with fixed point count"); + mPointsPerVolume = float(mTargetPointCount) / float(voxelVolume * double(mVoxelCount)); + } else { + return false; + } + + std::unique_ptr idList{new Index64[mTargetPointCount]}; + math::RandInt rand(BaseT::mRand01.engine(), 0, mVoxelCount-1); + for (Index64 i=0; i +class DenseUniformPointScatter : public BasePointScatter +{ +public: + using BaseT = BasePointScatter; + + DenseUniformPointScatter(PointAccessorType& points, + float pointsPerVoxel, + RandomGenerator& randGen, + double spread = 1.0, + InterruptType* interrupt = nullptr) + : BaseT(points, randGen, spread, interrupt) + , mPointsPerVoxel(pointsPerVoxel) + { + } + + /// This is the main functor method implementing the actual scattering of points. + template + bool operator()(const GridT& grid) + { + using ValueIter = typename GridT::ValueOnCIter; + if (mPointsPerVoxel < 1.0e-6) return false; + mVoxelCount = grid.activeVoxelCount(); + if (mVoxelCount == 0) return false; + BaseT::start("Dense uniform scattering with fixed point count"); + CoordBBox bbox; + const Vec3R offset(0.5, 0.5, 0.5); + + const int ppv = math::Floor(mPointsPerVoxel); + const double delta = mPointsPerVoxel - float(ppv); + const bool fractional = !math::isApproxZero(delta, 1.0e-6); + + for (ValueIter iter = grid.cbeginValueOn(); iter; ++iter) { + if (BaseT::interrupt()) return false; + if (iter.isVoxelValue()) {// a majority is expected to be voxels + const Vec3R dmin = iter.getCoord() - offset; + for (int n = 0; n != ppv; ++n) BaseT::addPoint(grid, dmin); + if (fractional && BaseT::getRand01() < delta) BaseT::addPoint(grid, dmin); + } else {// tiles contain multiple (virtual) voxels + iter.getBoundingBox(bbox); + const Coord size(bbox.extents()); + const Vec3R dmin = bbox.min() - offset; + const double d = mPointsPerVoxel * float(iter.getVoxelCount()); + const int m = math::Floor(d); + for (int n = 0; n != m; ++n) BaseT::addPoint(grid, dmin, size); + if (BaseT::getRand01() < d - m) BaseT::addPoint(grid, dmin, size); + } + }//loop over all the active voxels and tiles + //} + BaseT::end(); + return true; + } + + // The following methods should only be called after the + // the operator() method was called + void print(const std::string &name, std::ostream& os = std::cout) const + { + os << "Dense uniformly scattered " << mPointCount << " points into " << mVoxelCount + << " active voxels in \"" << name << "\" corresponding to " + << mPointsPerVoxel << " points per voxel." << std::endl; + } + + float getPointsPerVoxel() const { return mPointsPerVoxel; } + +private: + using BaseT::mPointCount; + using BaseT::mVoxelCount; + float mPointsPerVoxel; +}; // class DenseUniformPointScatter + +/// @brief Non-uniform scatters of point in the active voxels. +/// The local point count is implicitly defined as a product of +/// of a global density (called pointsPerVolume) and the local voxel +/// (or tile) value. +/// +/// @note This scattering technique can be significantly slower +/// than a uniform scattering since its computational complexity +/// is proportional to the active voxel (and tile) count. +template +class NonUniformPointScatter : public BasePointScatter +{ +public: + using BaseT = BasePointScatter; + + NonUniformPointScatter(PointAccessorType& points, + float pointsPerVolume, + RandomGenerator& randGen, + double spread = 1.0, + InterruptType* interrupt = nullptr) + : BaseT(points, randGen, spread, interrupt) + , mPointsPerVolume(pointsPerVolume)//note this is merely a + //multiplier for the local point density + { + } + + /// This is the main functor method implementing the actual scattering of points. + template + bool operator()(const GridT& grid) + { + if (mPointsPerVolume <= 0.0f) return false; + mVoxelCount = grid.activeVoxelCount(); + if (mVoxelCount == 0) return false; + BaseT::start("Non-uniform scattering with local point density"); + const Vec3d dim = grid.voxelSize(); + const double volumePerVoxel = dim[0]*dim[1]*dim[2], + pointsPerVoxel = mPointsPerVolume * volumePerVoxel; + CoordBBox bbox; + const Vec3R offset(0.5, 0.5, 0.5); + for (typename GridT::ValueOnCIter iter = grid.cbeginValueOn(); iter; ++iter) { + if (BaseT::interrupt()) return false; + const double d = double(*iter) * pointsPerVoxel * double(iter.getVoxelCount()); + const int n = int(d); + if (iter.isVoxelValue()) { // a majority is expected to be voxels + const Vec3R dmin =iter.getCoord() - offset; + for (int i = 0; i < n; ++i) BaseT::addPoint(grid, dmin); + if (BaseT::getRand01() < (d - n)) BaseT::addPoint(grid, dmin); + } else { // tiles contain multiple (virtual) voxels + iter.getBoundingBox(bbox); + const Coord size(bbox.extents()); + const Vec3R dmin = bbox.min() - offset; + for (int i = 0; i < n; ++i) BaseT::addPoint(grid, dmin, size); + if (BaseT::getRand01() < (d - n)) BaseT::addPoint(grid, dmin, size); + } + }//loop over all the active voxels and tiles + BaseT::end(); + return true; + } + + // The following methods should only be called after the + // the operator() method was called + void print(const std::string &name, std::ostream& os = std::cout) const + { + os << "Non-uniformly scattered " << mPointCount << " points into " << mVoxelCount + << " active voxels in \"" << name << "\"." << std::endl; + } + + float getPointPerVolume() const { return mPointsPerVolume; } + +private: + using BaseT::mPointCount; + using BaseT::mVoxelCount; + float mPointsPerVolume; + +}; // class NonUniformPointScatter + +/// Base class of all the point scattering classes defined above +template +class BasePointScatter +{ +public: + + Index64 getPointCount() const { return mPointCount; } + Index64 getVoxelCount() const { return mVoxelCount; } + +protected: + + PointAccessorType& mPoints; + InterruptType* mInterrupter; + Index64 mPointCount; + Index64 mVoxelCount; + Index64 mInterruptCount; + const double mSpread; + math::Rand01 mRand01; + + /// This is a base class so the constructor is protected + BasePointScatter(PointAccessorType& points, + RandomGenerator& randGen, + double spread, + InterruptType* interrupt = nullptr) + : mPoints(points) + , mInterrupter(interrupt) + , mPointCount(0) + , mVoxelCount(0) + , mInterruptCount(0) + , mSpread(math::Clamp01(spread)) + , mRand01(randGen) + { + } + + inline void start(const char* name) + { + if (mInterrupter) mInterrupter->start(name); + } + + inline void end() + { + if (mInterrupter) mInterrupter->end(); + } + + inline bool interrupt() + { + //only check interrupter for every 32'th call + return !(mInterruptCount++ & ((1<<5)-1)) && util::wasInterrupted(mInterrupter); + } + + /// @brief Return a random floating point number between zero and one + inline double getRand01() { return mRand01(); } + + /// @brief Return a random floating point number between 0.5 -+ mSpread/2 + inline double getRand() { return 0.5 + mSpread * (mRand01() - 0.5); } + + template + inline void addPoint(const GridT &grid, const Vec3R &dmin) + { + const Vec3R pos(dmin[0] + this->getRand(), + dmin[1] + this->getRand(), + dmin[2] + this->getRand()); + mPoints.add(grid.indexToWorld(pos)); + ++mPointCount; + } + + template + inline void addPoint(const GridT &grid, const Vec3R &dmin, const Coord &size) + { + const Vec3R pos(dmin[0] + size[0]*this->getRand(), + dmin[1] + size[1]*this->getRand(), + dmin[2] + size[2]*this->getRand()); + mPoints.add(grid.indexToWorld(pos)); + ++mPointCount; + } +};// class BasePointScatter + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/PointsToMask.h b/openvdb/tools/PointsToMask.h new file mode 100644 index 00000000..592ab7a1 --- /dev/null +++ b/openvdb/tools/PointsToMask.h @@ -0,0 +1,252 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @author Ken Museth +/// +/// @file tools/PointsToMask.h +/// +/// @brief This tool produces a grid where every voxel that contains a +/// point is active. It employes thread-local storage for best performance. +/// +/// The @c PointListT template argument below refers to any class +/// with the following interface (see unittest/TestPointsToMask.cc +/// and SOP_OpenVDB_From_Particles.cc for practical examples): +/// @code +/// +/// class PointList { +/// ... +/// public: +/// +/// // Return the total number of particles in list. +/// size_t size() const; +/// +/// // Get the world space position of the nth particle. +/// void getPos(size_t n, Vec3R& xyz) const; +/// }; +/// @endcode +/// +/// @note See unittest/TestPointsToMask.cc for an example. +/// +/// The @c InterruptT template argument below refers to any class +/// with the following interface: +/// @code +/// class Interrupter { +/// ... +/// public: +/// void start(const char* name = nullptr) // called when computations begin +/// void end() // called when computations end +/// bool wasInterrupted(int percent = -1) // return true to break computation +/// }; +/// @endcode +/// +/// @note If no template argument is provided for this InterruptT +/// the util::NullInterrupter is used which implies that all +/// interrupter calls are no-ops (i.e. incurs no computational overhead). + +#ifndef OPENVDB_TOOLS_POINTSTOMASK_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINTSTOMASK_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include // for MaskGrid +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +// Forward declaration of main class +template +class PointsToMask; + +/// @brief Makes every voxel of the @c grid active if it contains a point. +/// +/// @param points points that active the voxels of @c grid +/// @param grid on out its voxels with points are active +template +inline void +maskPoints(const PointListT& points, GridT& grid) +{ + PointsToMask tmp(grid, nullptr); + tmp.addPoints(points); +} + +/// @brief Return a MaskGrid where each binary voxel value +/// is on if the voxel contains one (or more) points (i.e. +/// the 3D position of a point is closer to this voxel than +/// any other voxels). +/// +/// @param points points that active the voxels in the returned grid. +/// @param xform transform from world space to voxels in grid space. +template +inline MaskGrid::Ptr +createPointMask(const PointListT& points, const math::Transform& xform) +{ + MaskGrid::Ptr grid = createGrid( false ); + grid->setTransform( xform.copy() ); + maskPoints( points, *grid ); + return grid; +} + +//////////////////////////////////////// + +/// @brief Makes every voxel of a grid active if it contains a point. +template +class PointsToMask +{ +public: + using ValueT = typename GridT::ValueType; + + /// @brief Constructor from a grid and optional interrupter + /// + /// @param grid Grid whoes voxels will have their state activated by points. + /// @param interrupter Optional interrupter to prematurely terminate execution. + explicit PointsToMask(GridT& grid, InterrupterT* interrupter = nullptr) + : mGrid(&grid) + , mInterrupter(interrupter) + { + } + + /// @brief Activates the state of any voxel in the input grid that contains a point. + /// + /// @param points List of points that active the voxels in the input grid. + /// @param grainSize Set the grain-size used for multi-threading. A value of 0 + /// disables multi-threading! + template + void addPoints(const PointListT& points, size_t grainSize = 1024) + { + if (mInterrupter) mInterrupter->start("PointsToMask: adding points"); + if (grainSize > 0) { + typename GridT::Ptr examplar = mGrid->copyWithNewTree(); + PoolType pool( *examplar );//thread local storage pool of grids + AddPoints tmp(points, pool, grainSize, *this ); + if ( this->interrupt() ) return; + ReducePool reducePool(pool, mGrid, size_t(0)); + } else { + const math::Transform& xform = mGrid->transform(); + typename GridT::Accessor acc = mGrid->getAccessor(); + Vec3R wPos; + for (size_t i = 0, n = points.size(); i < n; ++i) { + if ( this->interrupt() ) break; + points.getPos(i, wPos); + acc.setValueOn( xform.worldToIndexCellCentered( wPos ) ); + } + } + if (mInterrupter) mInterrupter->end(); + } + +private: + // Disallow copy construction and copy by assignment! + PointsToMask(const PointsToMask&);// not implemented + PointsToMask& operator=(const PointsToMask&);// not implemented + + bool interrupt() const + { + if (mInterrupter && util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return true; + } + return false; + } + + // Private struct that implements concurrent thread-local + // insersion of points into a grid + using PoolType = tbb::enumerable_thread_specific; + template struct AddPoints; + + // Private class that implements concurrent reduction of a thread-local pool + struct ReducePool; + + GridT* mGrid; + InterrupterT* mInterrupter; +};// PointsToMask + +// Private member class that implements concurrent thread-local +// insersion of points into a grid +template +template +struct PointsToMask::AddPoints +{ + AddPoints(const PointListT& points, + PoolType& pool, + size_t grainSize, + const PointsToMask& parent) + : mPoints(&points) + , mParent(&parent) + , mPool(&pool) + { + tbb::parallel_for(tbb::blocked_range(0, mPoints->size(), grainSize), *this); + } + void operator()(const tbb::blocked_range& range) const + { + if (mParent->interrupt()) return; + GridT& grid = mPool->local(); + const math::Transform& xform = grid.transform(); + typename GridT::Accessor acc = grid.getAccessor(); + Vec3R wPos; + for (size_t i=range.begin(), n=range.end(); i!=n; ++i) { + mPoints->getPos(i, wPos); + acc.setValueOn( xform.worldToIndexCellCentered( wPos ) ); + } + } + const PointListT* mPoints; + const PointsToMask* mParent; + PoolType* mPool; + +};// end of private member class AddPoints + +// Private member class that implements concurrent reduction of a thread-local pool +template +struct PointsToMask::ReducePool +{ + using VecT = std::vector; + using IterT = typename VecT::iterator; + using RangeT = tbb::blocked_range; + + ReducePool(PoolType& pool, GridT* grid, size_t grainSize = 1) + : mOwnsGrid(false) + , mGrid(grid) + { + if (grainSize == 0) { + for (typename PoolType::const_iterator i = pool.begin(); i != pool.end(); ++i) { + mGrid->topologyUnion(*i); + } + } else { + VecT grids( pool.size() ); + typename PoolType::iterator i = pool.begin(); + for (size_t j=0; j != pool.size(); ++i, ++j) grids[j] = &(*i); + tbb::parallel_reduce( RangeT( grids.begin(), grids.end(), grainSize ), *this ); + } + } + + ReducePool(const ReducePool&, tbb::split) + : mOwnsGrid(true) + , mGrid(new GridT()) + { + } + + ~ReducePool() { if (mOwnsGrid) delete mGrid; } + + void operator()(const RangeT& r) + { + for (IterT i=r.begin(); i!=r.end(); ++i) mGrid->topologyUnion( *(*i) ); + } + + void join(ReducePool& other) { mGrid->topologyUnion(*other.mGrid); } + + const bool mOwnsGrid; + GridT* mGrid; +};// end of private member class ReducePool + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_POINTSTOMASK_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/PoissonSolver.h b/openvdb/tools/PoissonSolver.h new file mode 100644 index 00000000..1de2a1a5 --- /dev/null +++ b/openvdb/tools/PoissonSolver.h @@ -0,0 +1,842 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file PoissonSolver.h +/// +/// @authors D.J. Hill, Peter Cucka +/// +/// @brief Solve Poisson's equation ∇2x = b +/// for x, where @e b is a vector comprising the values of all of the active voxels +/// in a grid. +/// +/// @par Example: +/// Solve for the pressure in a cubic tank of liquid, assuming uniform boundary conditions: +/// @code +/// FloatTree source(/*background=*/0.0f); +/// // Activate voxels to indicate that they contain liquid. +/// source.fill(CoordBBox(Coord(0, -10, 0), Coord(10, 0, 10)), /*value=*/0.0f); +/// +/// math::pcg::State state = math::pcg::terminationDefaults(); +/// FloatTree::Ptr solution = tools::poisson::solve(source, state); +/// @endcode +/// +/// @par Example: +/// Solve for the pressure, P, in a cubic tank of liquid that is open at the top. +/// Boundary conditions are P = 0 at the top, +/// ∂P/∂y = −1 at the bottom +/// and ∂P/∂x = 0 at the sides: +///
+///                P = 0
+///             +--------+ (N,0,N)
+///            /|       /|
+///   (0,0,0) +--------+ |
+///           | |      | | dP/dx = 0
+/// dP/dx = 0 | +------|-+
+///           |/       |/
+///  (0,-N,0) +--------+ (N,-N,0)
+///           dP/dy = -1
+/// 
+/// @code +/// const int N = 10; +/// DoubleTree source(/*background=*/0.0); +/// // Activate voxels to indicate that they contain liquid. +/// source.fill(CoordBBox(Coord(0, -N, 0), Coord(N, 0, N)), /*value=*/0.0); +/// +/// auto boundary = [](const openvdb::Coord& ijk, const openvdb::Coord& neighbor, +/// double& source, double& diagonal) +/// { +/// if (neighbor.x() == ijk.x() && neighbor.z() == ijk.z()) { +/// if (neighbor.y() < ijk.y()) source -= 1.0; +/// else diagonal -= 1.0; +/// } +/// }; +/// +/// math::pcg::State state = math::pcg::terminationDefaults(); +/// util::NullInterrupter interrupter; +/// +/// DoubleTree::Ptr solution = tools::poisson::solveWithBoundaryConditions( +/// source, boundary, state, interrupter); +/// @endcode + +#ifndef OPENVDB_TOOLS_POISSONSOLVER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POISSONSOLVER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include "Morphology.h" // for erodeVoxels + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { +namespace poisson { + +// This type should be at least as wide as math::pcg::SizeType. +using VIndex = Int32; + +/// The type of a matrix used to represent a three-dimensional %Laplacian operator +using LaplacianMatrix = math::pcg::SparseStencilMatrix; + + +//@{ +/// @brief Solve ∇2x = b for x, +/// where @e b is a vector comprising the values of all of the active voxels +/// in the input tree. +/// @return a new tree, with the same active voxel topology as the input tree, +/// whose voxel values are the elements of the solution vector x. +/// @details On input, the State object should specify convergence criteria +/// (minimum error and maximum number of iterations); on output, it gives +/// the actual termination conditions. +/// @details The solution is computed using the conjugate gradient method +/// with (where possible) incomplete Cholesky preconditioning, falling back +/// to Jacobi preconditioning. +/// @sa solveWithBoundaryConditions +template +inline typename TreeType::Ptr +solve(const TreeType&, math::pcg::State&, bool staggered = false); + +template +inline typename TreeType::Ptr +solve(const TreeType&, math::pcg::State&, Interrupter&, bool staggered = false); +//@} + + +//@{ +/// @brief Solve ∇2x = b for x +/// with user-specified boundary conditions, where @e b is a vector comprising +/// the values of all of the active voxels in the input tree or domain mask if provided +/// @return a new tree, with the same active voxel topology as the input tree, +/// whose voxel values are the elements of the solution vector x. +/// @details On input, the State object should specify convergence criteria +/// (minimum error and maximum number of iterations); on output, it gives +/// the actual termination conditions. +/// @details The solution is computed using the conjugate gradient method with +/// the specified type of preconditioner (default: incomplete Cholesky), +/// falling back to Jacobi preconditioning if necessary. +/// @details Each thread gets its own copy of the BoundaryOp, which should be +/// a functor of the form +/// @code +/// struct BoundaryOp { +/// using ValueType = LaplacianMatrix::ValueType; +/// void operator()( +/// const Coord& ijk, // coordinates of a boundary voxel +/// const Coord& ijkNeighbor, // coordinates of an exterior neighbor of ijk +/// ValueType& source, // element of b corresponding to ijk +/// ValueType& diagonal // element of Laplacian matrix corresponding to ijk +/// ) const; +/// }; +/// @endcode +/// The functor is called for each of the exterior neighbors of each boundary voxel @ijk, +/// and it must specify a boundary condition for @ijk by modifying one or both of two +/// provided values: the entry in the source vector @e b corresponding to @ijk and +/// the weighting coefficient for @ijk in the Laplacian operator matrix. +/// +/// @sa solve +template +inline typename TreeType::Ptr +solveWithBoundaryConditions( + const TreeType&, + const BoundaryOp&, + math::pcg::State&, + Interrupter&, + bool staggered = false); + +template< + typename PreconditionerType, + typename TreeType, + typename BoundaryOp, + typename Interrupter> +inline typename TreeType::Ptr +solveWithBoundaryConditionsAndPreconditioner( + const TreeType&, + const BoundaryOp&, + math::pcg::State&, + Interrupter&, + bool staggered = false); + +template< + typename PreconditionerType, + typename TreeType, + typename DomainTreeType, + typename BoundaryOp, + typename Interrupter> +inline typename TreeType::Ptr +solveWithBoundaryConditionsAndPreconditioner( + const TreeType&, + const DomainTreeType&, + const BoundaryOp&, + math::pcg::State&, + Interrupter&, + bool staggered = false); +//@} + + +/// @name Low-level functions +//@{ +// The following are low-level routines that can be used to assemble custom solvers. + +/// @brief Overwrite each active voxel in the given scalar tree +/// with a sequential index, starting from zero. +template +inline void populateIndexTree(VIndexTreeType&); + +/// @brief Iterate over the active voxels of the input tree and for each one +/// assign its index in the iteration sequence to the corresponding voxel +/// of an integer-valued output tree. +template +inline typename TreeType::template ValueConverter::Type::Ptr +createIndexTree(const TreeType&); + + +/// @brief Return a vector of the active voxel values of the scalar-valued @a source tree. +/// @details The nth element of the vector corresponds to the voxel whose value +/// in the @a index tree is @e n. +/// @param source a tree with a scalar value type +/// @param index a tree of the same configuration as @a source but with +/// value type VIndex that maps voxels to elements of the output vector +template +inline typename math::pcg::Vector::Ptr +createVectorFromTree( + const SourceTreeType& source, + const typename SourceTreeType::template ValueConverter::Type& index); + + +/// @brief Return a tree with the same active voxel topology as the @a index tree +/// but whose voxel values are taken from the the given vector. +/// @details The voxel whose value in the @a index tree is @e n gets assigned +/// the nth element of the vector. +/// @param index a tree with value type VIndex that maps voxels to elements of @a values +/// @param values a vector of values with which to populate the active voxels of the output tree +/// @param background the value for the inactive voxels of the output tree +template +inline typename VIndexTreeType::template ValueConverter::Type::Ptr +createTreeFromVector( + const math::pcg::Vector& values, + const VIndexTreeType& index, + const TreeValueType& background); + + +/// @brief Generate a sparse matrix of the index-space (Δx = 1) %Laplacian operator +/// using second-order finite differences. +/// @details This construction assumes homogeneous Dirichlet boundary conditions +/// (exterior grid points are zero). +template +inline LaplacianMatrix::Ptr +createISLaplacian( + const typename BoolTreeType::template ValueConverter::Type& vectorIndexTree, + const BoolTreeType& interiorMask, + bool staggered = false); + + +/// @brief Generate a sparse matrix of the index-space (Δx = 1) %Laplacian operator +/// with user-specified boundary conditions using second-order finite differences. +/// @details Each thread gets its own copy of @a boundaryOp, which should be +/// a functor of the form +/// @code +/// struct BoundaryOp { +/// using ValueType = LaplacianMatrix::ValueType; +/// void operator()( +/// const Coord& ijk, // coordinates of a boundary voxel +/// const Coord& ijkNeighbor, // coordinates of an exterior neighbor of ijk +/// ValueType& source, // element of source vector corresponding to ijk +/// ValueType& diagonal // element of Laplacian matrix corresponding to ijk +/// ) const; +/// }; +/// @endcode +/// The functor is called for each of the exterior neighbors of each boundary voxel @ijk, +/// and it must specify a boundary condition for @ijk by modifying one or both of two +/// provided values: an entry in the given @a source vector corresponding to @ijk and +/// the weighting coefficient for @ijk in the %Laplacian matrix. +template +inline LaplacianMatrix::Ptr +createISLaplacianWithBoundaryConditions( + const typename BoolTreeType::template ValueConverter::Type& vectorIndexTree, + const BoolTreeType& interiorMask, + const BoundaryOp& boundaryOp, + typename math::pcg::Vector& source, + bool staggered = false); + + +/// @brief Dirichlet boundary condition functor +/// @details This is useful in describing fluid/air interfaces, where the pressure +/// of the air is assumed to be zero. +template +struct DirichletBoundaryOp { + inline void operator()(const Coord&, const Coord&, ValueType&, ValueType& diag) const { + // Exterior neighbors are empty, so decrement the weighting coefficient + // as for interior neighbors but leave the source vector unchanged. + diag -= 1; + } +}; + +//@} + + +//////////////////////////////////////// + + +namespace internal { + +/// @brief Functor for use with LeafManager::foreach() to populate an array +/// with per-leaf active voxel counts +template +struct LeafCountOp +{ + VIndex* count; + LeafCountOp(VIndex* count_): count(count_) {} + void operator()(const LeafType& leaf, size_t leafIdx) const { + count[leafIdx] = static_cast(leaf.onVoxelCount()); + } +}; + + +/// @brief Functor for use with LeafManager::foreach() to populate +/// active leaf voxels with sequential indices +template +struct LeafIndexOp +{ + const VIndex* count; + LeafIndexOp(const VIndex* count_): count(count_) {} + void operator()(LeafType& leaf, size_t leafIdx) const { + VIndex idx = (leafIdx == 0) ? 0 : count[leafIdx - 1]; + for (typename LeafType::ValueOnIter it = leaf.beginValueOn(); it; ++it) { + it.setValue(idx++); + } + } +}; + +} // namespace internal + + +template +inline void +populateIndexTree(VIndexTreeType& result) +{ + using LeafT = typename VIndexTreeType::LeafNodeType; + using LeafMgrT = typename tree::LeafManager; + + // Linearize the tree. + LeafMgrT leafManager(result); + const size_t leafCount = leafManager.leafCount(); + + if (leafCount == 0) return; + + // Count the number of active voxels in each leaf node. + std::unique_ptr perLeafCount(new VIndex[leafCount]); + VIndex* perLeafCountPtr = perLeafCount.get(); + leafManager.foreach(internal::LeafCountOp(perLeafCountPtr)); + + // The starting index for each leaf node is the total number + // of active voxels in all preceding leaf nodes. + for (size_t i = 1; i < leafCount; ++i) { + perLeafCount[i] += perLeafCount[i - 1]; + } + + // The last accumulated value should be the total of all active voxels. + assert(Index64(perLeafCount[leafCount-1]) == result.activeVoxelCount()); + + // Parallelize over the leaf nodes of the tree, storing a unique index + // in each active voxel. + leafManager.foreach(internal::LeafIndexOp(perLeafCountPtr)); +} + + +template +inline typename TreeType::template ValueConverter::Type::Ptr +createIndexTree(const TreeType& tree) +{ + using VIdxTreeT = typename TreeType::template ValueConverter::Type; + + // Construct an output tree with the same active voxel topology as the input tree. + const VIndex invalidIdx = -1; + typename VIdxTreeT::Ptr result( + new VIdxTreeT(tree, /*background=*/invalidIdx, TopologyCopy())); + + // All active voxels are degrees of freedom, including voxels contained in active tiles. + result->voxelizeActiveTiles(); + + populateIndexTree(*result); + + return result; +} + + +//////////////////////////////////////// + + +namespace internal { + +/// @brief Functor for use with LeafManager::foreach() to populate a vector +/// with the values of a tree's active voxels +template +struct CopyToVecOp +{ + using VIdxTreeT = typename SourceTreeType::template ValueConverter::Type; + using VIdxLeafT = typename VIdxTreeT::LeafNodeType; + using LeafT = typename SourceTreeType::LeafNodeType; + using TreeValueT = typename SourceTreeType::ValueType; + using VectorT = typename math::pcg::Vector; + + const SourceTreeType* tree; + VectorT* vector; + + CopyToVecOp(const SourceTreeType& t, VectorT& v): tree(&t), vector(&v) {} + + void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const + { + VectorT& vec = *vector; + if (const LeafT* leaf = tree->probeLeaf(idxLeaf.origin())) { + // If a corresponding leaf node exists in the source tree, + // copy voxel values from the source node to the output vector. + for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { + vec[*it] = leaf->getValue(it.pos()); + } + } else { + // If no corresponding leaf exists in the source tree, + // fill the vector with a uniform value. + const TreeValueT& value = tree->getValue(idxLeaf.origin()); + for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { + vec[*it] = value; + } + } + } +}; + +} // namespace internal + + +template +inline typename math::pcg::Vector::Ptr +createVectorFromTree(const SourceTreeType& tree, + const typename SourceTreeType::template ValueConverter::Type& idxTree) +{ + using VIdxTreeT = typename SourceTreeType::template ValueConverter::Type; + using VIdxLeafMgrT = tree::LeafManager; + using VectorT = typename math::pcg::Vector; + + // Allocate the vector. + const size_t numVoxels = idxTree.activeVoxelCount(); + typename VectorT::Ptr result(new VectorT(static_cast(numVoxels))); + + // Parallelize over the leaf nodes of the index tree, filling the output vector + // with values from corresponding voxels of the source tree. + VIdxLeafMgrT leafManager(idxTree); + leafManager.foreach(internal::CopyToVecOp(tree, *result)); + + return result; +} + + +//////////////////////////////////////// + + +namespace internal { + +/// @brief Functor for use with LeafManager::foreach() to populate a tree +/// with values from a vector +template +struct CopyFromVecOp +{ + using OutTreeT = typename VIndexTreeType::template ValueConverter::Type; + using OutLeafT = typename OutTreeT::LeafNodeType; + using VIdxLeafT = typename VIndexTreeType::LeafNodeType; + using VectorT = typename math::pcg::Vector; + + const VectorT* vector; + OutTreeT* tree; + + CopyFromVecOp(const VectorT& v, OutTreeT& t): vector(&v), tree(&t) {} + + void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const + { + const VectorT& vec = *vector; + OutLeafT* leaf = tree->probeLeaf(idxLeaf.origin()); + assert(leaf != nullptr); + for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { + leaf->setValueOnly(it.pos(), static_cast(vec[*it])); + } + } +}; + +} // namespace internal + + +template +inline typename VIndexTreeType::template ValueConverter::Type::Ptr +createTreeFromVector( + const math::pcg::Vector& vector, + const VIndexTreeType& idxTree, + const TreeValueType& background) +{ + using OutTreeT = typename VIndexTreeType::template ValueConverter::Type; + using VIdxLeafMgrT = typename tree::LeafManager; + + // Construct an output tree with the same active voxel topology as the index tree. + typename OutTreeT::Ptr result(new OutTreeT(idxTree, background, TopologyCopy())); + OutTreeT& tree = *result; + + // Parallelize over the leaf nodes of the index tree, populating voxels + // of the output tree with values from the input vector. + VIdxLeafMgrT leafManager(idxTree); + leafManager.foreach( + internal::CopyFromVecOp(vector, tree)); + + return result; +} + + +//////////////////////////////////////// + + +namespace internal { + +/// Functor for use with LeafManager::foreach() to populate a sparse %Laplacian matrix +template +struct ISStaggeredLaplacianOp +{ + using VIdxTreeT = typename BoolTreeType::template ValueConverter::Type; + using VIdxLeafT = typename VIdxTreeT::LeafNodeType; + using ValueT = LaplacianMatrix::ValueType; + using VectorT = typename math::pcg::Vector; + + LaplacianMatrix* laplacian; + const VIdxTreeT* idxTree; + const BoolTreeType* interiorMask; + const BoundaryOp boundaryOp; + VectorT* source; + + ISStaggeredLaplacianOp(LaplacianMatrix& m, const VIdxTreeT& idx, + const BoolTreeType& mask, const BoundaryOp& op, VectorT& src): + laplacian(&m), idxTree(&idx), interiorMask(&mask), boundaryOp(op), source(&src) {} + + void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const + { + // Local accessors + typename tree::ValueAccessor interior(*interiorMask); + typename tree::ValueAccessor vectorIdx(*idxTree); + + Coord ijk; + VIndex column; + const ValueT diagonal = -6.f, offDiagonal = 1.f; + + // Loop over active voxels in this leaf. + for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { + assert(it.getValue() > -1); + const math::pcg::SizeType rowNum = static_cast(it.getValue()); + + LaplacianMatrix::RowEditor row = laplacian->getRowEditor(rowNum); + + ijk = it.getCoord(); + if (interior.isValueOn(ijk)) { + // The current voxel is an interior voxel. + // All of its neighbors are in the solution domain. + + // -x direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(-1, 0, 0)), offDiagonal); + // -y direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(0, -1, 0)), offDiagonal); + // -z direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(0, 0, -1)), offDiagonal); + // diagonal + row.setValue(rowNum, diagonal); + // +z direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(0, 0, 1)), offDiagonal); + // +y direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(0, 1, 0)), offDiagonal); + // +x direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(1, 0, 0)), offDiagonal); + + } else { + // The current voxel is a boundary voxel. + // At least one of its neighbors is outside the solution domain. + + ValueT modifiedDiagonal = 0.f; + + // -x direction + if (vectorIdx.probeValue(ijk.offsetBy(-1, 0, 0), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(-1, 0, 0), source->at(rowNum), modifiedDiagonal); + } + // -y direction + if (vectorIdx.probeValue(ijk.offsetBy(0, -1, 0), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(0, -1, 0), source->at(rowNum), modifiedDiagonal); + } + // -z direction + if (vectorIdx.probeValue(ijk.offsetBy(0, 0, -1), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(0, 0, -1), source->at(rowNum), modifiedDiagonal); + } + // +z direction + if (vectorIdx.probeValue(ijk.offsetBy(0, 0, 1), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(0, 0, 1), source->at(rowNum), modifiedDiagonal); + } + // +y direction + if (vectorIdx.probeValue(ijk.offsetBy(0, 1, 0), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(0, 1, 0), source->at(rowNum), modifiedDiagonal); + } + // +x direction + if (vectorIdx.probeValue(ijk.offsetBy(1, 0, 0), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(1, 0, 0), source->at(rowNum), modifiedDiagonal); + } + // diagonal + row.setValue(rowNum, modifiedDiagonal); + } + } // end loop over voxels + } +}; + + +// Stencil 1 is the correct stencil, but stencil 2 requires +// half as many comparisons and produces smoother results at boundaries. +//#define OPENVDB_TOOLS_POISSON_LAPLACIAN_STENCIL 1 +#define OPENVDB_TOOLS_POISSON_LAPLACIAN_STENCIL 2 + +/// Functor for use with LeafManager::foreach() to populate a sparse %Laplacian matrix +template +struct ISLaplacianOp +{ + using VIdxLeafT = typename VIdxTreeT::LeafNodeType; + using ValueT = LaplacianMatrix::ValueType; + using VectorT = typename math::pcg::Vector; + + LaplacianMatrix* laplacian; + const VIdxTreeT* idxTree; + const BoundaryOp boundaryOp; + VectorT* source; + + ISLaplacianOp(LaplacianMatrix& m, const VIdxTreeT& idx, const BoundaryOp& op, VectorT& src): + laplacian(&m), idxTree(&idx), boundaryOp(op), source(&src) {} + + void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const + { + typename tree::ValueAccessor vectorIdx(*idxTree); + + const int kNumOffsets = 6; + const Coord ijkOffset[kNumOffsets] = { +#if OPENVDB_TOOLS_POISSON_LAPLACIAN_STENCIL == 1 + Coord(-1,0,0), Coord(1,0,0), Coord(0,-1,0), Coord(0,1,0), Coord(0,0,-1), Coord(0,0,1) +#else + Coord(-2,0,0), Coord(2,0,0), Coord(0,-2,0), Coord(0,2,0), Coord(0,0,-2), Coord(0,0,2) +#endif + }; + + // For each active voxel in this leaf... + for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { + assert(it.getValue() > -1); + + const Coord ijk = it.getCoord(); + const math::pcg::SizeType rowNum = static_cast(it.getValue()); + + LaplacianMatrix::RowEditor row = laplacian->getRowEditor(rowNum); + + ValueT modifiedDiagonal = 0.f; + + // For each of the neighbors of the voxel at (i,j,k)... + for (int dir = 0; dir < kNumOffsets; ++dir) { + const Coord neighbor = ijk + ijkOffset[dir]; + VIndex column; + // For collocated vector grids, the central differencing stencil requires + // access to neighbors at a distance of two voxels in each direction + // (-x, +x, -y, +y, -z, +z). +#if OPENVDB_TOOLS_POISSON_LAPLACIAN_STENCIL == 1 + const bool ijkIsInterior = (vectorIdx.probeValue(neighbor + ijkOffset[dir], column) + && vectorIdx.isValueOn(neighbor)); +#else + const bool ijkIsInterior = vectorIdx.probeValue(neighbor, column); +#endif + if (ijkIsInterior) { + // If (i,j,k) is sufficiently far away from the exterior, + // set its weight to one and adjust the center weight accordingly. + row.setValue(column, 1.f); + modifiedDiagonal -= 1.f; + } else { + // If (i,j,k) is adjacent to or one voxel away from the exterior, + // invoke the boundary condition functor. + boundaryOp(ijk, neighbor, source->at(rowNum), modifiedDiagonal); + } + } + // Set the (possibly modified) weight for the voxel at (i,j,k). + row.setValue(rowNum, modifiedDiagonal); + } + } +}; + +} // namespace internal + + +template +inline LaplacianMatrix::Ptr +createISLaplacian(const typename BoolTreeType::template ValueConverter::Type& idxTree, + const BoolTreeType& interiorMask, bool staggered) +{ + using ValueT = LaplacianMatrix::ValueType; + math::pcg::Vector unused( + static_cast(idxTree.activeVoxelCount())); + DirichletBoundaryOp op; + return createISLaplacianWithBoundaryConditions(idxTree, interiorMask, op, unused, staggered); +} + + +template +inline LaplacianMatrix::Ptr +createISLaplacianWithBoundaryConditions( + const typename BoolTreeType::template ValueConverter::Type& idxTree, + const BoolTreeType& interiorMask, + const BoundaryOp& boundaryOp, + typename math::pcg::Vector& source, + bool staggered) +{ + using VIdxTreeT = typename BoolTreeType::template ValueConverter::Type; + using VIdxLeafMgrT = typename tree::LeafManager; + + // The number of active voxels is the number of degrees of freedom. + const Index64 numDoF = idxTree.activeVoxelCount(); + + // Construct the matrix. + LaplacianMatrix::Ptr laplacianPtr( + new LaplacianMatrix(static_cast(numDoF))); + LaplacianMatrix& laplacian = *laplacianPtr; + + // Populate the matrix using a second-order, 7-point CD stencil. + VIdxLeafMgrT idxLeafManager(idxTree); + if (staggered) { + idxLeafManager.foreach(internal::ISStaggeredLaplacianOp( + laplacian, idxTree, interiorMask, boundaryOp, source)); + } else { + idxLeafManager.foreach(internal::ISLaplacianOp( + laplacian, idxTree, boundaryOp, source)); + } + + return laplacianPtr; +} + + +//////////////////////////////////////// + + +template +inline typename TreeType::Ptr +solve(const TreeType& inTree, math::pcg::State& state, bool staggered) +{ + util::NullInterrupter interrupter; + return solve(inTree, state, interrupter, staggered); +} + + +template +inline typename TreeType::Ptr +solve(const TreeType& inTree, math::pcg::State& state, Interrupter& interrupter, bool staggered) +{ + DirichletBoundaryOp boundaryOp; + return solveWithBoundaryConditions(inTree, boundaryOp, state, interrupter, staggered); +} + + +template +inline typename TreeType::Ptr +solveWithBoundaryConditions(const TreeType& inTree, const BoundaryOp& boundaryOp, + math::pcg::State& state, Interrupter& interrupter, bool staggered) +{ + using DefaultPrecondT = math::pcg::IncompleteCholeskyPreconditioner; + return solveWithBoundaryConditionsAndPreconditioner( + inTree, boundaryOp, state, interrupter, staggered); +} + + +template< + typename PreconditionerType, + typename TreeType, + typename BoundaryOp, + typename Interrupter> +inline typename TreeType::Ptr +solveWithBoundaryConditionsAndPreconditioner( + const TreeType& inTree, + const BoundaryOp& boundaryOp, + math::pcg::State& state, + Interrupter& interrupter, + bool staggered) +{ + return solveWithBoundaryConditionsAndPreconditioner( + /*source=*/inTree, /*domain mask=*/inTree, boundaryOp, state, interrupter, staggered); +} + +template< + typename PreconditionerType, + typename TreeType, + typename DomainTreeType, + typename BoundaryOp, + typename Interrupter> +inline typename TreeType::Ptr +solveWithBoundaryConditionsAndPreconditioner( + const TreeType& inTree, + const DomainTreeType& domainMask, + const BoundaryOp& boundaryOp, + math::pcg::State& state, + Interrupter& interrupter, + bool staggered) +{ + using TreeValueT = typename TreeType::ValueType; + using VecValueT = LaplacianMatrix::ValueType; + using VectorT = typename math::pcg::Vector; + using VIdxTreeT = typename TreeType::template ValueConverter::Type; + using MaskTreeT = typename TreeType::template ValueConverter::Type; + + // 1. Create a mapping from active voxels of the input tree to elements of a vector. + typename VIdxTreeT::ConstPtr idxTree = createIndexTree(domainMask); + + // 2. Populate a vector with values from the input tree. + typename VectorT::Ptr b = createVectorFromTree(inTree, *idxTree); + + // 3. Create a mask of the interior voxels of the input tree (from the densified index tree). + /// @todo Is this really needed? + typename MaskTreeT::Ptr interiorMask( + new MaskTreeT(*idxTree, /*background=*/false, TopologyCopy())); + tools::erodeVoxels(*interiorMask, /*iterations=*/1, tools::NN_FACE); + + // 4. Create the Laplacian matrix. + LaplacianMatrix::Ptr laplacian = createISLaplacianWithBoundaryConditions( + *idxTree, *interiorMask, boundaryOp, *b, staggered); + + // 5. Solve the Poisson equation. + laplacian->scale(-1.0); // matrix is negative-definite; solve -M x = -b + b->scale(-1.0); + typename VectorT::Ptr x(new VectorT(b->size(), zeroVal())); + typename math::pcg::Preconditioner::Ptr precond( + new PreconditionerType(*laplacian)); + if (!precond->isValid()) { + precond.reset(new math::pcg::JacobiPreconditioner(*laplacian)); + } + + state = math::pcg::solve(*laplacian, *b, *x, *precond, interrupter, state); + + // 6. Populate the output tree with values from the solution vector. + /// @todo if (state.success) ... ? + return createTreeFromVector(*x, *idxTree, /*background=*/zeroVal()); +} + +} // namespace poisson +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_POISSONSOLVER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/PotentialFlow.h b/openvdb/tools/PotentialFlow.h new file mode 100644 index 00000000..c918c33e --- /dev/null +++ b/openvdb/tools/PotentialFlow.h @@ -0,0 +1,395 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file tools/PotentialFlow.h +/// +/// @brief Tools for creating potential flow fields through solving Laplace's equation +/// +/// @authors Todd Keeler, Dan Bailey + +#ifndef OPENVDB_TOOLS_POTENTIAL_FLOW_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POTENTIAL_FLOW_HAS_BEEN_INCLUDED + +#include + +#include "GridOperators.h" +#include "GridTransformer.h" +#include "Mask.h" // interiorMask +#include "Morphology.h" // dilateVoxels, erodeVoxels +#include "PoissonSolver.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Metafunction to convert a vector-valued grid type to a scalar grid type +template +struct VectorToScalarGrid { + using Type = + typename VecGridT::template ValueConverter::Type; + using Ptr = typename Type::Ptr; + using ConstPtr = typename Type::ConstPtr; +}; + + +/// @brief Construct a mask for the Potential Flow domain. +/// @details For a level set, this represents a rebuilt exterior narrow band. +/// For any other grid it is a new region that surrounds the active voxels. +/// @param grid source grid to use for computing the mask +/// @param dilation dilation in voxels of the source grid to form the new potential flow mask +template::Type> +inline typename MaskT::Ptr +createPotentialFlowMask(const GridT& grid, int dilation = 5); + + +/// @brief Create a Potential Flow velocities grid for the Neumann boundary. +/// @param collider a level set that represents the boundary +/// @param domain a mask to represent the potential flow domain +/// @param boundaryVelocity an optional grid pointer to stores the velocities of the boundary +/// @param backgroundVelocity a background velocity value +/// @details Typically this method involves supplying a velocity grid for the +/// collider boundary, however it can also be used for a global wind field +/// around the collider by supplying an empty boundary Velocity and a +/// non-zero background velocity. +template +inline typename GridT::template ValueConverter::Type::Ptr +createPotentialFlowNeumannVelocities(const GridT& collider, const MaskT& domain, + const typename GridT::template ValueConverter::Type::ConstPtr boundaryVelocity, + const Vec3T& backgroundVelocity); + + +/// @brief Compute the Potential on the domain using the Neumann boundary conditions on +/// solid boundaries +/// @param domain a mask to represent the domain in which to perform the solve +/// @param neumann the topology of this grid defines where the solid boundaries are and grid +/// values give the Neumann boundaries that should be applied there +/// @param state the solver parameters for computing the solution +/// @param interrupter pointer to an optional interrupter adhering to the +/// util::NullInterrupter interface +/// @details On input, the State object should specify convergence criteria +/// (minimum error and maximum number of iterations); on output, it gives +/// the actual termination conditions. +template +inline typename VectorToScalarGrid::Ptr +computeScalarPotential(const MaskT& domain, const Vec3GridT& neumann, math::pcg::State& state, + InterrupterT* interrupter = nullptr); + + +/// @brief Compute a vector Flow Field comprising the gradient of the potential with Neumann +/// boundary conditions applied +/// @param potential scalar potential, typically computed from computeScalarPotential() +/// @param neumann the topology of this grid defines where the solid boundaries are and grid +/// values give the Neumann boundaries that should be applied there +/// @param backgroundVelocity a background velocity value +template +inline typename Vec3GridT::Ptr +computePotentialFlow(const typename VectorToScalarGrid::Type& potential, + const Vec3GridT& neumann, + const typename Vec3GridT::ValueType backgroundVelocity = + zeroVal()); + + +////////////////////////////////////////////////////////// + + +namespace potential_flow_internal { + + +/// @private +// helper function for retrieving a mask that comprises the outer-most layer of voxels +template +inline typename GridT::TreeType::template ValueConverter::Type::Ptr +extractOuterVoxelMask(GridT& inGrid) +{ + using MaskTreeT = typename GridT::TreeType::template ValueConverter::Type; + typename MaskTreeT::Ptr interiorMask(new MaskTreeT(inGrid.tree(), false, TopologyCopy())); + typename MaskTreeT::Ptr boundaryMask(new MaskTreeT(inGrid.tree(), false, TopologyCopy())); + + erodeVoxels(*interiorMask, 1, NN_FACE); + boundaryMask->topologyDifference(*interiorMask); + return boundaryMask; +} + + +// computes Neumann velocities through sampling the gradient and velocities +template +struct ComputeNeumannVelocityOp +{ + using ValueT = typename Vec3GridT::ValueType; + using VelocityAccessor = typename Vec3GridT::ConstAccessor; + using VelocitySamplerT = GridSampler< + typename Vec3GridT::ConstAccessor, BoxSampler>; + using GradientValueT = typename GradientT::TreeType::ValueType; + + ComputeNeumannVelocityOp( const GradientT& gradient, + const Vec3GridT& velocity, + const ValueT& backgroundVelocity) + : mGradient(gradient) + , mVelocity(&velocity) + , mBackgroundVelocity(backgroundVelocity) { } + + ComputeNeumannVelocityOp( const GradientT& gradient, + const ValueT& backgroundVelocity) + : mGradient(gradient) + , mBackgroundVelocity(backgroundVelocity) { } + + void operator()(typename Vec3GridT::TreeType::LeafNodeType& leaf, size_t) const { + auto gradientAccessor = mGradient.getConstAccessor(); + + std::unique_ptr velocityAccessor; + std::unique_ptr velocitySampler; + if (mVelocity) { + velocityAccessor.reset(new VelocityAccessor(mVelocity->getConstAccessor())); + velocitySampler.reset(new VelocitySamplerT(*velocityAccessor, mVelocity->transform())); + } + + for (auto it = leaf.beginValueOn(); it; ++it) { + Coord ijk = it.getCoord(); + auto gradient = gradientAccessor.getValue(ijk); + if (gradient.normalize()) { + const Vec3d xyz = mGradient.transform().indexToWorld(ijk); + const ValueT sampledVelocity = velocitySampler ? + velocitySampler->wsSample(xyz) : zeroVal(); + auto velocity = sampledVelocity + mBackgroundVelocity; + auto value = gradient.dot(velocity) * gradient; + it.setValue(value); + } + else { + it.setValueOff(); + } + } + } + +private: + const GradientT& mGradient; + const Vec3GridT* mVelocity = nullptr; + const ValueT& mBackgroundVelocity; +}; // struct ComputeNeumannVelocityOp + + +// initalizes the boundary conditions for use in the Poisson Solver +template +struct SolveBoundaryOp +{ + SolveBoundaryOp(const Vec3GridT& velGrid, const MaskT& domainGrid) + : mVoxelSize(domainGrid.voxelSize()[0]) + , mVelGrid(velGrid) + , mDomainGrid(domainGrid) + { } + + void operator()(const Coord& ijk, const Coord& neighbor, + double& source, double& diagonal) const { + + typename Vec3GridT::ConstAccessor velGridAccessor = mVelGrid.getAccessor(); + const Coord diff = (ijk - neighbor); + + if (velGridAccessor.isValueOn(ijk)) { // Neumann + const typename Vec3GridT::ValueType& sampleVel = velGridAccessor.getValue(ijk); + source += mVoxelSize*diff[0]*sampleVel[0]; + source += mVoxelSize*diff[1]*sampleVel[1]; + source += mVoxelSize*diff[2]*sampleVel[2]; + } else { + diagonal -= 1; // Zero Dirichlet + } + + } + + const double mVoxelSize; + const Vec3GridT& mVelGrid; + const MaskT& mDomainGrid; +}; // struct SolveBoundaryOp + + +} // namespace potential_flow_internal + + +//////////////////////////////////////////////////////////////////////////// + +template +inline typename MaskT::Ptr +createPotentialFlowMask(const GridT& grid, int dilation) +{ + using MaskTreeT = typename MaskT::TreeType; + + if (!grid.hasUniformVoxels()) { + OPENVDB_THROW(ValueError, "Transform must have uniform voxels for Potential Flow mask."); + } + + // construct a new mask grid representing the interior region + auto interior = interiorMask(grid); + + // create a new mask grid from the interior topology + typename MaskTreeT::Ptr maskTree(new MaskTreeT(interior->tree(), false, TopologyCopy())); + typename MaskT::Ptr mask = MaskT::create(maskTree); + mask->setTransform(grid.transform().copy()); + + dilateActiveValues(*maskTree, dilation, NN_FACE_EDGE); + + // subtract the interior region from the mask to leave just the exterior narrow band + mask->tree().topologyDifference(interior->tree()); + + return mask; +} + + +template +typename GridT::template ValueConverter::Type::Ptr createPotentialFlowNeumannVelocities( + const GridT& collider, + const MaskT& domain, + const typename GridT::template ValueConverter::Type::ConstPtr boundaryVelocity, + const Vec3T& backgroundVelocity) +{ + using Vec3GridT = typename GridT::template ValueConverter::Type; + using TreeT = typename Vec3GridT::TreeType; + using ValueT = typename TreeT::ValueType; + using GradientT = typename ScalarToVectorConverter::Type; + + using potential_flow_internal::ComputeNeumannVelocityOp; + + // this method requires the collider to be a level set to generate the gradient + // use the tools::topologyToLevelset() method if you need to convert a mask into a level set + if (collider.getGridClass() != GRID_LEVEL_SET || + !std::is_floating_point::value) { + OPENVDB_THROW(TypeError, "Potential Flow expecting the collider to be a level set."); + } + + // empty grid if there are no velocities + if (backgroundVelocity == zeroVal() && + (!boundaryVelocity || boundaryVelocity->empty())) { + auto neumann = Vec3GridT::create(); + neumann->setTransform(collider.transform().copy()); + return neumann; + } + + // extract the intersection between the collider and the domain + using MaskTreeT = typename GridT::TreeType::template ValueConverter::Type; + typename MaskTreeT::Ptr boundary(new MaskTreeT(domain.tree(), false, TopologyCopy())); + boundary->topologyIntersection(collider.tree()); + + typename TreeT::Ptr neumannTree(new TreeT(*boundary, zeroVal(), TopologyCopy())); + neumannTree->voxelizeActiveTiles(); + + // compute the gradient from the collider + const typename GradientT::Ptr gradient = tools::gradient(collider); + + typename tree::LeafManager leafManager(*neumannTree); + + if (boundaryVelocity && !boundaryVelocity->empty()) { + ComputeNeumannVelocityOp + neumannOp(*gradient, *boundaryVelocity, backgroundVelocity); + leafManager.foreach(neumannOp, false); + } + else { + ComputeNeumannVelocityOp + neumannOp(*gradient, backgroundVelocity); + leafManager.foreach(neumannOp, false); + } + + // prune any inactive values + tools::pruneInactive(*neumannTree); + + typename Vec3GridT::Ptr neumann(Vec3GridT::create(neumannTree)); + neumann->setTransform(collider.transform().copy()); + + return neumann; +} + + +template +inline typename VectorToScalarGrid::Ptr +computeScalarPotential(const MaskT& domain, const Vec3GridT& neumann, + math::pcg::State& state, InterrupterT* interrupter) +{ + using ScalarT = typename Vec3GridT::ValueType::value_type; + using ScalarTreeT = typename Vec3GridT::TreeType::template ValueConverter::Type; + using ScalarGridT = typename Vec3GridT::template ValueConverter::Type; + + using potential_flow_internal::SolveBoundaryOp; + + // create the solution tree and activate using domain topology + ScalarTreeT solveTree(domain.tree(), zeroVal(), TopologyCopy()); + solveTree.voxelizeActiveTiles(); + + util::NullInterrupter nullInterrupt; + if (!interrupter) interrupter = &nullInterrupt; + + // solve for scalar potential + SolveBoundaryOp solve(neumann, domain); + typename ScalarTreeT::Ptr potentialTree = + poisson::solveWithBoundaryConditions(solveTree, solve, state, *interrupter, true); + + auto potential = ScalarGridT::create(potentialTree); + potential->setTransform(domain.transform().copy()); + + return potential; +} + + +template +inline typename Vec3GridT::Ptr +computePotentialFlow(const typename VectorToScalarGrid::Type& potential, + const Vec3GridT& neumann, + const typename Vec3GridT::ValueType backgroundVelocity) +{ + using Vec3T = const typename Vec3GridT::ValueType; + using potential_flow_internal::extractOuterVoxelMask; + + // The VDB gradient op uses the background grid value, which is zero by default, when + // computing the gradient at the boundary. This works at the zero-dirichlet boundaries, but + // give spurious values at Neumann ones as the potential should be non-zero there. To avoid + // the extra error, we just substitute the Neumann condition on the boundaries. + // Technically, we should allow for some tangential velocity, coming from the gradient of + // potential. However, considering the voxelized nature of our solve, a decent approximation + // to a tangential derivative isn't probably worth our time. Any tangential component will be + // found in the next interior ring of voxels. + + auto gradient = tools::gradient(potential); + + // apply Neumann values to the gradient + + auto applyNeumann = [&gradient, &neumann] ( + const MaskGrid::TreeType::LeafNodeType& leaf, size_t) + { + typename Vec3GridT::Accessor gradientAccessor = gradient->getAccessor(); + typename Vec3GridT::ConstAccessor neumannAccessor = neumann.getAccessor(); + for (auto it = leaf.beginValueOn(); it; ++it) { + const Coord ijk = it.getCoord(); + typename Vec3GridT::ValueType value; + if (neumannAccessor.probeValue(ijk, value)) { + gradientAccessor.setValue(ijk, value); + } + } + }; + + const MaskGrid::TreeType::Ptr boundary = extractOuterVoxelMask(*gradient); + typename tree::LeafManager leafManager(*boundary); + leafManager.foreach(applyNeumann); + + // apply the background value to the gradient if supplied + + if (backgroundVelocity != zeroVal()) { + auto applyBackgroundVelocity = [&backgroundVelocity] ( + typename Vec3GridT::TreeType::LeafNodeType& leaf, size_t) + { + for (auto it = leaf.beginValueOn(); it; ++it) { + it.setValue(it.getValue() - backgroundVelocity); + } + }; + + typename tree::LeafManager leafManager2(gradient->tree()); + leafManager2.foreach(applyBackgroundVelocity); + } + + return gradient; +} + + +//////////////////////////////////////// + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_POTENTIAL_FLOW_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Prune.h b/openvdb/tools/Prune.h new file mode 100644 index 00000000..d221928c --- /dev/null +++ b/openvdb/tools/Prune.h @@ -0,0 +1,400 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Prune.h +/// +/// @brief Defined various multi-threaded utility functions for trees +/// +/// @author Ken Museth + +#ifndef OPENVDB_TOOLS_PRUNE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_PRUNE_HAS_BEEN_INCLUDED + +#include // for isNegative and negative +#include +#include +#include // for std::nth_element() +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Reduce the memory footprint of a @a tree by replacing with tiles +/// any nodes whose values are all the same (optionally to within a tolerance) +/// and have the same active state. +/// +/// @note For trees with non-boolean values a child node with (approximately) +/// constant values are replaced with a tile value corresponding to the median +/// of the values in said child node. +/// +/// @param tree the tree to be pruned +/// @param tolerance tolerance within which values are considered to be equal +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +template +inline void +prune(TreeT& tree, + typename TreeT::ValueType tolerance = zeroVal(), + bool threaded = true, + size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing with tiles +/// any non-leaf nodes whose values are all the same (optionally to within a tolerance) +/// and have the same active state. +/// +/// @param tree the tree to be pruned +/// @param tolerance tolerance within which values are considered to be equal +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +template +inline void +pruneTiles(TreeT& tree, + typename TreeT::ValueType tolerance = zeroVal(), + bool threaded = true, + size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing with +/// background tiles any nodes whose values are all inactive. +/// +/// @param tree the tree to be pruned +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +template +inline void +pruneInactive(TreeT& tree, bool threaded = true, size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing any nodes +/// whose values are all inactive with tiles of the given @a value. +/// +/// @param tree the tree to be pruned +/// @param value value assigned to inactive tiles created during pruning +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +template +inline void +pruneInactiveWithValue( + TreeT& tree, + const typename TreeT::ValueType& value, + bool threaded = true, + size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing nodes +/// whose values are all inactive with inactive tiles having a value equal to +/// the first value encountered in the (inactive) child. +/// @details This method is faster than tolerance-based prune and +/// useful for narrow-band level set applications where inactive +/// values are limited to either an inside or an outside value. +/// +/// @param tree the tree to be pruned +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +/// +/// @throw ValueError if the background of the @a tree is negative (as defined by math::isNegative) +template +inline void +pruneLevelSet(TreeT& tree, + bool threaded = true, + size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing nodes whose voxel values +/// are all inactive with inactive tiles having the value -| @a insideWidth | +/// if the voxel values are negative and | @a outsideWidth | otherwise. +/// +/// @details This method is faster than tolerance-based prune and +/// useful for narrow-band level set applications where inactive +/// values are limited to either an inside or an outside value. +/// +/// @param tree the tree to be pruned +/// @param outsideWidth the width of the outside of the narrow band +/// @param insideWidth the width of the inside of the narrow band +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +/// +/// @throw ValueError if @a outsideWidth is negative or @a insideWidth is +/// not negative (as defined by math::isNegative). +template +inline void +pruneLevelSet(TreeT& tree, + const typename TreeT::ValueType& outsideWidth, + const typename TreeT::ValueType& insideWidth, + bool threaded = true, + size_t grainSize = 1); + + +//////////////////////////////////////////////// + + +template +class InactivePruneOp +{ +public: + using ValueT = typename TreeT::ValueType; + using RootT = typename TreeT::RootNodeType; + using LeafT = typename TreeT::LeafNodeType; + static_assert(RootT::LEVEL > TerminationLevel, "TerminationLevel out of range"); + + InactivePruneOp(TreeT& tree) : mValue(tree.background()) + { + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + + InactivePruneOp(TreeT& tree, const ValueT& v) : mValue(v) + { + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + + // Nothing to do at the leaf node level + void operator()(LeafT&) const {} + + // Prune the child nodes of the internal nodes + template + void operator()(NodeT& node) const + { + if (NodeT::LEVEL > TerminationLevel) { + for (typename NodeT::ChildOnIter it=node.beginChildOn(); it; ++it) { + if (it->isInactive()) node.addTile(it.pos(), mValue, false); + } + } + } + + // Prune the child nodes of the root node + void operator()(RootT& root) const + { + for (typename RootT::ChildOnIter it = root.beginChildOn(); it; ++it) { + if (it->isInactive()) root.addTile(it.getCoord(), mValue, false); + } + root.eraseBackgroundTiles(); + } + +private: + const ValueT mValue; +};// InactivePruneOp + + +template +class TolerancePruneOp +{ +public: + using ValueT = typename TreeT::ValueType; + using RootT = typename TreeT::RootNodeType; + using LeafT = typename TreeT::LeafNodeType; + static_assert(RootT::LEVEL > TerminationLevel, "TerminationLevel out of range"); + + TolerancePruneOp(TreeT& tree, const ValueT& tol) : mTolerance(tol) + { + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + + // Prune the child nodes of the root node + inline void operator()(RootT& root) const + { + ValueT value; + bool state; + for (typename RootT::ChildOnIter it = root.beginChildOn(); it; ++it) { + if (this->isConstant(*it, value, state)) root.addTile(it.getCoord(), value, state); + } + root.eraseBackgroundTiles(); + } + + // Prune the child nodes of the internal nodes + template + inline void operator()(NodeT& node) const + { + if (NodeT::LEVEL > TerminationLevel) { + ValueT value; + bool state; + for (typename NodeT::ChildOnIter it=node.beginChildOn(); it; ++it) { + if (this->isConstant(*it, value, state)) node.addTile(it.pos(), value, state); + } + } + } + + // Nothing to do at the leaf node level + inline void operator()(LeafT&) const {} + +private: + // Private method specialized for leaf nodes + inline ValueT median(LeafT& leaf) const {return leaf.medianAll(leaf.buffer().data());} + + // Private method for internal nodes + template + inline typename NodeT::ValueType median(NodeT& node) const + { + using UnionT = typename NodeT::UnionType; + UnionT* data = const_cast(node.getTable());//never do this at home kids :) + static const size_t midpoint = (NodeT::NUM_VALUES - 1) >> 1; + auto op = [](const UnionT& a, const UnionT& b){return a.getValue() < b.getValue();}; + std::nth_element(data, data + midpoint, data + NodeT::NUM_VALUES, op); + return data[midpoint].getValue(); + } + + // Specialization to nodes templated on booleans values + template + inline + typename std::enable_if::value, bool>::type + isConstant(NodeT& node, bool& value, bool& state) const + { + return node.isConstant(value, state, mTolerance); + } + + // Nodes templated on non-boolean values + template + inline + typename std::enable_if::value, bool>::type + isConstant(NodeT& node, ValueT& value, bool& state) const + { + ValueT tmp; + const bool test = node.isConstant(value, tmp, state, mTolerance); + if (test) value = this->median(node); + return test; + } + + const ValueT mTolerance; +};// TolerancePruneOp + + +template +class LevelSetPruneOp +{ +public: + using ValueT = typename TreeT::ValueType; + using RootT = typename TreeT::RootNodeType; + using LeafT = typename TreeT::LeafNodeType; + static_assert(RootT::LEVEL > TerminationLevel, "TerminationLevel out of range"); + + LevelSetPruneOp(TreeT& tree) + : mOutside(tree.background()) + , mInside(math::negative(mOutside)) + { + if (math::isNegative(mOutside)) { + OPENVDB_THROW(ValueError, + "LevelSetPruneOp: the background value cannot be negative!"); + } + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + + LevelSetPruneOp(TreeT& tree, const ValueT& outside, const ValueT& inside) + : mOutside(outside) + , mInside(inside) + { + if (math::isNegative(mOutside)) { + OPENVDB_THROW(ValueError, + "LevelSetPruneOp: the outside value cannot be negative!"); + } + if (!math::isNegative(mInside)) { + OPENVDB_THROW(ValueError, + "LevelSetPruneOp: the inside value must be negative!"); + } + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + + // Nothing to do at the leaf node level + void operator()(LeafT&) const {} + + // Prune the child nodes of the internal nodes + template + void operator()(NodeT& node) const + { + if (NodeT::LEVEL > TerminationLevel) { + for (typename NodeT::ChildOnIter it=node.beginChildOn(); it; ++it) { + if (it->isInactive()) node.addTile(it.pos(), this->getTileValue(it), false); + } + } + } + + // Prune the child nodes of the root node + void operator()(RootT& root) const + { + for (typename RootT::ChildOnIter it = root.beginChildOn(); it; ++it) { + if (it->isInactive()) root.addTile(it.getCoord(), this->getTileValue(it), false); + } + root.eraseBackgroundTiles(); + } + +private: + template + inline ValueT getTileValue(const IterT& iter) const + { + return math::isNegative(iter->getFirstValue()) ? mInside : mOutside; + } + + const ValueT mOutside, mInside; +};// LevelSetPruneOp + + +template +inline void +prune(TreeT& tree, typename TreeT::ValueType tol, bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + TolerancePruneOp op(tree, tol); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneTiles(TreeT& tree, typename TreeT::ValueType tol, bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + TolerancePruneOp op(tree, tol); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneInactive(TreeT& tree, bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + InactivePruneOp op(tree); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneInactiveWithValue(TreeT& tree, const typename TreeT::ValueType& v, + bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + InactivePruneOp op(tree, v); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneLevelSet(TreeT& tree, + const typename TreeT::ValueType& outside, + const typename TreeT::ValueType& inside, + bool threaded, + size_t grainSize) +{ + tree::NodeManager nodes(tree); + LevelSetPruneOp op(tree, outside, inside); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneLevelSet(TreeT& tree, bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + LevelSetPruneOp op(tree); + nodes.foreachBottomUp(op, threaded, grainSize); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_PRUNE_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/RayIntersector.h b/openvdb/tools/RayIntersector.h new file mode 100644 index 00000000..282d99aa --- /dev/null +++ b/openvdb/tools/RayIntersector.h @@ -0,0 +1,671 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +/// +/// @file RayIntersector.h +/// +/// @author Ken Museth +/// +/// @brief Accelerated intersection of a ray with a narrow-band level +/// set or a generic (e.g. density) volume. This will of course be +/// useful for respectively surface and volume rendering. +/// +/// @details This file defines two main classes, +/// LevelSetRayIntersector and VolumeRayIntersector, as well as the +/// three support classes LevelSetHDDA, VolumeHDDA and LinearSearchImpl. +/// The LevelSetRayIntersector is templated on the LinearSearchImpl class +/// and calls instances of the LevelSetHDDA class. The reason to split +/// level set ray intersection into three classes is twofold. First +/// LevelSetRayIntersector defines the public API for client code and +/// LinearSearchImpl defines the actual algorithm used for the +/// ray level-set intersection. In other words this design will allow +/// for the public API to be fixed while the intersection algorithm +/// can change without resolving to (slow) virtual methods. Second, +/// LevelSetHDDA, which implements a hierarchical Differential Digital +/// Analyzer, relies on partial template specialization, so it has to +/// be a standalone class (as opposed to a member class of +/// LevelSetRayIntersector). The VolumeRayIntersector is conceptually +/// much simpler than the LevelSetRayIntersector, and hence it only +/// depends on VolumeHDDA that implements the hierarchical +/// Differential Digital Analyzer. + + +#ifndef OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include "Morphology.h" +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +// Helper class that implements the actual search of the zero-crossing +// of the level set along the direction of a ray. This particular +// implementation uses iterative linear search. +template +class LinearSearchImpl; + + +///////////////////////////////////// LevelSetRayIntersector ///////////////////////////////////// + + +/// @brief This class provides the public API for intersecting a ray +/// with a narrow-band level set. +/// +/// @details It wraps a SearchImplT with a simple public API and +/// performs the actual hierarchical tree node and voxel traversal. +/// +/// @warning Use the (default) copy-constructor to make sure each +/// computational thread has their own instance of this class. This is +/// important since the SearchImplT contains a ValueAccessor that is +/// not thread-safe. However copying is very efficient. +/// +/// @see tools/RayTracer.h for examples of intended usage. +/// +/// @todo Add TrilinearSearchImpl, as an alternative to LinearSearchImpl, +/// that performs analytical 3D trilinear intersection tests, i.e., solves +/// cubic equations. This is slower but also more accurate than the 1D +/// linear interpolation in LinearSearchImpl. +template, + int NodeLevel = GridT::TreeType::RootNodeType::ChildNodeType::LEVEL, + typename RayT = math::Ray > +class LevelSetRayIntersector +{ +public: + using GridType = GridT; + using RayType = RayT; + using RealType = typename RayT::RealType; + using Vec3Type = typename RayT::Vec3T; + using ValueT = typename GridT::ValueType; + using TreeT = typename GridT::TreeType; + + static_assert(NodeLevel >= -1 && NodeLevel < int(TreeT::DEPTH)-1, "NodeLevel out of range"); + static_assert(std::is_floating_point::value, + "level set grids must have scalar, floating-point value types"); + + /// @brief Constructor + /// @param grid level set grid to intersect rays against. + /// @param isoValue optional iso-value for the ray-intersection. + LevelSetRayIntersector(const GridT& grid, const ValueT& isoValue = zeroVal()) + : mTester(grid, isoValue) + { + if (!grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "LevelSetRayIntersector only supports uniform voxels!"); + } + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "LevelSetRayIntersector only supports level sets!" + "\nUse Grid::setGridClass(openvdb::GRID_LEVEL_SET)"); + } + } + + /// @brief Return the iso-value used for ray-intersections + const ValueT& getIsoValue() const { return mTester.getIsoValue(); } + + /// @brief Return @c true if the index-space ray intersects the level set. + /// @param iRay ray represented in index space. + bool intersectsIS(const RayType& iRay) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + return math::LevelSetHDDA::test(mTester); + } + + /// @brief Return @c true if the index-space ray intersects the level set + /// @param iRay ray represented in index space. + /// @param iTime if an intersection was found it is assigned the time of the + /// intersection along the index ray. + bool intersectsIS(const RayType& iRay, RealType &iTime) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + iTime = mTester.getIndexTime(); + return math::LevelSetHDDA::test(mTester); + } + + /// @brief Return @c true if the index-space ray intersects the level set. + /// @param iRay ray represented in index space. + /// @param xyz if an intersection was found it is assigned the + /// intersection point in index space, otherwise it is unchanged. + bool intersectsIS(const RayType& iRay, Vec3Type& xyz) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getIndexPos(xyz); + return true; + } + + /// @brief Return @c true if the index-space ray intersects the level set. + /// @param iRay ray represented in index space. + /// @param xyz if an intersection was found it is assigned the + /// intersection point in index space, otherwise it is unchanged. + /// @param iTime if an intersection was found it is assigned the time of the + /// intersection along the index ray. + bool intersectsIS(const RayType& iRay, Vec3Type& xyz, RealType &iTime) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getIndexPos(xyz); + iTime = mTester.getIndexTime(); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + bool intersectsWS(const RayType& wRay) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + return math::LevelSetHDDA::test(mTester); + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param wTime if an intersection was found it is assigned the time of the + /// intersection along the world ray. + bool intersectsWS(const RayType& wRay, RealType &wTime) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + wTime = mTester.getWorldTime(); + return math::LevelSetHDDA::test(mTester); + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged + bool intersectsWS(const RayType& wRay, Vec3Type& world) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getWorldPos(world); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged. + /// @param wTime if an intersection was found it is assigned the time of the + /// intersection along the world ray. + bool intersectsWS(const RayType& wRay, Vec3Type& world, RealType &wTime) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getWorldPos(world); + wTime = mTester.getWorldTime(); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged. + /// @param normal if an intersection was found it is assigned the normal + /// of the level set surface in world space, otherwise it is unchanged. + bool intersectsWS(const RayType& wRay, Vec3Type& world, Vec3Type& normal) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getWorldPosAndNml(world, normal); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged. + /// @param normal if an intersection was found it is assigned the normal + /// of the level set surface in world space, otherwise it is unchanged. + /// @param wTime if an intersection was found it is assigned the time of the + /// intersection along the world ray. + bool intersectsWS(const RayType& wRay, Vec3Type& world, Vec3Type& normal, RealType &wTime) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getWorldPosAndNml(world, normal); + wTime = mTester.getWorldTime(); + return true; + } + +private: + + mutable SearchImplT mTester; + +};// LevelSetRayIntersector + + +////////////////////////////////////// VolumeRayIntersector ////////////////////////////////////// + + +/// @brief This class provides the public API for intersecting a ray +/// with a generic (e.g. density) volume. +/// @details Internally it performs the actual hierarchical tree node traversal. +/// @warning Use the (default) copy-constructor to make sure each +/// computational thread has their own instance of this class. This is +/// important since it contains a ValueAccessor that is +/// not thread-safe and a CoordBBox of the active voxels that should +/// not be re-computed for each thread. However copying is very efficient. +/// @par Example: +/// @code +/// // Create an instance for the master thread +/// VolumeRayIntersector inter(grid); +/// // For each additional thread use the copy constructor. This +/// // amortizes the overhead of computing the bbox of the active voxels! +/// VolumeRayIntersector inter2(inter); +/// // Before each ray-traversal set the index ray. +/// iter.setIndexRay(ray); +/// // or world ray +/// iter.setWorldRay(ray); +/// // Now you can begin the ray-marching using consecutive calls to VolumeRayIntersector::march +/// double t0=0, t1=0;// note the entry and exit times are with respect to the INDEX ray +/// while ( inter.march(t0, t1) ) { +/// // perform line-integration between t0 and t1 +/// }} +/// @endcode +template > +class VolumeRayIntersector +{ +public: + using GridType = GridT; + using RayType = RayT; + using RealType = typename RayT::RealType; + using RootType = typename GridT::TreeType::RootNodeType; + using TreeT = tree::Tree::Type>; + + static_assert(NodeLevel >= 0 && NodeLevel < int(TreeT::DEPTH)-1, "NodeLevel out of range"); + + /// @brief Grid constructor + /// @param grid Generic grid to intersect rays against. + /// @param dilationCount The number of voxel dilations performed + /// on (a boolean copy of) the input grid. This allows the + /// intersector to account for the size of interpolation kernels + /// in client code. + /// @throw RuntimeError if the voxels of the grid are not uniform + /// or the grid is empty. + VolumeRayIntersector(const GridT& grid, int dilationCount = 0) + : mIsMaster(true) + , mTree(new TreeT(grid.tree(), false, TopologyCopy())) + , mGrid(&grid) + , mAccessor(*mTree) + { + if (!grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "VolumeRayIntersector only supports uniform voxels!"); + } + if ( grid.empty() ) { + OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); + } + + // Dilate active voxels to better account for the size of interpolation kernels + tools::dilateVoxels(*mTree, dilationCount); + + mTree->root().evalActiveBoundingBox(mBBox, /*visit individual voxels*/false); + + mBBox.max().offset(1);//padding so the bbox of a node becomes (origin,origin + node_dim) + } + + /// @brief Grid and BBox constructor + /// @param grid Generic grid to intersect rays against. + /// @param bbox The axis-aligned bounding-box in the index space of the grid. + /// @warning It is assumed that bbox = (min, min + dim) where min denotes + /// to the smallest grid coordinates and dim are the integer length of the bbox. + /// @throw RuntimeError if the voxels of the grid are not uniform + /// or the grid is empty. + VolumeRayIntersector(const GridT& grid, const math::CoordBBox& bbox) + : mIsMaster(true) + , mTree(new TreeT(grid.tree(), false, TopologyCopy())) + , mGrid(&grid) + , mAccessor(*mTree) + , mBBox(bbox) + { + if (!grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "VolumeRayIntersector only supports uniform voxels!"); + } + if ( grid.empty() ) { + OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); + } + } + + /// @brief Shallow copy constructor + /// @warning This copy constructor creates shallow copies of data + /// members of the instance passed as the argument. For + /// performance reasons we are not using shared pointers (their + /// mutex-lock impairs multi-threading). + VolumeRayIntersector(const VolumeRayIntersector& other) + : mIsMaster(false) + , mTree(other.mTree)//shallow copy + , mGrid(other.mGrid)//shallow copy + , mAccessor(*mTree)//initialize new (vs deep copy) + , mRay(other.mRay)//deep copy + , mTmax(other.mTmax)//deep copy + , mBBox(other.mBBox)//deep copy + { + } + + /// @brief Destructor + ~VolumeRayIntersector() { if (mIsMaster) delete mTree; } + + /// @brief Return @c false if the index ray misses the bbox of the grid. + /// @param iRay Ray represented in index space. + /// @warning Call this method (or setWorldRay) before the ray + /// traversal starts and use the return value to decide if further + /// marching is required. + inline bool setIndexRay(const RayT& iRay) + { + mRay = iRay; + const bool hit = mRay.clip(mBBox); + if (hit) mTmax = mRay.t1(); + return hit; + } + + /// @brief Return @c false if the world ray misses the bbox of the grid. + /// @param wRay Ray represented in world space. + /// @warning Call this method (or setIndexRay) before the ray + /// traversal starts and use the return value to decide if further + /// marching is required. + /// @details Since hit times are computed with respect to the ray + /// represented in index space of the current grid, it is + /// recommended that either the client code uses getIndexPos to + /// compute index position from hit times or alternatively keeps + /// an instance of the index ray and instead uses setIndexRay to + /// initialize the ray. + inline bool setWorldRay(const RayT& wRay) + { + return this->setIndexRay(wRay.worldToIndex(*mGrid)); + } + + inline typename RayT::TimeSpan march() + { + const typename RayT::TimeSpan t = mHDDA.march(mRay, mAccessor); + if (t.t1>0) mRay.setTimes(t.t1 + math::Delta::value(), mTmax); + return t; + } + + /// @brief Return @c true if the ray intersects active values, + /// i.e. either active voxels or tiles. Only when a hit is + /// detected are t0 and t1 updated with the corresponding entry + /// and exit times along the INDEX ray! + /// @note Note that t0 and t1 are only resolved at the node level + /// (e.g. a LeafNode with active voxels) as opposed to the individual + /// active voxels. + /// @param t0 If the return value > 0 this is the time of the + /// first hit of an active tile or leaf. + /// @param t1 If the return value > t0 this is the time of the + /// first hit (> t0) of an inactive tile or exit point of the + /// BBOX for the leaf nodes. + /// @warning t0 and t1 are computed with respect to the ray represented in + /// index space of the current grid, not world space! + inline bool march(RealType& t0, RealType& t1) + { + const typename RayT::TimeSpan t = this->march(); + t.get(t0, t1); + return t.valid(); + } + + /// @brief Generates a list of hits along the ray. + /// + /// @param list List of hits represented as time spans. + /// + /// @note ListType is a list of RayType::TimeSpan and is required to + /// have the two methods: clear() and push_back(). Thus, it could + /// be std::vector or + /// std::deque. + template + inline void hits(ListType& list) + { + mHDDA.hits(mRay, mAccessor, list); + } + + /// @brief Return the floating-point index position along the + /// current index ray at the specified time. + inline Vec3R getIndexPos(RealType time) const { return mRay(time); } + + /// @brief Return the floating-point world position along the + /// current index ray at the specified time. + inline Vec3R getWorldPos(RealType time) const { return mGrid->indexToWorld(mRay(time)); } + + inline RealType getWorldTime(RealType time) const + { + return time*mGrid->transform().baseMap()->applyJacobian(mRay.dir()).length(); + } + + /// @brief Return a const reference to the input grid. + const GridT& grid() const { return *mGrid; } + + /// @brief Return a const reference to the (potentially dilated) + /// bool tree used to accelerate the ray marching. + const TreeT& tree() const { return *mTree; } + + /// @brief Return a const reference to the BBOX of the grid + const math::CoordBBox& bbox() const { return mBBox; } + + /// @brief Print bbox, statistics, memory usage and other information. + /// @param os a stream to which to write textual information + /// @param verboseLevel 1: print bbox only; 2: include boolean tree + /// statistics; 3: include memory usage + void print(std::ostream& os = std::cout, int verboseLevel = 1) + { + if (verboseLevel>0) { + os << "BBox: " << mBBox << std::endl; + if (verboseLevel==2) { + mTree->print(os, 1); + } else if (verboseLevel>2) { + mTree->print(os, 2); + } + } + } + +private: + using AccessorT = typename tree::ValueAccessor; + + const bool mIsMaster; + TreeT* mTree; + const GridT* mGrid; + AccessorT mAccessor; + RayT mRay; + RealType mTmax; + math::CoordBBox mBBox; + math::VolumeHDDA mHDDA; + +};// VolumeRayIntersector + + +//////////////////////////////////////// LinearSearchImpl //////////////////////////////////////// + + +/// @brief Implements linear iterative search for an iso-value of +/// the level set along the direction of the ray. +/// +/// @note Since this class is used internally in +/// LevelSetRayIntersector (define above) and LevelSetHDDA (defined below) +/// client code should never interact directly with its API. This also +/// explains why we are not concerned with the fact that several of +/// its methods are unsafe to call unless roots were already detected. +/// +/// @details It is approximate due to the limited number of iterations +/// which can can be defined with a template parameter. However the default value +/// has proven surprisingly accurate and fast. In fact more iterations +/// are not guaranteed to give significantly better results. +/// +/// @warning Since the root-searching algorithm is approximate +/// (first-order) it is possible to miss intersections if the +/// iso-value is too close to the inside or outside of the narrow +/// band (typically a distance less than a voxel unit). +/// +/// @warning Since this class internally stores a ValueAccessor it is NOT thread-safe, +/// so make sure to give each thread its own instance. This of course also means that +/// the cost of allocating an instance should (if possible) be amortized over +/// as many ray intersections as possible. +template +class LinearSearchImpl +{ +public: + using RayT = math::Ray; + using VecT = math::Vec3; + using ValueT = typename GridT::ValueType; + using AccessorT = typename GridT::ConstAccessor; + using StencilT = math::BoxStencil; + + /// @brief Constructor from a grid. + /// @throw RunTimeError if the grid is empty. + /// @throw ValueError if the isoValue is not inside the narrow-band. + LinearSearchImpl(const GridT& grid, const ValueT& isoValue = zeroVal()) + : mStencil(grid), + mIsoValue(isoValue), + mMinValue(isoValue - ValueT(2 * grid.voxelSize()[0])), + mMaxValue(isoValue + ValueT(2 * grid.voxelSize()[0])) + { + if ( grid.empty() ) { + OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); + } + if (mIsoValue<= -grid.background() || + mIsoValue>= grid.background() ){ + OPENVDB_THROW(ValueError, "The iso-value must be inside the narrow-band!"); + } + grid.tree().root().evalActiveBoundingBox(mBBox, /*visit individual voxels*/false); + } + + /// @brief Return the iso-value used for ray-intersections + const ValueT& getIsoValue() const { return mIsoValue; } + + /// @brief Return @c false if the ray misses the bbox of the grid. + /// @param iRay Ray represented in index space. + /// @warning Call this method before the ray traversal starts. + inline bool setIndexRay(const RayT& iRay) + { + mRay = iRay; + return mRay.clip(mBBox);//did it hit the bbox + } + + /// @brief Return @c false if the ray misses the bbox of the grid. + /// @param wRay Ray represented in world space. + /// @warning Call this method before the ray traversal starts. + inline bool setWorldRay(const RayT& wRay) + { + mRay = wRay.worldToIndex(mStencil.grid()); + return mRay.clip(mBBox);//did it hit the bbox + } + + /// @brief Get the intersection point in index space. + /// @param xyz The position in index space of the intersection. + inline void getIndexPos(VecT& xyz) const { xyz = mRay(mTime); } + + /// @brief Get the intersection point in world space. + /// @param xyz The position in world space of the intersection. + inline void getWorldPos(VecT& xyz) const { xyz = mStencil.grid().indexToWorld(mRay(mTime)); } + + /// @brief Get the intersection point and normal in world space + /// @param xyz The position in world space of the intersection. + /// @param nml The surface normal in world space of the intersection. + inline void getWorldPosAndNml(VecT& xyz, VecT& nml) + { + this->getIndexPos(xyz); + mStencil.moveTo(xyz); + nml = mStencil.gradient(xyz); + nml.normalize(); + xyz = mStencil.grid().indexToWorld(xyz); + } + + /// @brief Return the time of intersection along the index ray. + inline RealT getIndexTime() const { return mTime; } + + /// @brief Return the time of intersection along the world ray. + inline RealT getWorldTime() const + { + return mTime*mStencil.grid().transform().baseMap()->applyJacobian(mRay.dir()).length(); + } + +private: + + /// @brief Initiate the local voxel intersection test. + /// @warning Make sure to call this method before the local voxel intersection test. + inline void init(RealT t0) + { + mT[0] = t0; + mV[0] = static_cast(this->interpValue(t0)); + } + + inline void setRange(RealT t0, RealT t1) { mRay.setTimes(t0, t1); } + + /// @brief Return a const reference to the ray. + inline const RayT& ray() const { return mRay; } + + /// @brief Return true if a node of the specified type exists at ijk. + template + inline bool hasNode(const Coord& ijk) + { + return mStencil.accessor().template probeConstNode(ijk) != nullptr; + } + + /// @brief Return @c true if an intersection is detected. + /// @param ijk Grid coordinate of the node origin or voxel being tested. + /// @param time Time along the index ray being tested. + /// @warning Only if an intersection is detected is it safe to + /// call getIndexPos, getWorldPos and getWorldPosAndNml! + inline bool operator()(const Coord& ijk, RealT time) + { + ValueT V; + if (mStencil.accessor().probeValue(ijk, V) &&//within narrow band + V>mMinValue && V(this->interpValue(time)); + if (math::ZeroCrossing(mV[0], mV[1])) { + mTime = this->interpTime(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + for (int n=0; Iterations>0 && n(this->interpValue(mTime)); + const int m = math::ZeroCrossing(mV[0], V) ? 1 : 0; + mV[m] = V; + mT[m] = mTime; + mTime = this->interpTime(); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + return true; + } + mT[0] = mT[1]; + mV[0] = mV[1]; + } + return false; + } + + inline RealT interpTime() + { + assert( math::isApproxLarger(mT[1], mT[0], RealT(1e-6) ) ); + return mT[0]+(mT[1]-mT[0])*mV[0]/(mV[0]-mV[1]); + } + + inline RealT interpValue(RealT time) + { + const VecT pos = mRay(time); + mStencil.moveTo(pos); + return mStencil.interpolation(pos) - mIsoValue; + } + + template friend struct math::LevelSetHDDA; + + RayT mRay; + StencilT mStencil; + RealT mTime;//time of intersection + ValueT mV[2]; + RealT mT[2]; + const ValueT mIsoValue, mMinValue, mMaxValue; + math::CoordBBox mBBox; +};// LinearSearchImpl + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/RayTracer.h b/openvdb/tools/RayTracer.h new file mode 100644 index 00000000..e56f5cfa --- /dev/null +++ b/openvdb/tools/RayTracer.h @@ -0,0 +1,1094 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file RayTracer.h +/// +/// @author Ken Museth +/// +/// @brief Defines two simple but multithreaded renders, a level-set +/// ray tracer and a volume render. To support these renders we also define +/// perspective and orthographic cameras (both designed to mimic a Houdini camera), +/// a Film class and some rather naive shaders. +/// +/// @note These classes are included mainly as reference implementations for +/// ray-tracing of OpenVDB volumes. In other words they are not intended for +/// production-quality rendering, but could be used for fast pre-visualization +/// or as a starting point for a more serious render. + +#ifndef OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENVDB_TOOLS_RAYTRACER_USE_EXR +#include +#include +#include +#include +#include +#endif + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +// Forward declarations +class BaseCamera; +class BaseShader; + +/// @brief Ray-trace a volume. +template +inline void rayTrace(const GridT&, + const BaseShader&, + BaseCamera&, + size_t pixelSamples = 1, + unsigned int seed = 0, + bool threaded = true); + +/// @brief Ray-trace a volume using a given ray intersector. +template +inline void rayTrace(const GridT&, + const IntersectorT&, + const BaseShader&, + BaseCamera&, + size_t pixelSamples = 1, + unsigned int seed = 0, + bool threaded = true); + + +///////////////////////////////LEVEL SET RAY TRACER /////////////////////////////////////// + +/// @brief A (very) simple multithreaded ray tracer specifically for narrow-band level sets. +/// @details Included primarily as a reference implementation. +template > +class LevelSetRayTracer +{ +public: + using GridType = GridT; + using Vec3Type = typename IntersectorT::Vec3Type; + using RayType = typename IntersectorT::RayType; + + /// @brief Constructor based on an instance of the grid to be rendered. + LevelSetRayTracer(const GridT& grid, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples = 1, + unsigned int seed = 0); + + /// @brief Constructor based on an instance of the intersector + /// performing the ray-intersections. + LevelSetRayTracer(const IntersectorT& inter, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples = 1, + unsigned int seed = 0); + + /// @brief Copy constructor + LevelSetRayTracer(const LevelSetRayTracer& other); + + /// @brief Destructor + ~LevelSetRayTracer(); + + /// @brief Set the level set grid to be ray-traced + void setGrid(const GridT& grid); + + /// @brief Set the intersector that performs the actual + /// intersection of the rays against the narrow-band level set. + void setIntersector(const IntersectorT& inter); + + /// @brief Set the shader derived from the abstract BaseShader class. + /// + /// @note The shader is not assumed to be thread-safe so each + /// thread will get its only deep copy. For instance it could + /// contains a ValueAccessor into another grid with auxiliary + /// shading information. Thus, make sure it is relatively + /// light-weight and efficient to copy (which is the case for ValueAccesors). + void setShader(const BaseShader& shader); + + /// @brief Set the camera derived from the abstract BaseCamera class. + void setCamera(BaseCamera& camera); + + /// @brief Set the number of pixel samples and the seed for + /// jittered sub-rays. A value larger than one implies + /// anti-aliasing by jittered super-sampling. + /// @throw ValueError if pixelSamples is equal to zero. + void setPixelSamples(size_t pixelSamples, unsigned int seed = 0); + + /// @brief Perform the actual (potentially multithreaded) ray-tracing. + void render(bool threaded = true) const; + + /// @brief Public method required by tbb::parallel_for. + /// @warning Never call it directly. + void operator()(const tbb::blocked_range& range) const; + +private: + const bool mIsMaster; + double* mRand; + IntersectorT mInter; + std::unique_ptr mShader; + BaseCamera* mCamera; + size_t mSubPixels; +};// LevelSetRayTracer + + +///////////////////////////////VOLUME RENDER /////////////////////////////////////// + +/// @brief A (very) simple multithreaded volume render specifically for scalar density. +/// @details Included primarily as a reference implementation. +/// @note It will only compile if the IntersectorT is templated on a Grid with a +/// floating-point voxel type. +template +class VolumeRender +{ +public: + + using GridType = typename IntersectorT::GridType; + using RayType = typename IntersectorT::RayType; + using ValueType = typename GridType::ValueType; + using AccessorType = typename GridType::ConstAccessor; + using SamplerType = tools::GridSampler; + static_assert(std::is_floating_point::value, + "VolumeRender requires a floating-point-valued grid"); + + /// @brief Constructor taking an intersector and a base camera. + VolumeRender(const IntersectorT& inter, BaseCamera& camera); + + /// @brief Copy constructor which creates a thread-safe clone + VolumeRender(const VolumeRender& other); + + /// @brief Perform the actual (potentially multithreaded) volume rendering. + void render(bool threaded=true) const; + + /// @brief Set the camera derived from the abstract BaseCamera class. + void setCamera(BaseCamera& camera) { mCamera = &camera; } + + /// @brief Set the intersector that performs the actual + /// intersection of the rays against the volume. + void setIntersector(const IntersectorT& inter); + + /// @brief Set the vector components of a directional light source + /// @throw ArithmeticError if input is a null vector. + void setLightDir(Real x, Real y, Real z) { mLightDir = Vec3R(x,y,z).unit(); } + + /// @brief Set the color of the directional light source. + void setLightColor(Real r, Real g, Real b) { mLightColor = Vec3R(r,g,b); } + + /// @brief Set the integration step-size in voxel units for the primay ray. + void setPrimaryStep(Real primaryStep) { mPrimaryStep = primaryStep; } + + /// @brief Set the integration step-size in voxel units for the primay ray. + void setShadowStep(Real shadowStep) { mShadowStep = shadowStep; } + + /// @brief Set Scattering coefficients. + void setScattering(Real x, Real y, Real z) { mScattering = Vec3R(x,y,z); } + + /// @brief Set absorption coefficients. + void setAbsorption(Real x, Real y, Real z) { mAbsorption = Vec3R(x,y,z); } + + /// @brief Set parameter that imitates multi-scattering. A value + /// of zero implies no multi-scattering. + void setLightGain(Real gain) { mLightGain = gain; } + + /// @brief Set the cut-off value for density and transmittance. + void setCutOff(Real cutOff) { mCutOff = cutOff; } + + /// @brief Print parameters, statistics, memory usage and other information. + /// @param os a stream to which to write textual information + /// @param verboseLevel 1: print parameters only; 2: include grid + /// statistics; 3: include memory usage + void print(std::ostream& os = std::cout, int verboseLevel = 1); + + /// @brief Public method required by tbb::parallel_for. + /// @warning Never call it directly. + void operator()(const tbb::blocked_range& range) const; + +private: + + AccessorType mAccessor; + BaseCamera* mCamera; + std::unique_ptr mPrimary, mShadow; + Real mPrimaryStep, mShadowStep, mCutOff, mLightGain; + Vec3R mLightDir, mLightColor, mAbsorption, mScattering; +};//VolumeRender + +//////////////////////////////////////// FILM //////////////////////////////////////// + +/// @brief A simple class that allows for concurrent writes to pixels in an image, +/// background initialization of the image, and PPM or EXR file output. +class Film +{ +public: + /// @brief Floating-point RGBA components in the range [0, 1]. + /// @details This is our preferred representation for color processing. + struct RGBA + { + using ValueT = float; + + RGBA() : r(0), g(0), b(0), a(1) {} + explicit RGBA(ValueT intensity) : r(intensity), g(intensity), b(intensity), a(1) {} + RGBA(ValueT _r, ValueT _g, ValueT _b, ValueT _a = static_cast(1.0)): + r(_r), g(_g), b(_b), a(_a) + {} + RGBA(double _r, double _g, double _b, double _a = 1.0) + : r(static_cast(_r)) + , g(static_cast(_g)) + , b(static_cast(_b)) + , a(static_cast(_a)) + {} + + RGBA operator* (ValueT scale) const { return RGBA(r*scale, g*scale, b*scale);} + RGBA operator+ (const RGBA& rhs) const { return RGBA(r+rhs.r, g+rhs.g, b+rhs.b);} + RGBA operator* (const RGBA& rhs) const { return RGBA(r*rhs.r, g*rhs.g, b*rhs.b);} + RGBA& operator+=(const RGBA& rhs) { r+=rhs.r; g+=rhs.g; b+=rhs.b; a+=rhs.a; return *this;} + + void over(const RGBA& rhs) + { + const float s = rhs.a*(1.0f-a); + r = a*r+s*rhs.r; + g = a*g+s*rhs.g; + b = a*b+s*rhs.b; + a = a + s; + } + + ValueT r, g, b, a; + }; + + + Film(size_t width, size_t height) + : mWidth(width), mHeight(height), mSize(width*height), mPixels(new RGBA[mSize]) + { + } + Film(size_t width, size_t height, const RGBA& bg) + : mWidth(width), mHeight(height), mSize(width*height), mPixels(new RGBA[mSize]) + { + this->fill(bg); + } + + const RGBA& pixel(size_t w, size_t h) const + { + assert(w < mWidth); + assert(h < mHeight); + return mPixels[w + h*mWidth]; + } + + RGBA& pixel(size_t w, size_t h) + { + assert(w < mWidth); + assert(h < mHeight); + return mPixels[w + h*mWidth]; + } + + void fill(const RGBA& rgb=RGBA(0)) { for (size_t i=0; i buffer(new unsigned char[3*mSize]); + unsigned char *tmp = buffer.get(), *q = tmp; + RGBA* p = mPixels.get(); + size_t n = mSize; + while (n--) { + *q++ = static_cast(255.0f*(*p ).r); + *q++ = static_cast(255.0f*(*p ).g); + *q++ = static_cast(255.0f*(*p++).b); + } + + std::ofstream os(name.c_str(), std::ios_base::binary); + if (!os.is_open()) { + std::cerr << "Error opening PPM file \"" << name << "\"" << std::endl; + return; + } + + os << "P6\n" << mWidth << " " << mHeight << "\n255\n"; + os.write(reinterpret_cast(&(*tmp)), 3 * mSize * sizeof(unsigned char)); + } + +#ifdef OPENVDB_TOOLS_RAYTRACER_USE_EXR + void saveEXR(const std::string& fileName, size_t compression = 2, size_t threads = 8) + { + std::string name(fileName); + if (name.find_last_of(".") == std::string::npos) name.append(".exr"); + + if (threads>0) Imf::setGlobalThreadCount(threads); + Imf::Header header(mWidth, mHeight); + if (compression==0) header.compression() = Imf::NO_COMPRESSION; + if (compression==1) header.compression() = Imf::RLE_COMPRESSION; + if (compression>=2) header.compression() = Imf::ZIP_COMPRESSION; + header.channels().insert("R", Imf::Channel(Imf::FLOAT)); + header.channels().insert("G", Imf::Channel(Imf::FLOAT)); + header.channels().insert("B", Imf::Channel(Imf::FLOAT)); + header.channels().insert("A", Imf::Channel(Imf::FLOAT)); + + Imf::FrameBuffer framebuffer; + framebuffer.insert("R", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].r), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + framebuffer.insert("G", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].g), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + framebuffer.insert("B", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].b), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + framebuffer.insert("A", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].a), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + + Imf::OutputFile file(name.c_str(), header); + file.setFrameBuffer(framebuffer); + file.writePixels(mHeight); + } +#endif + + size_t width() const { return mWidth; } + size_t height() const { return mHeight; } + size_t numPixels() const { return mSize; } + const RGBA* pixels() const { return mPixels.get(); } + +private: + size_t mWidth, mHeight, mSize; + std::unique_ptr mPixels; +};// Film + + +//////////////////////////////////////// CAMERAS //////////////////////////////////////// + +/// Abstract base class for the perspective and orthographic cameras +class BaseCamera +{ +public: + BaseCamera(Film& film, const Vec3R& rotation, const Vec3R& translation, + double frameWidth, double nearPlane, double farPlane) + : mFilm(&film) + , mScaleWidth(frameWidth) + , mScaleHeight(frameWidth * double(film.height()) / double(film.width())) + { + assert(nearPlane > 0 && farPlane > nearPlane); + mScreenToWorld.accumPostRotation(math::X_AXIS, rotation[0] * M_PI / 180.0); + mScreenToWorld.accumPostRotation(math::Y_AXIS, rotation[1] * M_PI / 180.0); + mScreenToWorld.accumPostRotation(math::Z_AXIS, rotation[2] * M_PI / 180.0); + mScreenToWorld.accumPostTranslation(translation); + this->initRay(nearPlane, farPlane); + } + + virtual ~BaseCamera() {} + + Film::RGBA& pixel(size_t i, size_t j) { return mFilm->pixel(i, j); } + + size_t width() const { return mFilm->width(); } + size_t height() const { return mFilm->height(); } + + /// Rotate the camera so its negative z-axis points at xyz and its + /// y axis is in the plane of the xyz and up vectors. In other + /// words the camera will look at xyz and use up as the + /// horizontal direction. + void lookAt(const Vec3R& xyz, const Vec3R& up = Vec3R(0.0, 1.0, 0.0)) + { + const Vec3R orig = mScreenToWorld.applyMap(Vec3R(0.0)); + const Vec3R dir = orig - xyz; + try { + Mat4d xform = math::aim(dir, up); + xform.postTranslate(orig); + mScreenToWorld = math::AffineMap(xform); + this->initRay(mRay.t0(), mRay.t1()); + } catch (...) {} + } + + Vec3R rasterToScreen(double i, double j, double z) const + { + return Vec3R( (2 * i / double(mFilm->width()) - 1) * mScaleWidth, + (1 - 2 * j / double(mFilm->height())) * mScaleHeight, z ); + } + + /// @brief Return a Ray in world space given the pixel indices and + /// optional offsets in the range [0, 1]. An offset of 0.5 corresponds + /// to the center of the pixel. + virtual math::Ray getRay( + size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const = 0; + +protected: + void initRay(double t0, double t1) + { + mRay.setTimes(t0, t1); + mRay.setEye(mScreenToWorld.applyMap(Vec3R(0.0))); + mRay.setDir(mScreenToWorld.applyJacobian(Vec3R(0.0, 0.0, -1.0))); + } + + Film* mFilm; + double mScaleWidth, mScaleHeight; + math::Ray mRay; + math::AffineMap mScreenToWorld; +};// BaseCamera + + +class PerspectiveCamera: public BaseCamera +{ + public: + /// @brief Constructor + /// @param film film (i.e. image) defining the pixel resolution + /// @param rotation rotation in degrees of the camera in world space + /// (applied in x, y, z order) + /// @param translation translation of the camera in world-space units, + /// applied after rotation + /// @param focalLength focal length of the camera in mm + /// (the default of 50mm corresponds to Houdini's default camera) + /// @param aperture width in mm of the frame, i.e., the visible field + /// (the default 41.2136 mm corresponds to Houdini's default camera) + /// @param nearPlane depth of the near clipping plane in world-space units + /// @param farPlane depth of the far clipping plane in world-space units + /// + /// @details If no rotation or translation is provided, the camera is placed + /// at (0,0,0) in world space and points in the direction of the negative z axis. + PerspectiveCamera(Film& film, + const Vec3R& rotation = Vec3R(0.0), + const Vec3R& translation = Vec3R(0.0), + double focalLength = 50.0, + double aperture = 41.2136, + double nearPlane = 1e-3, + double farPlane = std::numeric_limits::max()) + : BaseCamera(film, rotation, translation, 0.5*aperture/focalLength, nearPlane, farPlane) + { + } + + ~PerspectiveCamera() override = default; + + /// @brief Return a Ray in world space given the pixel indices and + /// optional offsets in the range [0,1]. An offset of 0.5 corresponds + /// to the center of the pixel. + math::Ray getRay( + size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const override + { + math::Ray ray(mRay); + Vec3R dir = BaseCamera::rasterToScreen(Real(i) + iOffset, Real(j) + jOffset, -1.0); + dir = BaseCamera::mScreenToWorld.applyJacobian(dir); + dir.normalize(); + ray.scaleTimes(1.0/dir.dot(ray.dir())); + ray.setDir(dir); + return ray; + } + + /// @brief Return the horizontal field of view in degrees given a + /// focal lenth in mm and the specified aperture in mm. + static double focalLengthToFieldOfView(double length, double aperture) + { + return 360.0 / M_PI * atan(aperture/(2.0*length)); + } + /// @brief Return the focal length in mm given a horizontal field of + /// view in degrees and the specified aperture in mm. + static double fieldOfViewToFocalLength(double fov, double aperture) + { + return aperture/(2.0*(tan(fov * M_PI / 360.0))); + } +};// PerspectiveCamera + + +class OrthographicCamera: public BaseCamera +{ +public: + /// @brief Constructor + /// @param film film (i.e. image) defining the pixel resolution + /// @param rotation rotation in degrees of the camera in world space + /// (applied in x, y, z order) + /// @param translation translation of the camera in world-space units, + /// applied after rotation + /// @param frameWidth width in of the frame in world-space units + /// @param nearPlane depth of the near clipping plane in world-space units + /// @param farPlane depth of the far clipping plane in world-space units + /// + /// @details If no rotation or translation is provided, the camera is placed + /// at (0,0,0) in world space and points in the direction of the negative z axis. + OrthographicCamera(Film& film, + const Vec3R& rotation = Vec3R(0.0), + const Vec3R& translation = Vec3R(0.0), + double frameWidth = 1.0, + double nearPlane = 1e-3, + double farPlane = std::numeric_limits::max()) + : BaseCamera(film, rotation, translation, 0.5*frameWidth, nearPlane, farPlane) + { + } + ~OrthographicCamera() override = default; + + math::Ray getRay( + size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const override + { + math::Ray ray(mRay); + Vec3R eye = BaseCamera::rasterToScreen(Real(i) + iOffset, Real(j) + jOffset, 0.0); + ray.setEye(BaseCamera::mScreenToWorld.applyMap(eye)); + return ray; + } +};// OrthographicCamera + + +//////////////////////////////////////// SHADERS //////////////////////////////////////// + + +/// Abstract base class for the shaders +class BaseShader +{ +public: + using RayT = math::Ray; + BaseShader() {} + BaseShader(const BaseShader&) = default; + virtual ~BaseShader() = default; + /// @brief Defines the interface of the virtual function that returns a RGB color. + /// @param xyz World position of the intersection point. + /// @param nml Normal in world space at the intersection point. + /// @param dir Direction of the ray in world space. + virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R& nml, const Vec3R& dir) const = 0; + virtual BaseShader* copy() const = 0; +}; + + +/// @brief Shader that produces a simple matte. +/// +/// @details The color can either be constant (if GridT = +/// Film::RGBA which is the default) or defined in a separate Vec3 +/// color grid. Use SamplerType to define the order of interpolation +/// (default is zero order, i.e. closes-point). +template +class MatteShader: public BaseShader +{ +public: + MatteShader(const GridT& grid) : mAcc(grid.getAccessor()), mXform(&grid.transform()) {} + MatteShader(const MatteShader&) = default; + ~MatteShader() override = default; + Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const override + { + typename GridT::ValueType v = zeroVal(); + SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); + return Film::RGBA(v[0], v[1], v[2]); + } + BaseShader* copy() const override { return new MatteShader(*this); } + +private: + typename GridT::ConstAccessor mAcc; + const math::Transform* mXform; +}; + +// Template specialization using a constant color of the material. +template +class MatteShader: public BaseShader +{ +public: + MatteShader(const Film::RGBA& c = Film::RGBA(1.0f)): mRGBA(c) {} + MatteShader(const MatteShader&) = default; + ~MatteShader() override = default; + Film::RGBA operator()(const Vec3R&, const Vec3R&, const Vec3R&) const override + { + return mRGBA; + } + BaseShader* copy() const override { return new MatteShader(*this); } + +private: + const Film::RGBA mRGBA; +}; + + +/// @brief Color shader that treats the surface normal (x, y, z) as an +/// RGB color. +/// +/// @details The color can either be constant (if GridT = +/// Film::RGBA which is the default) or defined in a separate Vec3 +/// color grid. Use SamplerType to define the order of interpolation +/// (default is zero order, i.e. closes-point). +template +class NormalShader: public BaseShader +{ +public: + NormalShader(const GridT& grid) : mAcc(grid.getAccessor()), mXform(&grid.transform()) {} + NormalShader(const NormalShader&) = default; + ~NormalShader() override = default; + Film::RGBA operator()(const Vec3R& xyz, const Vec3R& normal, const Vec3R&) const override + { + typename GridT::ValueType v = zeroVal(); + SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); + return Film::RGBA(v[0]*(normal[0]+1.0), v[1]*(normal[1]+1.0), v[2]*(normal[2]+1.0)); + } + BaseShader* copy() const override { return new NormalShader(*this); } + +private: + typename GridT::ConstAccessor mAcc; + const math::Transform* mXform; +}; + +// Template specialization using a constant color of the material. +template +class NormalShader: public BaseShader +{ +public: + NormalShader(const Film::RGBA& c = Film::RGBA(1.0f)) : mRGBA(c*0.5f) {} + NormalShader(const NormalShader&) = default; + ~NormalShader() override = default; + Film::RGBA operator()(const Vec3R&, const Vec3R& normal, const Vec3R&) const override + { + return mRGBA * Film::RGBA(normal[0] + 1.0, normal[1] + 1.0, normal[2] + 1.0); + } + BaseShader* copy() const override { return new NormalShader(*this); } + +private: + const Film::RGBA mRGBA; +}; + + +/// @brief Color shader that treats position (x, y, z) as an RGB color in a +/// cube defined from an axis-aligned bounding box in world space. +/// +/// @details The color can either be constant (if GridT = +/// Film::RGBA which is the default) or defined in a separate Vec3 +/// color grid. Use SamplerType to define the order of interpolation +/// (default is zero order, i.e. closes-point). +template +class PositionShader: public BaseShader +{ +public: + PositionShader(const math::BBox& bbox, const GridT& grid) + : mMin(bbox.min()) + , mInvDim(1.0/bbox.extents()) + , mAcc(grid.getAccessor()) + , mXform(&grid.transform()) + { + } + PositionShader(const PositionShader&) = default; + ~PositionShader() override = default; + Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const override + { + typename GridT::ValueType v = zeroVal(); + SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); + const Vec3R rgb = (xyz - mMin) * mInvDim; + return Film::RGBA(v[0],v[1],v[2]) * Film::RGBA(rgb[0], rgb[1], rgb[2]); + } + BaseShader* copy() const override { return new PositionShader(*this); } + +private: + const Vec3R mMin, mInvDim; + typename GridT::ConstAccessor mAcc; + const math::Transform* mXform; +}; + +// Template specialization using a constant color of the material. +template +class PositionShader: public BaseShader +{ +public: + PositionShader(const math::BBox& bbox, const Film::RGBA& c = Film::RGBA(1.0f)) + : mMin(bbox.min()), mInvDim(1.0/bbox.extents()), mRGBA(c) {} + PositionShader(const PositionShader&) = default; + ~PositionShader() override = default; + Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const override + { + const Vec3R rgb = (xyz - mMin)*mInvDim; + return mRGBA*Film::RGBA(rgb[0], rgb[1], rgb[2]); + } + BaseShader* copy() const override { return new PositionShader(*this); } + +private: + const Vec3R mMin, mInvDim; + const Film::RGBA mRGBA; +}; + + +/// @brief Simple diffuse Lambertian surface shader. +/// +/// @details The diffuse color can either be constant (if GridT = +/// Film::RGBA which is the default) or defined in a separate Vec3 +/// color grid. Lambertian implies that the (radiant) intensity is +/// directly proportional to the cosine of the angle between the +/// surface normal and the direction of the light source. Use +/// SamplerType to define the order of interpolation (default is +/// zero order, i.e. closes-point). +template +class DiffuseShader: public BaseShader +{ +public: + DiffuseShader(const GridT& grid): mAcc(grid.getAccessor()), mXform(&grid.transform()) {} + DiffuseShader(const DiffuseShader&) = default; + ~DiffuseShader() override = default; + Film::RGBA operator()(const Vec3R& xyz, const Vec3R& normal, const Vec3R& rayDir) const override + { + typename GridT::ValueType v = zeroVal(); + SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); + // We take the abs of the dot product corresponding to having + // light sources at +/- rayDir, i.e., two-sided shading. + return Film::RGBA(v[0],v[1],v[2]) + * static_cast(math::Abs(normal.dot(rayDir))); + } + BaseShader* copy() const override { return new DiffuseShader(*this); } + +private: + typename GridT::ConstAccessor mAcc; + const math::Transform* mXform; +}; + +// Template specialization using a constant color of the material. +template +class DiffuseShader: public BaseShader +{ +public: + DiffuseShader(const Film::RGBA& d = Film::RGBA(1.0f)): mRGBA(d) {} + DiffuseShader(const DiffuseShader&) = default; + ~DiffuseShader() override = default; + Film::RGBA operator()(const Vec3R&, const Vec3R& normal, const Vec3R& rayDir) const override + { + // We assume a single directional light source at the camera, + // so the cosine of the angle between the surface normal and the + // direction of the light source becomes the dot product of the + // surface normal and inverse direction of the ray. We also ignore + // negative dot products, corresponding to strict one-sided shading. + //return mRGBA * math::Max(0.0, normal.dot(-rayDir)); + + // We take the abs of the dot product corresponding to having + // light sources at +/- rayDir, i.e., two-sided shading. + return mRGBA * static_cast(math::Abs(normal.dot(rayDir))); + } + BaseShader* copy() const override { return new DiffuseShader(*this); } + +private: + const Film::RGBA mRGBA; +}; + + +//////////////////////////////////////// RAYTRACER //////////////////////////////////////// + +template +inline void rayTrace(const GridT& grid, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed, + bool threaded) +{ + LevelSetRayTracer > + tracer(grid, shader, camera, pixelSamples, seed); + tracer.render(threaded); +} + + +template +inline void rayTrace(const GridT&, + const IntersectorT& inter, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed, + bool threaded) +{ + LevelSetRayTracer tracer(inter, shader, camera, pixelSamples, seed); + tracer.render(threaded); +} + + +//////////////////////////////////////// LevelSetRayTracer //////////////////////////////////////// + + +template +inline LevelSetRayTracer:: +LevelSetRayTracer(const GridT& grid, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed) + : mIsMaster(true), + mRand(nullptr), + mInter(grid), + mShader(shader.copy()), + mCamera(&camera) +{ + this->setPixelSamples(pixelSamples, seed); +} + +template +inline LevelSetRayTracer:: +LevelSetRayTracer(const IntersectorT& inter, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed) + : mIsMaster(true), + mRand(nullptr), + mInter(inter), + mShader(shader.copy()), + mCamera(&camera) +{ + this->setPixelSamples(pixelSamples, seed); +} + +template +inline LevelSetRayTracer:: +LevelSetRayTracer(const LevelSetRayTracer& other) : + mIsMaster(false), + mRand(other.mRand), + mInter(other.mInter), + mShader(other.mShader->copy()), + mCamera(other.mCamera), + mSubPixels(other.mSubPixels) +{ +} + +template +inline LevelSetRayTracer:: +~LevelSetRayTracer() +{ + if (mIsMaster) delete [] mRand; +} + +template +inline void LevelSetRayTracer:: +setGrid(const GridT& grid) +{ + assert(mIsMaster); + mInter = IntersectorT(grid); +} + +template +inline void LevelSetRayTracer:: +setIntersector(const IntersectorT& inter) +{ + assert(mIsMaster); + mInter = inter; +} + +template +inline void LevelSetRayTracer:: +setShader(const BaseShader& shader) +{ + assert(mIsMaster); + mShader.reset(shader.copy()); +} + +template +inline void LevelSetRayTracer:: +setCamera(BaseCamera& camera) +{ + assert(mIsMaster); + mCamera = &camera; +} + +template +inline void LevelSetRayTracer:: +setPixelSamples(size_t pixelSamples, unsigned int seed) +{ + assert(mIsMaster); + if (pixelSamples == 0) { + OPENVDB_THROW(ValueError, "pixelSamples must be larger than zero!"); + } + mSubPixels = pixelSamples - 1; + delete [] mRand; + if (mSubPixels > 0) { + mRand = new double[16]; + math::Rand01 rand(seed);//offsets for anti-aliaing by jittered super-sampling + for (size_t i=0; i<16; ++i) mRand[i] = rand(); + } else { + mRand = nullptr; + } +} + +template +inline void LevelSetRayTracer:: +render(bool threaded) const +{ + tbb::blocked_range range(0, mCamera->height()); + threaded ? tbb::parallel_for(range, *this) : (*this)(range); +} + +template +inline void LevelSetRayTracer:: +operator()(const tbb::blocked_range& range) const +{ + const BaseShader& shader = *mShader; + Vec3Type xyz, nml; + const float frac = 1.0f / (1.0f + float(mSubPixels)); + for (size_t j=range.begin(), n=0, je = range.end(); jwidth(); ipixel(i,j); + RayType ray = mCamera->getRay(i, j);//primary ray + Film::RGBA c = mInter.intersectsWS(ray, xyz, nml) ? shader(xyz, nml, ray.dir()) : bg; + for (size_t k=0; kgetRay(i, j, mRand[n & 15], mRand[(n+1) & 15]); + c += mInter.intersectsWS(ray, xyz, nml) ? shader(xyz, nml, ray.dir()) : bg; + }//loop over sub-pixels + bg = c*frac; + }//loop over image height + }//loop over image width +} + +//////////////////////////////////////// VolumeRender //////////////////////////////////////// + +template +inline VolumeRender:: +VolumeRender(const IntersectorT& inter, BaseCamera& camera) + : mAccessor(inter.grid().getConstAccessor()) + , mCamera(&camera) + , mPrimary(new IntersectorT(inter)) + , mShadow(new IntersectorT(inter)) + , mPrimaryStep(1.0) + , mShadowStep(3.0) + , mCutOff(0.005) + , mLightGain(0.2) + , mLightDir(Vec3R(0.3, 0.3, 0).unit()) + , mLightColor(0.7, 0.7, 0.7) + , mAbsorption(0.1) + , mScattering(1.5) +{ +} + +template +inline VolumeRender:: +VolumeRender(const VolumeRender& other) + : mAccessor(other.mAccessor) + , mCamera(other.mCamera) + , mPrimary(new IntersectorT(*(other.mPrimary))) + , mShadow(new IntersectorT(*(other.mShadow))) + , mPrimaryStep(other.mPrimaryStep) + , mShadowStep(other.mShadowStep) + , mCutOff(other.mCutOff) + , mLightGain(other.mLightGain) + , mLightDir(other.mLightDir) + , mLightColor(other.mLightColor) + , mAbsorption(other.mAbsorption) + , mScattering(other.mScattering) +{ +} + +template +inline void VolumeRender:: +print(std::ostream& os, int verboseLevel) +{ + if (verboseLevel>0) { + os << "\nPrimary step: " << mPrimaryStep + << "\nShadow step: " << mShadowStep + << "\nCutoff: " << mCutOff + << "\nLightGain: " << mLightGain + << "\nLightDir: " << mLightDir + << "\nLightColor: " << mLightColor + << "\nAbsorption: " << mAbsorption + << "\nScattering: " << mScattering << std::endl; + } + mPrimary->print(os, verboseLevel); +} + +template +inline void VolumeRender:: +setIntersector(const IntersectorT& inter) +{ + mPrimary.reset(new IntersectorT(inter)); + mShadow.reset( new IntersectorT(inter)); +} + +template +inline void VolumeRender:: +render(bool threaded) const +{ + tbb::blocked_range range(0, mCamera->height()); + threaded ? tbb::parallel_for(range, *this) : (*this)(range); +} + +template +inline void VolumeRender:: +operator()(const tbb::blocked_range& range) const +{ + SamplerType sampler(mAccessor, mShadow->grid().transform());//light-weight wrapper + + // Any variable prefixed with p (or s) means it's associated with a primary (or shadow) ray + const Vec3R extinction = -mScattering-mAbsorption, One(1.0); + const Vec3R albedo = mLightColor*mScattering/(mScattering+mAbsorption);//single scattering + const Real sGain = mLightGain;//in-scattering along shadow ray + const Real pStep = mPrimaryStep;//Integration step along primary ray in voxel units + const Real sStep = mShadowStep;//Integration step along shadow ray in voxel units + const Real cutoff = mCutOff;//Cutoff for density and transmittance + + // For the sake of completeness we show how to use two different + // methods (hits/march) in VolumeRayIntersector that produce + // segments along the ray that intersects active values. Comment out + // the line below to use VolumeRayIntersector::march instead of + // VolumeRayIntersector::hits. +#define USE_HITS +#ifdef USE_HITS + std::vector pTS, sTS; + //std::deque pTS, sTS; +#endif + + RayType sRay(Vec3R(0), mLightDir);//Shadow ray + for (size_t j=range.begin(), je = range.end(); jwidth(); ipixel(i, j); + bg.a = bg.r = bg.g = bg.b = 0; + RayType pRay = mCamera->getRay(i, j);// Primary ray + if( !mPrimary->setWorldRay(pRay)) continue; + Vec3R pTrans(1.0), pLumi(0.0); +#ifndef USE_HITS + Real pT0, pT1; + while (mPrimary->march(pT0, pT1)) { + for (Real pT = pStep*ceil(pT0/pStep); pT <= pT1; pT += pStep) { +#else + mPrimary->hits(pTS); + for (size_t k=0; kgetWorldPos(pT); + const Real density = sampler.wsSample(pPos); + if (density < cutoff) continue; + const Vec3R dT = math::Exp(extinction * density * pStep); + Vec3R sTrans(1.0); + sRay.setEye(pPos); + if( !mShadow->setWorldRay(sRay)) continue; +#ifndef USE_HITS + Real sT0, sT1; + while (mShadow->march(sT0, sT1)) { + for (Real sT = sStep*ceil(sT0/sStep); sT <= sT1; sT+= sStep) { +#else + mShadow->hits(sTS); + for (size_t l=0; lgetWorldPos(sT)); + if (d < cutoff) continue; + sTrans *= math::Exp(extinction * d * sStep/(1.0+sT*sGain)); + if (sTrans.lengthSqr()(pLumi[0]); + bg.g = static_cast(pLumi[1]); + bg.b = static_cast(pLumi[2]); + bg.a = static_cast(1.0f - pTrans.sum()/3.0f); + }//Horizontal pixel scan + }//Vertical pixel scan +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/SignedFloodFill.h b/openvdb/tools/SignedFloodFill.h new file mode 100644 index 00000000..d2dc3298 --- /dev/null +++ b/openvdb/tools/SignedFloodFill.h @@ -0,0 +1,279 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file SignedFloodFill.h +/// +/// @brief Propagate the signs of distance values from the active voxels +/// in the narrow band to the inactive values outside the narrow band. +/// +/// @author Ken Museth + +#ifndef OPENVDB_TOOLS_SIGNEDFLOODFILL_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_SIGNEDFLOODFILL_HAS_BEEN_INCLUDED + +#include +#include // for Index typedef +#include // for math::negative +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Set the values of all inactive voxels and tiles of a narrow-band +/// level set from the signs of the active voxels, setting outside values to +/// +background and inside values to -background. +/// +/// @warning This method should only be used on closed, symmetric narrow-band level sets. +/// +/// @note If a LeafManager is used the cached leaf nodes are reused, +/// resulting in slightly better overall performance. +/// +/// @param tree Tree or LeafManager that will be flood filled. +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +/// @param minLevel Specify the lowest tree level to process (leafnode level = 0) +/// +/// @throw TypeError if the ValueType of @a tree is not floating-point. +template +inline void +signedFloodFill(TreeOrLeafManagerT& tree, bool threaded = true, + size_t grainSize = 1, Index minLevel = 0); + + +/// @brief Set the values of all inactive voxels and tiles of a narrow-band +/// level set from the signs of the active voxels, setting exterior values to +/// @a outsideWidth and interior values to @a insideWidth. Set the background value +/// of this tree to @a outsideWidth. +/// +/// @warning This method should only be used on closed, narrow-band level sets. +/// +/// @note If a LeafManager is used the cached leaf nodes are reused +/// resulting in slightly better overall performance. +/// +/// @param tree Tree or LeafManager that will be flood filled +/// @param outsideWidth the width of the outside of the narrow band +/// @param insideWidth the width of the inside of the narrow band +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +/// @param minLevel Specify the lowest tree level to process (leafnode level = 0) +/// +/// @throw TypeError if the ValueType of @a tree is not floating-point. +template +inline void +signedFloodFillWithValues( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& outsideWidth, + const typename TreeOrLeafManagerT::ValueType& insideWidth, + bool threaded = true, + size_t grainSize = 1, + Index minLevel = 0); + + +////////////////////////// Implementation of SignedFloodFill //////////////////////////// + + +template +class SignedFloodFillOp +{ +public: + using ValueT = typename TreeOrLeafManagerT::ValueType; + using RootT = typename TreeOrLeafManagerT::RootNodeType; + using LeafT = typename TreeOrLeafManagerT::LeafNodeType; + static_assert(std::is_signed::value, + "signed flood fill is supported only for signed value grids"); + + SignedFloodFillOp(const TreeOrLeafManagerT& tree, Index minLevel = 0) + : mOutside(ValueT(math::Abs(tree.background()))) + , mInside(ValueT(math::negative(mOutside))) + , mMinLevel(minLevel) + { + } + + SignedFloodFillOp(ValueT outsideValue, ValueT insideValue, Index minLevel = 0) + : mOutside(ValueT(math::Abs(outsideValue))) + , mInside(ValueT(math::negative(math::Abs(insideValue)))) + , mMinLevel(minLevel) + { + } + + // Nothing to do at the leaf node level + void operator()(LeafT& leaf) const + { + if (LeafT::LEVEL < mMinLevel) return; + + if (!leaf.allocate()) return; // this assures that the buffer is allocated and in-memory + + const typename LeafT::NodeMaskType& valueMask = leaf.getValueMask(); + // WARNING: "Never do what you're about to see at home, we're what you call experts!" + typename LeafT::ValueType* buffer = + const_cast(&(leaf.getFirstValue())); + + const Index first = valueMask.findFirstOn(); + if (first < LeafT::SIZE) { + bool xInside = buffer[first]<0, yInside = xInside, zInside = xInside; + for (Index x = 0; x != (1 << LeafT::LOG2DIM); ++x) { + const Index x00 = x << (2 * LeafT::LOG2DIM); + if (valueMask.isOn(x00)) xInside = buffer[x00] < 0; // element(x, 0, 0) + yInside = xInside; + for (Index y = 0; y != (1 << LeafT::LOG2DIM); ++y) { + const Index xy0 = x00 + (y << LeafT::LOG2DIM); + if (valueMask.isOn(xy0)) yInside = buffer[xy0] < 0; // element(x, y, 0) + zInside = yInside; + for (Index z = 0; z != (1 << LeafT::LOG2DIM); ++z) { + const Index xyz = xy0 + z; // element(x, y, z) + if (valueMask.isOn(xyz)) { + zInside = buffer[xyz] < 0; + } else { + buffer[xyz] = zInside ? mInside : mOutside; + } + } + } + } + } else {// if no active voxels exist simply use the sign of the first value + leaf.fill(buffer[0] < 0 ? mInside : mOutside); + } + } + + // Prune the child nodes of the internal nodes + template + void operator()(NodeT& node) const + { + if (NodeT::LEVEL < mMinLevel) return; + // We assume the child nodes have already been flood filled! + const typename NodeT::NodeMaskType& childMask = node.getChildMask(); + // WARNING: "Never do what you're about to see at home, we're what you call experts!" + typename NodeT::UnionType* table = const_cast(node.getTable()); + + const Index first = childMask.findFirstOn(); + if (first < NodeT::NUM_VALUES) { + bool xInside = table[first].getChild()->getFirstValue()<0; + bool yInside = xInside, zInside = xInside; + for (Index x = 0; x != (1 << NodeT::LOG2DIM); ++x) { + const int x00 = x << (2 * NodeT::LOG2DIM); // offset for block(x, 0, 0) + if (childMask.isOn(x00)) xInside = table[x00].getChild()->getLastValue()<0; + yInside = xInside; + for (Index y = 0; y != (1 << NodeT::LOG2DIM); ++y) { + const Index xy0 = x00 + (y << NodeT::LOG2DIM); // offset for block(x, y, 0) + if (childMask.isOn(xy0)) yInside = table[xy0].getChild()->getLastValue()<0; + zInside = yInside; + for (Index z = 0; z != (1 << NodeT::LOG2DIM); ++z) { + const Index xyz = xy0 + z; // offset for block(x, y, z) + if (childMask.isOn(xyz)) { + zInside = table[xyz].getChild()->getLastValue()<0; + } else { + table[xyz].setValue(zInside ? mInside : mOutside); + } + } + } + } + } else {//no child nodes exist simply use the sign of the first tile value. + const ValueT v = table[0].getValue()<0 ? mInside : mOutside; + for (Index i = 0; i < NodeT::NUM_VALUES; ++i) table[i].setValue(v); + } + } + + // Prune the child nodes of the root node + void operator()(RootT& root) const + { + if (RootT::LEVEL < mMinLevel) return; + using ChildT = typename RootT::ChildNodeType; + // Insert the child nodes into a map sorted according to their origin + std::map nodeKeys; + typename RootT::ChildOnIter it = root.beginChildOn(); + for (; it; ++it) nodeKeys.insert(std::pair(it.getCoord(), &(*it))); + static const Index DIM = RootT::ChildNodeType::DIM; + + // We employ a simple z-scanline algorithm that inserts inactive tiles with + // the inside value if they are sandwiched between inside child nodes only! + typename std::map::const_iterator b = nodeKeys.begin(), e = nodeKeys.end(); + if ( b == e ) return; + for (typename std::map::const_iterator a = b++; b != e; ++a, ++b) { + Coord d = b->first - a->first; // delta of neighboring coordinates + if (d[0]!=0 || d[1]!=0 || d[2]==Int32(DIM)) continue;// not same z-scanline or neighbors + const ValueT fill[] = { a->second->getLastValue(), b->second->getFirstValue() }; + if (!(fill[0] < 0) || !(fill[1] < 0)) continue; // scanline isn't inside + Coord c = a->first + Coord(0u, 0u, DIM); + for (; c[2] != b->first[2]; c[2] += DIM) root.addTile(c, mInside, false); + } + root.setBackground(mOutside, /*updateChildNodes=*/false); + } + +private: + const ValueT mOutside, mInside; + const Index mMinLevel; +};// SignedFloodFillOp + + +//{ +/// @cond OPENVDB_SIGNED_FLOOD_FILL_INTERNAL + +template +inline +typename std::enable_if::value, void>::type +doSignedFloodFill(TreeOrLeafManagerT& tree, + typename TreeOrLeafManagerT::ValueType outsideValue, + typename TreeOrLeafManagerT::ValueType insideValue, + bool threaded, + size_t grainSize, + Index minLevel) +{ + tree::NodeManager nodes(tree); + SignedFloodFillOp op(outsideValue, insideValue, minLevel); + nodes.foreachBottomUp(op, threaded, grainSize); +} + +// Dummy (no-op) implementation for unsigned types +template +inline +typename std::enable_if::value, void>::type +doSignedFloodFill(TreeOrLeafManagerT&, + const typename TreeOrLeafManagerT::ValueType&, + const typename TreeOrLeafManagerT::ValueType&, + bool, + size_t, + Index) +{ + OPENVDB_THROW(TypeError, + "signedFloodFill is supported only for signed value grids"); +} + +/// @endcond +//} + + +// If the narrow-band is symmetric and unchanged +template +inline void +signedFloodFillWithValues( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& outsideValue, + const typename TreeOrLeafManagerT::ValueType& insideValue, + bool threaded, + size_t grainSize, + Index minLevel) +{ + doSignedFloodFill(tree, outsideValue, insideValue, threaded, grainSize, minLevel); +} + + +template +inline void +signedFloodFill(TreeOrLeafManagerT& tree, + bool threaded, + size_t grainSize, + Index minLevel) +{ + const typename TreeOrLeafManagerT::ValueType v = tree.root().background(); + doSignedFloodFill(tree, v, math::negative(v), threaded, grainSize, minLevel); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_RESETBACKGROUND_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/Statistics.h b/openvdb/tools/Statistics.h new file mode 100644 index 00000000..dfbc857b --- /dev/null +++ b/openvdb/tools/Statistics.h @@ -0,0 +1,407 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Statistics.h +/// +/// @brief Functions to efficiently compute histograms, extremas +/// (min/max) and statistics (mean, variance, etc.) of grid values + +#ifndef OPENVDB_TOOLS_STATISTICS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_STATISTICS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include "ValueTransformer.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Iterate over a scalar grid and compute a histogram of the values +/// of the voxels that are visited, or iterate over a vector-valued grid +/// and compute a histogram of the magnitudes of the vectors. +/// @param iter an iterator over the values of a grid or its tree +/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.) +/// @param minVal the smallest value that can be added to the histogram +/// @param maxVal the largest value that can be added to the histogram +/// @param numBins the number of histogram bins +/// @param threaded if true, iterate over the grid in parallel +template +inline math::Histogram +histogram(const IterT& iter, double minVal, double maxVal, + size_t numBins = 10, bool threaded = true); + +/// @brief Iterate over a scalar grid and compute extrema (min/max) of the +/// values of the voxels that are visited, or iterate over a vector-valued grid +/// and compute extrema of the magnitudes of the vectors. +/// @param iter an iterator over the values of a grid or its tree +/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.) +/// @param threaded if true, iterate over the grid in parallel +template +inline math::Extrema +extrema(const IterT& iter, bool threaded = true); + +/// @brief Iterate over a scalar grid and compute statistics (mean, variance, etc.) +/// of the values of the voxels that are visited, or iterate over a vector-valued grid +/// and compute statistics of the magnitudes of the vectors. +/// @param iter an iterator over the values of a grid or its tree +/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.) +/// @param threaded if true, iterate over the grid in parallel +template +inline math::Stats +statistics(const IterT& iter, bool threaded = true); + +/// @brief Iterate over a grid and compute extrema (min/max) of +/// the values produced by applying the given functor at each voxel that is visited. +/// @param iter an iterator over the values of a grid or its tree +/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.) +/// @param op a functor of the form void op(const IterT&, math::Stats&), +/// where @c IterT is the type of @a iter, that inserts zero or more +/// floating-point values into the provided @c math::Stats object +/// @param threaded if true, iterate over the grid in parallel +/// @note When @a threaded is true, each thread gets its own copy of the functor. +/// +/// @par Example: +/// Compute statistics of just the active and positive-valued voxels of a scalar, +/// floating-point grid. +/// @code +/// struct Local { +/// static inline +/// void addIfPositive(const FloatGrid::ValueOnCIter& iter, math::Extrema& ex) +/// { +/// const float f = *iter; +/// if (f > 0.0) { +/// if (iter.isVoxelValue()) ex.add(f); +/// else ex.add(f, iter.getVoxelCount()); +/// } +/// } +/// }; +/// FloatGrid grid = ...; +/// math::Extrema stats = +/// tools::extrema(grid.cbeginValueOn(), Local::addIfPositive, /*threaded=*/true); +/// @endcode +template +inline math::Extrema +extrema(const IterT& iter, const ValueOp& op, bool threaded); + +/// @brief Iterate over a grid and compute statistics (mean, variance, etc.) of +/// the values produced by applying the given functor at each voxel that is visited. +/// @param iter an iterator over the values of a grid or its tree +/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.) +/// @param op a functor of the form void op(const IterT&, math::Stats&), +/// where @c IterT is the type of @a iter, that inserts zero or more +/// floating-point values into the provided @c math::Stats object +/// @param threaded if true, iterate over the grid in parallel +/// @note When @a threaded is true, each thread gets its own copy of the functor. +/// +/// @par Example: +/// Compute statistics of just the active and positive-valued voxels of a scalar, +/// floating-point grid. +/// @code +/// struct Local { +/// static inline +/// void addIfPositive(const FloatGrid::ValueOnCIter& iter, math::Stats& stats) +/// { +/// const float f = *iter; +/// if (f > 0.0) { +/// if (iter.isVoxelValue()) stats.add(f); +/// else stats.add(f, iter.getVoxelCount()); +/// } +/// } +/// }; +/// FloatGrid grid = ...; +/// math::Stats stats = +/// tools::statistics(grid.cbeginValueOn(), Local::addIfPositive, /*threaded=*/true); +/// @endcode +template +inline math::Stats +statistics(const IterT& iter, const ValueOp& op, bool threaded); + + +/// @brief Iterate over a grid and compute statistics (mean, variance, etc.) +/// of the values produced by applying a given operator (see math/Operators.h) +/// at each voxel that is visited. +/// @param iter an iterator over the values of a grid or its tree +/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.) +/// @param op an operator object with a method of the form +/// double result(Accessor&, const Coord&) +/// @param threaded if true, iterate over the grid in parallel +/// @note World-space operators, whose @c result() methods are of the form +/// double result(const Map&, Accessor&, const Coord&), must be wrapped +/// in a math::MapAdapter. +/// @note Vector-valued operators like math::Gradient must be wrapped in an adapter +/// such as math::OpMagnitude. +/// +/// @par Example: +/// Compute statistics of the magnitude of the gradient at the active voxels of +/// a scalar, floating-point grid. (Note the use of the math::MapAdapter and +/// math::OpMagnitude adapters.) +/// @code +/// FloatGrid grid = ...; +/// +/// // Assume that we know that the grid has a uniform scale map. +/// using MapType = math::UniformScaleMap; +/// // Specify a world-space gradient operator that uses first-order differencing. +/// using GradientOp = math::Gradient; +/// // Wrap the operator with an adapter that computes the magnitude of the gradient. +/// using MagnitudeOp = math::OpMagnitude; +/// // Wrap the operator with an adapter that associates a map with it. +/// using CompoundOp = math::MapAdapter; +/// +/// if (MapType::Ptr map = grid.constTransform().constMap()) { +/// math::Stats stats = tools::opStatistics(grid.cbeginValueOn(), CompoundOp(*map)); +/// } +/// @endcode +/// +/// @par Example: +/// Compute statistics of the divergence at the active voxels of a vector-valued grid. +/// @code +/// Vec3SGrid grid = ...; +/// +/// // Assume that we know that the grid has a uniform scale map. +/// using MapType = math::UniformScaleMap; +/// // Specify a world-space divergence operator that uses first-order differencing. +/// using DivergenceOp = math::Divergence; +/// // Wrap the operator with an adapter that associates a map with it. +/// using CompoundOp = math::MapAdapter; +/// +/// if (MapType::Ptr map = grid.constTransform().constMap()) { +/// math::Stats stats = tools::opStatistics(grid.cbeginValueOn(), CompoundOp(*map)); +/// } +/// @endcode +/// +/// @par Example: +/// As above, but computing the divergence in index space. +/// @code +/// Vec3SGrid grid = ...; +/// +/// // Specify an index-space divergence operator that uses first-order differencing. +/// using DivergenceOp = math::ISDivergence; +/// +/// math::Stats stats = tools::opStatistics(grid.cbeginValueOn(), DivergenceOp()); +/// @endcode +template +inline math::Stats +opStatistics(const IterT& iter, const OperatorT& op = OperatorT(), bool threaded = true); + +/// @brief Same as opStatistics except it returns a math::Extrema vs a math::Stats +template +inline math::Extrema +opExtrema(const IterT& iter, const OperatorT& op = OperatorT(), bool threaded = true); + +//////////////////////////////////////// + + +namespace stats_internal { + +/// @todo This traits class is needed because tree::TreeValueIteratorBase uses +/// the name ValueT for the type of the value to which the iterator points, +/// whereas node-level iterators use the name ValueType. +template +struct IterTraits { + using ValueType = typename IterT::ValueType; +}; + +template +struct IterTraits > { + using ValueType = typename tree::TreeValueIteratorBase::ValueT; +}; + + +// Helper class to compute a scalar value from either a scalar or a vector value +// (the latter by computing the vector's magnitude) +template struct GetValImpl; + +template +struct GetValImpl { + static inline double get(const T& val) { return double(val); } +}; + +template +struct GetValImpl { + static inline double get(const T& val) { return val.length(); } +}; + + +// Helper class to compute a scalar value from a tree or node iterator +// that points to a value in either a scalar or a vector grid, and to +// add that value to a math::Stats object. +template +struct GetVal +{ + using ValueT = typename IterTraits::ValueType; + using ImplT = GetValImpl::IsVec>; + + inline void operator()(const IterT& iter, StatsT& stats) const { + if (iter.isVoxelValue()) stats.add(ImplT::get(*iter)); + else stats.add(ImplT::get(*iter), iter.getVoxelCount()); + } +}; + +// Helper class to accumulate scalar voxel values or vector voxel magnitudes +// into a math::Stats object +template +struct StatsOp +{ + StatsOp(const ValueOp& op): getValue(op) {} + + // Accumulate voxel and tile values into this functor's Stats object. + inline void operator()(const IterT& iter) { getValue(iter, stats); } + + // Accumulate another functor's Stats object into this functor's. + inline void join(StatsOp& other) { stats.add(other.stats); } + + StatsT stats; + ValueOp getValue; +}; + + +// Helper class to accumulate scalar voxel values or vector voxel magnitudes +// into a math::Histogram object +template +struct HistOp +{ + HistOp(const ValueOp& op, double vmin, double vmax, size_t bins): + hist(vmin, vmax, bins), getValue(op) + {} + + // Accumulate voxel and tile values into this functor's Histogram object. + inline void operator()(const IterT& iter) { getValue(iter, hist); } + + // Accumulate another functor's Histogram object into this functor's. + inline void join(HistOp& other) { hist.add(other.hist); } + + math::Histogram hist; + ValueOp getValue; +}; + + +// Helper class to apply an operator such as math::Gradient or math::Laplacian +// to voxels and accumulate the scalar results or the magnitudes of vector results +// into a math::Stats object +template +struct MathOp +{ + using TreeT = typename IterT::TreeT; + using ValueT = typename TreeT::ValueType; + using ConstAccessor = typename tree::ValueAccessor; + + // Each thread gets its own accessor and its own copy of the operator. + ConstAccessor mAcc; + OpT mOp; + StatsT mStats; + + template + static inline TreeT* THROW_IF_NULL(TreeT* ptr) { + if (ptr == nullptr) OPENVDB_THROW(ValueError, "iterator references a null tree"); + return ptr; + } + + MathOp(const IterT& iter, const OpT& op): + mAcc(*THROW_IF_NULL(iter.getTree())), mOp(op) + {} + + // Accumulate voxel and tile values into this functor's Stats object. + void operator()(const IterT& it) + { + if (it.isVoxelValue()) { + // Add the magnitude of the gradient at a single voxel. + mStats.add(mOp.result(mAcc, it.getCoord())); + } else { + // Iterate over the voxels enclosed by a tile and add the results + // of applying the operator at each voxel. + /// @todo This could be specialized to be done more efficiently for some operators. + /// For example, all voxels in the interior of a tile (i.e., not on the borders) + /// have gradient zero, so there's no need to apply the operator to every voxel. + CoordBBox bbox = it.getBoundingBox(); + Coord xyz; + int &x = xyz.x(), &y = xyz.y(), &z = xyz.z(); + for (x = bbox.min().x(); x <= bbox.max().x(); ++x) { + for (y = bbox.min().y(); y <= bbox.max().y(); ++y) { + for (z = bbox.min().z(); z <= bbox.max().z(); ++z) { + mStats.add(mOp.result(mAcc, it.getCoord())); + } + } + } + } + } + + // Accumulate another functor's Stats object into this functor's. + inline void join(MathOp& other) { mStats.add(other.mStats); } +}; // struct MathOp + +} // namespace stats_internal + + +template +inline math::Histogram +histogram(const IterT& iter, double vmin, double vmax, size_t numBins, bool threaded) +{ + using ValueOp = stats_internal::GetVal; + ValueOp valOp; + stats_internal::HistOp op(valOp, vmin, vmax, numBins); + tools::accumulate(iter, op, threaded); + return op.hist; +} + +template +inline math::Extrema +extrema(const IterT& iter, bool threaded) +{ + stats_internal::GetVal valOp; + return extrema(iter, valOp, threaded); +} + +template +inline math::Stats +statistics(const IterT& iter, bool threaded) +{ + stats_internal::GetVal valOp; + return statistics(iter, valOp, threaded); +} + +template +inline math::Extrema +extrema(const IterT& iter, const ValueOp& valOp, bool threaded) +{ + stats_internal::StatsOp op(valOp); + tools::accumulate(iter, op, threaded); + return op.stats; +} + +template +inline math::Stats +statistics(const IterT& iter, const ValueOp& valOp, bool threaded) +{ + stats_internal::StatsOp op(valOp); + tools::accumulate(iter, op, threaded); + return op.stats; +} + + +template +inline math::Extrema +opExtrema(const IterT& iter, const OperatorT& op, bool threaded) +{ + stats_internal::MathOp func(iter, op); + tools::accumulate(iter, func, threaded); + return func.mStats; +} + +template +inline math::Stats +opStatistics(const IterT& iter, const OperatorT& op, bool threaded) +{ + stats_internal::MathOp func(iter, op); + tools::accumulate(iter, func, threaded); + return func.mStats; +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_STATISTICS_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/TopologyToLevelSet.h b/openvdb/tools/TopologyToLevelSet.h new file mode 100644 index 00000000..2703cb77 --- /dev/null +++ b/openvdb/tools/TopologyToLevelSet.h @@ -0,0 +1,258 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file TopologyToLevelSet.h +/// +/// @brief This tool generates a narrow-band signed distance field / level set +/// from the interface between active and inactive voxels in a vdb grid. +/// +/// @par Example: +/// Combine with @c tools::PointsToVolume for fast point cloud to level set conversion. + +#ifndef OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED + +#include "LevelSetFilter.h" +#include "Morphology.h" // for erodeVoxels and dilateActiveValues +#include "SignedFloodFill.h" + +#include +#include +#include // for math::BiasedGradientScheme +#include +#include +#include // for std::min(), std::max() +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Compute the narrow-band signed distance to the interface between +/// active and inactive voxels in the input grid. +/// +/// @return A shared pointer to a new sdf / level set grid of type @c float +/// +/// @param grid Input grid of arbitrary type whose active voxels are used +/// in constructing the level set. +/// @param halfWidth Half the width of the narrow band in voxel units. +/// @param closingSteps Number of morphological closing steps used to fill gaps +/// in the active voxel region. +/// @param dilation Number of voxels to expand the active voxel region. +/// @param smoothingSteps Number of smoothing interations. +template +inline typename GridT::template ValueConverter::Type::Ptr +topologyToLevelSet(const GridT& grid, int halfWidth = 3, int closingSteps = 1, int dilation = 0, + int smoothingSteps = 0); + + +/// @brief Compute the narrow-band signed distance to the interface between +/// active and inactive voxels in the input grid. +/// +/// @return A shared pointer to a new sdf / level set grid of type @c float +/// +/// @param grid Input grid of arbitrary type whose active voxels are used +/// in constructing the level set. +/// @param halfWidth Half the width of the narrow band in voxel units. +/// @param closingSteps Number of morphological closing steps used to fill gaps +/// in the active voxel region. +/// @param dilation Number of voxels to expand the active voxel region. +/// @param smoothingSteps Number of smoothing interations. +/// @param interrupt Optional object adhering to the util::NullInterrupter interface. +template +inline typename GridT::template ValueConverter::Type::Ptr +topologyToLevelSet(const GridT& grid, int halfWidth = 3, int closingSteps = 1, int dilation = 0, + int smoothingSteps = 0, InterrupterT* interrupt = nullptr); + + +//////////////////////////////////////// + + +namespace ttls_internal { + + +template +struct DilateOp +{ + DilateOp(TreeT& t, int n) : tree(&t), size(n) {} + void operator()() const { + dilateActiveValues( *tree, size, tools::NN_FACE, tools::IGNORE_TILES); + } + TreeT* tree; + const int size; +}; + + +template +struct ErodeOp +{ + ErodeOp(TreeT& t, int n) : tree(&t), size(n) {} + void operator()() const { erodeVoxels( *tree, size); } + TreeT* tree; + const int size; +}; + + +template +struct OffsetAndMinComp +{ + using LeafNodeType = typename TreeType::LeafNodeType; + using ValueType = typename TreeType::ValueType; + + OffsetAndMinComp(std::vector& lhsNodes, + const TreeType& rhsTree, ValueType offset) + : mLhsNodes(lhsNodes.empty() ? nullptr : &lhsNodes[0]), mRhsTree(&rhsTree), mOffset(offset) + { + } + + void operator()(const tbb::blocked_range& range) const + { + using Iterator = typename LeafNodeType::ValueOnIter; + + tree::ValueAccessor rhsAcc(*mRhsTree); + const ValueType offset = mOffset; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& lhsNode = *mLhsNodes[n]; + const LeafNodeType * rhsNodePt = rhsAcc.probeConstLeaf(lhsNode.origin()); + if (!rhsNodePt) continue; + + for (Iterator it = lhsNode.beginValueOn(); it; ++it) { + ValueType& val = const_cast(it.getValue()); + val = std::min(val, offset + rhsNodePt->getValue(it.pos())); + } + } + } + +private: + LeafNodeType * * const mLhsNodes; + TreeType const * const mRhsTree; + ValueType const mOffset; +}; // struct OffsetAndMinComp + + +template +inline void +normalizeLevelSet(GridType& grid, const int halfWidthInVoxels, InterrupterType* interrupt = nullptr) +{ + LevelSetFilter filter(grid, interrupt); + filter.setSpatialScheme(math::FIRST_BIAS); + filter.setNormCount(halfWidthInVoxels); + filter.normalize(); + filter.prune(); +} + + +template +inline void +smoothLevelSet(GridType& grid, int iterations, int halfBandWidthInVoxels, + InterrupterType* interrupt = nullptr) +{ + using ValueType = typename GridType::ValueType; + using TreeType = typename GridType::TreeType; + using LeafNodeType = typename TreeType::LeafNodeType; + + GridType filterGrid(grid); + + LevelSetFilter filter(filterGrid, interrupt); + filter.setSpatialScheme(math::FIRST_BIAS); + + for (int n = 0; n < iterations; ++n) { + if (interrupt && interrupt->wasInterrupted()) break; + filter.mean(1); + } + + std::vector nodes; + grid.tree().getNodes(nodes); + + const ValueType offset = ValueType(double(0.5) * grid.transform().voxelSize()[0]); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + OffsetAndMinComp(nodes, filterGrid.tree(), -offset)); + + // Clean up any damanage that was done by the min operation + normalizeLevelSet(grid, halfBandWidthInVoxels, interrupt); +} + + +} // namespace ttls_internal + + + +template +inline typename GridT::template ValueConverter::Type::Ptr +topologyToLevelSet(const GridT& grid, int halfWidth, int closingSteps, int dilation, + int smoothingSteps, InterrupterT* interrupt) +{ + using MaskTreeT = typename GridT::TreeType::template ValueConverter::Type; + using FloatTreeT = typename GridT::TreeType::template ValueConverter::Type; + using FloatGridT = Grid; + + // Check inputs + + halfWidth = std::max(halfWidth, 1); + closingSteps = std::max(closingSteps, 0); + dilation = std::max(dilation, 0); + + if (!grid.hasUniformVoxels()) { + OPENVDB_THROW(ValueError, "Non-uniform voxels are not supported!"); + } + + // Copy the topology into a MaskGrid. + MaskTreeT maskTree( grid.tree(), false/*background*/, openvdb::TopologyCopy() ); + + // Morphological closing operation. + dilateActiveValues( maskTree, closingSteps + dilation, tools::NN_FACE, tools::IGNORE_TILES ); + erodeVoxels( maskTree, closingSteps ); + + // Generate a volume with an implicit zero crossing at the boundary + // between active and inactive values in the input grid. + const float background = float(grid.voxelSize()[0]) * float(halfWidth); + typename FloatTreeT::Ptr lsTree( + new FloatTreeT( maskTree, /*out=*/background, /*in=*/-background, openvdb::TopologyCopy() ) ); + + tbb::task_group pool; + pool.run( ttls_internal::ErodeOp< MaskTreeT >( maskTree, halfWidth ) ); + pool.run( ttls_internal::DilateOp( *lsTree , halfWidth ) ); + pool.wait();// wait for both tasks to complete + + lsTree->topologyDifference( maskTree ); + tools::pruneLevelSet( *lsTree, /*threading=*/true); + + // Create a level set grid from the tree + typename FloatGridT::Ptr lsGrid = FloatGridT::create( lsTree ); + lsGrid->setTransform( grid.transform().copy() ); + lsGrid->setGridClass( openvdb::GRID_LEVEL_SET ); + + // Use a PDE based scheme to propagate distance values from the + // implicit zero crossing. + ttls_internal::normalizeLevelSet(*lsGrid, 3*halfWidth, interrupt); + + // Additional filtering + if (smoothingSteps > 0) { + ttls_internal::smoothLevelSet(*lsGrid, smoothingSteps, halfWidth, interrupt); + } + + return lsGrid; +} + + +template +inline typename GridT::template ValueConverter::Type::Ptr +topologyToLevelSet(const GridT& grid, int halfWidth, int closingSteps, int dilation, int smoothingSteps) +{ + util::NullInterrupter interrupt; + return topologyToLevelSet(grid, halfWidth, closingSteps, dilation, smoothingSteps, &interrupt); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED + diff --git a/openvdb/tools/ValueTransformer.h b/openvdb/tools/ValueTransformer.h new file mode 100644 index 00000000..bea0dfea --- /dev/null +++ b/openvdb/tools/ValueTransformer.h @@ -0,0 +1,685 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file ValueTransformer.h +/// +/// @author Peter Cucka +/// +/// tools::foreach() and tools::transformValues() transform the values in a grid +/// by iterating over the grid with a user-supplied iterator and applying a +/// user-supplied functor at each step of the iteration. With tools::foreach(), +/// the transformation is done in-place on the input grid, whereas with +/// tools::transformValues(), transformed values are written to an output grid +/// (which can, for example, have a different value type than the input grid). +/// Both functions can optionally transform multiple values of the grid in parallel. +/// +/// tools::accumulate() can be used to accumulate the results of applying a functor +/// at each step of a grid iteration. (The functor is responsible for storing and +/// updating intermediate results.) When the iteration is done serially the behavior is +/// the same as with tools::foreach(), but when multiple values are processed in parallel, +/// an additional step is performed: when any two threads finish processing, +/// @c op.join(otherOp) is called on one thread's functor to allow it to coalesce +/// its intermediate result with the other thread's. +/// +/// Finally, tools::setValueOnMin(), tools::setValueOnMax(), tools::setValueOnSum() +/// and tools::setValueOnMult() are wrappers around Tree::modifyValue() (or +/// ValueAccessor::modifyValue()) for some commmon in-place operations. +/// These are typically significantly faster than calling getValue() followed by setValue(). + +#ifndef OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED + +#include // for std::min(), std::max() +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// Iterate over a grid and at each step call @c op(iter). +/// @param iter an iterator over a grid or its tree (@c Grid::ValueOnCIter, +/// @c Tree::NodeIter, etc.) +/// @param op a functor of the form void op(const IterT&), where @c IterT is +/// the type of @a iter +/// @param threaded if true, transform multiple values of the grid in parallel +/// @param shareOp if true and @a threaded is true, all threads use the same functor; +/// otherwise, each thread gets its own copy of the @e original functor +/// +/// @par Example: +/// Multiply all values (both set and unset) of a scalar, floating-point grid by two. +/// @code +/// struct Local { +/// static inline void op(const FloatGrid::ValueAllIter& iter) { +/// iter.setValue(*iter * 2); +/// } +/// }; +/// FloatGrid grid = ...; +/// tools::foreach(grid.beginValueAll(), Local::op); +/// @endcode +/// +/// @par Example: +/// Rotate all active vectors of a vector grid by 45 degrees about the y axis. +/// @code +/// namespace { +/// struct MatMul { +/// math::Mat3s M; +/// MatMul(const math::Mat3s& mat): M(mat) {} +/// inline void operator()(const VectorGrid::ValueOnIter& iter) const { +/// iter.setValue(M.transform(*iter)); +/// } +/// }; +/// } +/// { +/// VectorGrid grid = ...; +/// tools::foreach(grid.beginValueOn(), +/// MatMul(math::rotation(math::Y, M_PI_4))); +/// } +/// @endcode +/// +/// @note For more complex operations that require finer control over threading, +/// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction +/// with a tree::IteratorRange that wraps a grid or tree iterator. +template +inline void foreach(const IterT& iter, XformOp& op, + bool threaded = true, bool shareOp = true); + +template +inline void foreach(const IterT& iter, const XformOp& op, + bool threaded = true, bool shareOp = true); + + +/// Iterate over a grid and at each step call op(iter, accessor) to +/// populate (via the accessor) the given output grid, whose @c ValueType +/// need not be the same as the input grid's. +/// @param inIter a non-const or (preferably) @c const iterator over an +/// input grid or its tree (@c Grid::ValueOnCIter, @c Tree::NodeIter, etc.) +/// @param outGrid an empty grid to be populated +/// @param op a functor of the form +/// void op(const InIterT&, OutGridT::ValueAccessor&), +/// where @c InIterT is the type of @a inIter +/// @param threaded if true, transform multiple values of the input grid in parallel +/// @param shareOp if true and @a threaded is true, all threads use the same functor; +/// otherwise, each thread gets its own copy of the @e original functor +/// @param merge how to merge intermediate results from multiple threads (see Types.h) +/// +/// @par Example: +/// Populate a scalar floating-point grid with the lengths of the vectors from all +/// active voxels of a vector-valued input grid. +/// @code +/// struct Local { +/// static void op( +/// const Vec3fGrid::ValueOnCIter& iter, +/// FloatGrid::ValueAccessor& accessor) +/// { +/// if (iter.isVoxelValue()) { // set a single voxel +/// accessor.setValue(iter.getCoord(), iter->length()); +/// } else { // fill an entire tile +/// CoordBBox bbox; +/// iter.getBoundingBox(bbox); +/// accessor.getTree()->fill(bbox, iter->length()); +/// } +/// } +/// }; +/// Vec3fGrid inGrid = ...; +/// FloatGrid outGrid; +/// tools::transformValues(inGrid.cbeginValueOn(), outGrid, Local::op); +/// @endcode +/// +/// @note For more complex operations that require finer control over threading, +/// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction +/// with a tree::IteratorRange that wraps a grid or tree iterator. +template +inline void transformValues(const InIterT& inIter, OutGridT& outGrid, + XformOp& op, bool threaded = true, bool shareOp = true, + MergePolicy merge = MERGE_ACTIVE_STATES); + +#ifndef _MSC_VER +template +inline void transformValues(const InIterT& inIter, OutGridT& outGrid, + const XformOp& op, bool threaded = true, bool shareOp = true, + MergePolicy merge = MERGE_ACTIVE_STATES); +#endif + + +/// Iterate over a grid and at each step call @c op(iter). If threading is enabled, +/// call @c op.join(otherOp) to accumulate intermediate results from pairs of threads. +/// @param iter an iterator over a grid or its tree (@c Grid::ValueOnCIter, +/// @c Tree::NodeIter, etc.) +/// @param op a functor with a join method of the form void join(XformOp&) +/// and a call method of the form void op(const IterT&), +/// where @c IterT is the type of @a iter +/// @param threaded if true, transform multiple values of the grid in parallel +/// @note If @a threaded is true, each thread gets its own copy of the @e original functor. +/// The order in which threads are joined is unspecified. +/// @note If @a threaded is false, the join method is never called. +/// +/// @par Example: +/// Compute the average of the active values of a scalar, floating-point grid +/// using the math::Stats class. +/// @code +/// namespace { +/// struct Average { +/// math::Stats stats; +/// +/// // Accumulate voxel and tile values into this functor's Stats object. +/// inline void operator()(const FloatGrid::ValueOnCIter& iter) { +/// if (iter.isVoxelValue()) stats.add(*iter); +/// else stats.add(*iter, iter.getVoxelCount()); +/// } +/// +/// // Accumulate another functor's Stats object into this functor's. +/// inline void join(Average& other) { stats.add(other.stats); } +/// +/// // Return the cumulative result. +/// inline double average() const { return stats.mean(); } +/// }; +/// } +/// { +/// FloatGrid grid = ...; +/// Average op; +/// tools::accumulate(grid.cbeginValueOn(), op); +/// double average = op.average(); +/// } +/// @endcode +/// +/// @note For more complex operations that require finer control over threading, +/// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction +/// with a tree::IteratorRange that wraps a grid or tree iterator. +template +inline void accumulate(const IterT& iter, XformOp& op, bool threaded = true); + + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the minimum of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template +inline void setValueOnMin(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the maximum of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template +inline void setValueOnMax(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the sum of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template +inline void setValueOnSum(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the product of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template +inline void setValueOnMult(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + + +//////////////////////////////////////// + + +namespace valxform { + +template +struct MinOp { + const ValueType val; + MinOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v = std::min(v, val); } +}; + +template +struct MaxOp { + const ValueType val; + MaxOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v = std::max(v, val); } +}; + +template +struct SumOp { + const ValueType val; + SumOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v += val; } +}; + + +template<> +struct SumOp { + using ValueType = bool; + const ValueType val; + SumOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v = v || val; } +}; + +template +struct MultOp { + const ValueType val; + MultOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v *= val; } +}; + +} + + +template +inline void +setValueOnMin(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::MinOp(value)); +} + + +template +inline void +setValueOnMax(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::MaxOp(value)); +} + + +template +inline void +setValueOnSum(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::SumOp(value)); +} + + +template +inline void +setValueOnMult(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::MultOp(value)); +} + + +//////////////////////////////////////// + + +namespace valxform { + +template +class SharedOpApplier +{ +public: + using IterRange = typename tree::IteratorRange; + + SharedOpApplier(const IterT& iter, OpT& op): mIter(iter), mOp(op) {} + + void process(bool threaded = true) + { + IterRange range(mIter); + if (threaded) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + } + + void operator()(IterRange& r) const { for ( ; r; ++r) mOp(r.iterator()); } + +private: + IterT mIter; + OpT& mOp; +}; + + +template +class CopyableOpApplier +{ +public: + using IterRange = typename tree::IteratorRange; + + CopyableOpApplier(const IterT& iter, const OpT& op): mIter(iter), mOp(op), mOrigOp(&op) {} + + // When splitting this task, give the subtask a copy of the original functor, + // not of this task's functor, which might have been modified arbitrarily. + CopyableOpApplier(const CopyableOpApplier& other): + mIter(other.mIter), mOp(*other.mOrigOp), mOrigOp(other.mOrigOp) {} + + void process(bool threaded = true) + { + IterRange range(mIter); + if (threaded) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + } + + void operator()(IterRange& r) const { for ( ; r; ++r) mOp(r.iterator()); } + +private: + IterT mIter; + OpT mOp; // copy of original functor + OpT const * const mOrigOp; // pointer to original functor +}; + +} // namespace valxform + + +template +inline void +foreach(const IterT& iter, XformOp& op, bool threaded, bool shared) +{ + if (shared) { + typename valxform::SharedOpApplier proc(iter, op); + proc.process(threaded); + } else { + using Processor = typename valxform::CopyableOpApplier; + Processor proc(iter, op); + proc.process(threaded); + } +} + +template +inline void +foreach(const IterT& iter, const XformOp& op, bool threaded, bool /*shared*/) +{ + // Const ops are shared across threads, not copied. + typename valxform::SharedOpApplier proc(iter, op); + proc.process(threaded); +} + + +//////////////////////////////////////// + + +namespace valxform { + +template +class SharedOpTransformer +{ +public: + using InTreeT = typename InIterT::TreeT; + using IterRange = typename tree::IteratorRange; + using OutValueT = typename OutTreeT::ValueType; + + SharedOpTransformer(const InIterT& inIter, OutTreeT& outTree, OpT& op, MergePolicy merge): + mIsRoot(true), + mInputIter(inIter), + mInputTree(inIter.getTree()), + mOutputTree(&outTree), + mOp(op), + mMergePolicy(merge) + { + if (static_cast(mInputTree) == static_cast(mOutputTree)) { + OPENVDB_LOG_INFO("use tools::foreach(), not transformValues()," + " to transform a grid in place"); + } + } + + /// Splitting constructor + SharedOpTransformer(SharedOpTransformer& other, tbb::split): + mIsRoot(false), + mInputIter(other.mInputIter), + mInputTree(other.mInputTree), + mOutputTree(new OutTreeT(zeroVal())), + mOp(other.mOp), + mMergePolicy(other.mMergePolicy) + {} + + ~SharedOpTransformer() + { + // Delete the output tree only if it was allocated locally + // (the top-level output tree was supplied by the caller). + if (!mIsRoot) { + delete mOutputTree; + mOutputTree = nullptr; + } + } + + void process(bool threaded = true) + { + if (!mInputTree || !mOutputTree) return; + + IterRange range(mInputIter); + + // Independently transform elements in the iterator range, + // either in parallel or serially. + if (threaded) { + tbb::parallel_reduce(range, *this); + } else { + (*this)(range); + } + } + + /// Transform each element in the given range. + void operator()(IterRange& range) const + { + if (!mOutputTree) return; + typename tree::ValueAccessor outAccessor(*mOutputTree); + for ( ; range; ++range) { + mOp(range.iterator(), outAccessor); + } + } + + void join(const SharedOpTransformer& other) + { + if (mOutputTree && other.mOutputTree) { + mOutputTree->merge(*other.mOutputTree, mMergePolicy); + } + } + +private: + bool mIsRoot; + InIterT mInputIter; + const InTreeT* mInputTree; + OutTreeT* mOutputTree; + OpT& mOp; + MergePolicy mMergePolicy; +}; // class SharedOpTransformer + + +template +class CopyableOpTransformer +{ +public: + using InTreeT = typename InIterT::TreeT; + using IterRange = typename tree::IteratorRange; + using OutValueT = typename OutTreeT::ValueType; + + CopyableOpTransformer(const InIterT& inIter, OutTreeT& outTree, + const OpT& op, MergePolicy merge): + mIsRoot(true), + mInputIter(inIter), + mInputTree(inIter.getTree()), + mOutputTree(&outTree), + mOp(op), + mOrigOp(&op), + mMergePolicy(merge) + { + if (static_cast(mInputTree) == static_cast(mOutputTree)) { + OPENVDB_LOG_INFO("use tools::foreach(), not transformValues()," + " to transform a grid in place"); + } + } + + // When splitting this task, give the subtask a copy of the original functor, + // not of this task's functor, which might have been modified arbitrarily. + CopyableOpTransformer(CopyableOpTransformer& other, tbb::split): + mIsRoot(false), + mInputIter(other.mInputIter), + mInputTree(other.mInputTree), + mOutputTree(new OutTreeT(zeroVal())), + mOp(*other.mOrigOp), + mOrigOp(other.mOrigOp), + mMergePolicy(other.mMergePolicy) + {} + + ~CopyableOpTransformer() + { + // Delete the output tree only if it was allocated locally + // (the top-level output tree was supplied by the caller). + if (!mIsRoot) { + delete mOutputTree; + mOutputTree = nullptr; + } + } + + void process(bool threaded = true) + { + if (!mInputTree || !mOutputTree) return; + + IterRange range(mInputIter); + + // Independently transform elements in the iterator range, + // either in parallel or serially. + if (threaded) { + tbb::parallel_reduce(range, *this); + } else { + (*this)(range); + } + } + + /// Transform each element in the given range. + void operator()(IterRange& range) + { + if (!mOutputTree) return; + typename tree::ValueAccessor outAccessor(*mOutputTree); + for ( ; range; ++range) { + mOp(range.iterator(), outAccessor); + } + } + + void join(const CopyableOpTransformer& other) + { + if (mOutputTree && other.mOutputTree) { + mOutputTree->merge(*other.mOutputTree, mMergePolicy); + } + } + +private: + bool mIsRoot; + InIterT mInputIter; + const InTreeT* mInputTree; + OutTreeT* mOutputTree; + OpT mOp; // copy of original functor + OpT const * const mOrigOp; // pointer to original functor + MergePolicy mMergePolicy; +}; // class CopyableOpTransformer + +} // namespace valxform + + +//////////////////////////////////////// + + +template +inline void +transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op, + bool threaded, bool shared, MergePolicy merge) +{ + using Adapter = TreeAdapter; + using OutTreeT = typename Adapter::TreeType; + if (shared) { + using Processor = typename valxform::SharedOpTransformer; + Processor proc(inIter, Adapter::tree(outGrid), op, merge); + proc.process(threaded); + } else { + using Processor = typename valxform::CopyableOpTransformer; + Processor proc(inIter, Adapter::tree(outGrid), op, merge); + proc.process(threaded); + } +} + +#ifndef _MSC_VER +template +inline void +transformValues(const InIterT& inIter, OutGridT& outGrid, const XformOp& op, + bool threaded, bool /*share*/, MergePolicy merge) +{ + using Adapter = TreeAdapter; + using OutTreeT = typename Adapter::TreeType; + // Const ops are shared across threads, not copied. + using Processor = typename valxform::SharedOpTransformer; + Processor proc(inIter, Adapter::tree(outGrid), op, merge); + proc.process(threaded); +} +#endif + + +//////////////////////////////////////// + + +namespace valxform { + +template +class OpAccumulator +{ +public: + using IterRange = typename tree::IteratorRange; + + // The root task makes a const copy of the original functor (mOrigOp) + // and keeps a pointer to the original functor (mOp), which it then modifies. + // Each subtask keeps a const pointer to the root task's mOrigOp + // and makes and then modifies a non-const copy (mOp) of it. + OpAccumulator(const IterT& iter, OpT& op): + mIsRoot(true), + mIter(iter), + mOp(&op), + mOrigOp(new OpT(op)) + {} + + // When splitting this task, give the subtask a copy of the original functor, + // not of this task's functor, which might have been modified arbitrarily. + OpAccumulator(OpAccumulator& other, tbb::split): + mIsRoot(false), + mIter(other.mIter), + mOp(new OpT(*other.mOrigOp)), + mOrigOp(other.mOrigOp) + {} + + ~OpAccumulator() { if (mIsRoot) delete mOrigOp; else delete mOp; } + + void process(bool threaded = true) + { + IterRange range(mIter); + if (threaded) { + tbb::parallel_reduce(range, *this); + } else { + (*this)(range); + } + } + + void operator()(IterRange& r) { for ( ; r; ++r) (*mOp)(r.iterator()); } + + void join(OpAccumulator& other) { mOp->join(*other.mOp); } + +private: + const bool mIsRoot; + const IterT mIter; + OpT* mOp; // pointer to original functor, which might get modified + OpT const * const mOrigOp; // const copy of original functor +}; // class OpAccumulator + +} // namespace valxform + + +//////////////////////////////////////// + + +template +inline void +accumulate(const IterT& iter, XformOp& op, bool threaded) +{ + typename valxform::OpAccumulator proc(iter, op); + proc.process(threaded); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/VectorTransformer.h b/openvdb/tools/VectorTransformer.h new file mode 100644 index 00000000..09c56365 --- /dev/null +++ b/openvdb/tools/VectorTransformer.h @@ -0,0 +1,134 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file VectorTransformer.h + +#ifndef OPENVDB_TOOLS_VECTORTRANSFORMER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VECTORTRANSFORMER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include "ValueTransformer.h" // for tools::foreach() +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Apply an affine transform to the voxel values of a vector-valued grid +/// in accordance with the grid's vector type (covariant, contravariant, etc.). +/// @throw TypeError if the grid is not vector-valued +template +inline void +transformVectors(GridType&, const Mat4d&); + + +//////////////////////////////////////// + + +// Functors for use with tools::foreach() to transform vector voxel values + +struct HomogeneousMatMul +{ + const Mat4d mat; + HomogeneousMatMul(const Mat4d& _mat): mat(_mat) {} + template void operator()(const TreeIterT& it) const + { + Vec3d v(*it); + it.setValue(mat.transformH(v)); + } +}; + +struct MatMul +{ + const Mat4d mat; + MatMul(const Mat4d& _mat): mat(_mat) {} + template + void operator()(const TreeIterT& it) const + { + Vec3d v(*it); + it.setValue(mat.transform3x3(v)); + } +}; + +struct MatMulNormalize +{ + const Mat4d mat; + MatMulNormalize(const Mat4d& _mat): mat(_mat) {} + template + void operator()(const TreeIterT& it) const + { + Vec3d v(*it); + v = mat.transform3x3(v); + v.normalize(); + it.setValue(v); + } +}; + + +//{ +/// @cond OPENVDB_VECTOR_TRANSFORMER_INTERNAL + +/// @internal This overload is enabled only for scalar-valued grids. +template inline +typename std::enable_if::IsVec, void>::type +doTransformVectors(GridType&, const Mat4d&) +{ + OPENVDB_THROW(TypeError, "tools::transformVectors() requires a vector-valued grid"); +} + +/// @internal This overload is enabled only for vector-valued grids. +template inline +typename std::enable_if::IsVec, void>::type +doTransformVectors(GridType& grid, const Mat4d& mat) +{ + if (!grid.isInWorldSpace()) return; + + const VecType vecType = grid.getVectorType(); + switch (vecType) { + case VEC_COVARIANT: + case VEC_COVARIANT_NORMALIZE: + { + Mat4d invmat = mat.inverse(); + invmat = invmat.transpose(); + + if (vecType == VEC_COVARIANT_NORMALIZE) { + foreach(grid.beginValueAll(), MatMulNormalize(invmat)); + } else { + foreach(grid.beginValueAll(), MatMul(invmat)); + } + break; + } + + case VEC_CONTRAVARIANT_RELATIVE: + foreach(grid.beginValueAll(), MatMul(mat)); + break; + + case VEC_CONTRAVARIANT_ABSOLUTE: + foreach(grid.beginValueAll(), HomogeneousMatMul(mat)); + break; + + case VEC_INVARIANT: + break; + } +} + +/// @endcond +//} + + +template +inline void +transformVectors(GridType& grid, const Mat4d& mat) +{ + doTransformVectors(grid, mat); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VECTORTRANSFORMER_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/VelocityFields.h b/openvdb/tools/VelocityFields.h new file mode 100644 index 00000000..8261307c --- /dev/null +++ b/openvdb/tools/VelocityFields.h @@ -0,0 +1,275 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/////////////////////////////////////////////////////////////////////////// +// +/// @author Ken Museth +/// +/// @file VelocityFields.h +/// +/// @brief Defines two simple wrapper classes for advection velocity +/// fields as well as VelocitySampler and VelocityIntegrator +/// +/// +/// @details DiscreteField wraps a velocity grid and EnrightField is mostly +/// intended for debugging (it's an analytical divergence free and +/// periodic field). They both share the same API required by the +/// LevelSetAdvection class defined in LevelSetAdvect.h. Thus, any +/// class with this API should work with LevelSetAdvection. +/// +/// @warning Note the Field wrapper classes below always assume the velocity +/// is represented in the world-frame of reference. For DiscreteField +/// this implies the input grid must contain velocities in world +/// coordinates. + +#ifndef OPENVDB_TOOLS_VELOCITY_FIELDS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VELOCITY_FIELDS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include "Interpolation.h" // for Sampler, etc. +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Thin wrapper class for a velocity grid +/// @note Consider replacing BoxSampler with StaggeredBoxSampler +template +class DiscreteField +{ +public: + typedef typename VelGridT::ValueType VectorType; + typedef typename VectorType::ValueType ValueType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + DiscreteField(const VelGridT &vel) + : mAccessor(vel.tree()) + , mTransform(&vel.transform()) + { + } + + /// @brief Copy constructor + DiscreteField(const DiscreteField& other) + : mAccessor(other.mAccessor.tree()) + , mTransform(other.mTransform) + { + } + + /// @return const reference to the transform between world and index space + /// @note Use this method to determine if a client grid is + /// aligned with the coordinate space of the velocity grid. + const math::Transform& transform() const { return *mTransform; } + + /// @return the interpolated velocity at the world space position xyz + /// + /// @warning Not threadsafe since it uses a ValueAccessor! So use + /// one instance per thread (which is fine since its lightweight). + inline VectorType operator() (const Vec3d& xyz, ValueType/*dummy time*/) const + { + return Interpolator::sample(mAccessor, mTransform->worldToIndex(xyz)); + } + + /// @return the velocity at the coordinate space position ijk + /// + /// @warning Not threadsafe since it uses a ValueAccessor! So use + /// one instance per thread (which is fine since its lightweight). + inline VectorType operator() (const Coord& ijk, ValueType/*dummy time*/) const + { + return mAccessor.getValue(ijk); + } + +private: + const typename VelGridT::ConstAccessor mAccessor;//Not thread-safe + const math::Transform* mTransform; + +}; // end of DiscreteField + +/////////////////////////////////////////////////////////////////////// + +/// @brief Analytical, divergence-free and periodic velocity field +/// @note Primarily intended for debugging! +/// @warning This analytical velocity only produce meaningful values +/// in the unit box in world space. In other words make sure any level +/// set surface is fully enclosed in the axis aligned bounding box +/// spanning 0->1 in world units. +template +class EnrightField +{ +public: + typedef ScalarT ValueType; + typedef math::Vec3 VectorType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + EnrightField() {} + + /// @return const reference to the identity transform between world and index space + /// @note Use this method to determine if a client grid is + /// aligned with the coordinate space of this velocity field + math::Transform transform() const { return math::Transform(); } + + /// @return the velocity in world units, evaluated at the world + /// position xyz and at the specified time + inline VectorType operator() (const Vec3d& xyz, ValueType time) const; + + /// @return the velocity at the coordinate space position ijk + inline VectorType operator() (const Coord& ijk, ValueType time) const + { + return (*this)(ijk.asVec3d(), time); + } +}; // end of EnrightField + +template +inline math::Vec3 +EnrightField::operator() (const Vec3d& xyz, ValueType time) const +{ + const ScalarT pi = boost::math::constants::pi(); + const ScalarT phase = pi / ScalarT(3); + const ScalarT Px = pi * ScalarT(xyz[0]), Py = pi * ScalarT(xyz[1]), Pz = pi * ScalarT(xyz[2]); + const ScalarT tr = math::Cos(ScalarT(time) * phase); + const ScalarT a = math::Sin(ScalarT(2)*Py); + const ScalarT b = -math::Sin(ScalarT(2)*Px); + const ScalarT c = math::Sin(ScalarT(2)*Pz); + return math::Vec3( + tr * ( ScalarT(2) * math::Pow2(math::Sin(Px)) * a * c ), + tr * ( b * math::Pow2(math::Sin(Py)) * c ), + tr * ( b * a * math::Pow2(math::Sin(Pz)) )); +} + + +/////////////////////////////////////////////////////////////////////// + +/// Class to hold a Vec3 field interpreted as a velocity field. +/// Primarily exists to provide a method(s) that integrate a passive +/// point forward in the velocity field for a single time-step (dt) +template +class VelocitySampler +{ +public: + typedef typename GridT::ConstAccessor AccessorType; + typedef typename GridT::ValueType ValueType; + + /// @brief Constructor from a grid + VelocitySampler(const GridT& grid): + mGrid(&grid), + mAcc(grid.getAccessor()) + { + } + /// @brief Copy-constructor + VelocitySampler(const VelocitySampler& other): + mGrid(other.mGrid), + mAcc(mGrid->getAccessor()) + { + } + /// @brief Samples the velocity at world position onto result. Supports both + /// staggered (i.e. MAC) and collocated velocity grids. + /// + /// @return @c true if any one of the sampled values is active. + /// + /// @warning Not threadsafe since it uses a ValueAccessor! So use + /// one instance per thread (which is fine since its lightweight). + template + inline bool sample(const LocationType& world, ValueType& result) const + { + const Vec3R xyz = mGrid->worldToIndex(Vec3R(world[0], world[1], world[2])); + bool active = Sampler::sample(mAcc, xyz, result); + return active; + } + + /// @brief Samples the velocity at world position onto result. Supports both + /// staggered (i.e. MAC) and co-located velocity grids. + /// + /// @warning Not threadsafe since it uses a ValueAccessor! So use + /// one instance per thread (which is fine since its lightweight). + template + inline ValueType sample(const LocationType& world) const + { + const Vec3R xyz = mGrid->worldToIndex(Vec3R(world[0], world[1], world[2])); + return Sampler::sample(mAcc, xyz); + } + +private: + // holding the Grids for the transforms + const GridT* mGrid; // Velocity vector field + AccessorType mAcc; +};// end of VelocitySampler class + +/////////////////////////////////////////////////////////////////////// + +/// @brief Performs Runge-Kutta time integration of variable order in +/// a static velocity field. +/// +/// @note Note that the order of the velocity sampling is controlled +/// with the SampleOrder template parameter, which defaults +/// to one, i.e. a tri-linear interpolation kernel. +template +class VelocityIntegrator +{ +public: + typedef typename GridT::ValueType VecType; + typedef typename VecType::ValueType ElementType; + + VelocityIntegrator(const GridT& velGrid): + mVelSampler(velGrid) + { + } + /// @brief Variable order Runge-Kutta time integration for a single time step + /// + /// @param dt Time sub-step for the Runge-Kutte integrator of order OrderRK + /// @param world Location in world space coordinates (both input and output) + template + inline void rungeKutta(const ElementType dt, LocationType& world) const + { + BOOST_STATIC_ASSERT(OrderRK <= 4); + VecType P(static_cast(world[0]), + static_cast(world[1]), + static_cast(world[2])); + // Note the if-branching below is optimized away at compile time + if (OrderRK == 0) { + return;// do nothing + } else if (OrderRK == 1) { + VecType V0; + mVelSampler.sample(P, V0); + P = dt * V0; + } else if (OrderRK == 2) { + VecType V0, V1; + mVelSampler.sample(P, V0); + mVelSampler.sample(P + ElementType(0.5) * dt * V0, V1); + P = dt * V1; + } else if (OrderRK == 3) { + VecType V0, V1, V2; + mVelSampler.sample(P, V0); + mVelSampler.sample(P + ElementType(0.5) * dt * V0, V1); + mVelSampler.sample(P + dt * (ElementType(2.0) * V1 - V0), V2); + P = dt * (V0 + ElementType(4.0) * V1 + V2) * ElementType(1.0 / 6.0); + } else if (OrderRK == 4) { + VecType V0, V1, V2, V3; + mVelSampler.sample(P, V0); + mVelSampler.sample(P + ElementType(0.5) * dt * V0, V1); + mVelSampler.sample(P + ElementType(0.5) * dt * V1, V2); + mVelSampler.sample(P + dt * V2, V3); + P = dt * (V0 + ElementType(2.0) * (V1 + V2) + V3) * ElementType(1.0 / 6.0); + } + typedef typename LocationType::ValueType OutType; + world += LocationType(static_cast(P[0]), + static_cast(P[1]), + static_cast(P[2])); + } +private: + VelocitySampler mVelSampler; +};// end of VelocityIntegrator class + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VELOCITY_FIELDS_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/VolumeAdvect.h b/openvdb/tools/VolumeAdvect.h new file mode 100644 index 00000000..35b5ed9c --- /dev/null +++ b/openvdb/tools/VolumeAdvect.h @@ -0,0 +1,541 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/////////////////////////////////////////////////////////////////////////// +// +/// @author Ken Museth +/// +/// @file tools/VolumeAdvect.h +/// +/// @brief Sparse hyperbolic advection of volumes, e.g. a density or +/// velocity (vs a level set interface). + +#ifndef OPENVDB_TOOLS_VOLUME_ADVECT_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VOLUME_ADVECT_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include "Interpolation.h"// for Sampler +#include "VelocityFields.h" // for VelocityIntegrator +#include "Morphology.h"//for dilateActiveValues and dilateVoxels +#include "Prune.h"// for prune +#include "Statistics.h" // for extrema +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +namespace Scheme { + /// @brief Numerical advections schemes. + enum SemiLagrangian { SEMI, MID, RK3, RK4, MAC, BFECC }; + /// @brief Flux-limiters employed to stabalize the second-order + /// advection schemes MacCormack and BFECC. + enum Limiter { NO_LIMITER, CLAMP, REVERT }; +} + +/// @brief Performs advections of an arbitrary type of volume in a +/// static velocity field. The advections are performed by means +/// of various derivatives of Semi-Lagrangian integration, i.e. +/// backwards tracking along the hyperbolic characteristics +/// followed by interpolation. +/// +/// @note Optionally a limiter can be combined with the higher-order +/// integration schemes MacCormack and BFECC. There are two +/// types of limiters (CLAMP and REVERT) that supress +/// non-physical oscillations by means of either claminging or +/// reverting to a first-order schemes when the function is not +/// bounded by the cell values used for tri-linear interpolation. +/// +/// @verbatim The supported integrations schemes: +/// +/// ================================================================ +/// | Lable | Accuracy | Integration Scheme | Interpolations | +/// | |Time/Space| | velocity/volume | +/// ================================================================ +/// | SEMI | 1/1 | Semi-Lagrangian | 1/1 | +/// | MID | 2/1 | Mid-Point | 2/1 | +/// | RK3 | 3/1 | 3rd Order Runge-Kutta | 3/1 | +/// | RK4 | 4/1 | 4th Order Runge-Kutta | 4/1 | +/// | MAC | 2/2 | MacCormack | 2/2 | +/// | BFECC | 2/2 | BFECC | 3/2 | +/// ================================================================ +/// @endverbatim + +template +class VolumeAdvection +{ +public: + + /// @brief Constructor + /// + /// @param velGrid Velocity grid responsible for the (passive) advection. + /// @param interrupter Optional interrupter used to prematurely end computations. + /// + /// @note The velocity field is assumed to be constant for the duration of the + /// advection. + VolumeAdvection(const VelocityGridT& velGrid, InterrupterType* interrupter = nullptr) + : mVelGrid(velGrid) + , mInterrupter(interrupter) + , mIntegrator( Scheme::SEMI ) + , mLimiter( Scheme::CLAMP ) + , mGrainSize( 128 ) + , mSubSteps( 1 ) + { + math::Extrema e = extrema(velGrid.cbeginValueAll(), /*threading*/true); + e.add(velGrid.background().length()); + mMaxVelocity = e.max(); + } + + virtual ~VolumeAdvection() + { + } + + /// @brief Return the spatial order of accuracy of the advection scheme + /// + /// @note This is the optimal order in smooth regions. In + /// non-smooth regions the flux-limiter will drop the order of + /// accuracy to add numerical dissipation. + int spatialOrder() const { return (mIntegrator == Scheme::MAC || + mIntegrator == Scheme::BFECC) ? 2 : 1; } + + /// @brief Return the temporal order of accuracy of the advection scheme + /// + /// @note This is the optimal order in smooth regions. In + /// non-smooth regions the flux-limiter will drop the order of + /// accuracy to add numerical dissipation. + int temporalOrder() const { + switch (mIntegrator) { + case Scheme::SEMI: return 1; + case Scheme::MID: return 2; + case Scheme::RK3: return 3; + case Scheme::RK4: return 4; + case Scheme::BFECC:return 2; + case Scheme::MAC: return 2; + } + return 0;//should never reach this point + } + + /// @brief Set the integrator (see details in the table above) + void setIntegrator(Scheme::SemiLagrangian integrator) { mIntegrator = integrator; } + + /// @brief Return the integrator (see details in the table above) + Scheme::SemiLagrangian getIntegrator() const { return mIntegrator; } + + /// @brief Set the limiter (see details above) + void setLimiter(Scheme::Limiter limiter) { mLimiter = limiter; } + + /// @brief Retrun the limiter (see details above) + Scheme::Limiter getLimiter() const { return mLimiter; } + + /// @brief Return @c true if a limiter will be applied based on + /// the current settings. + bool isLimiterOn() const { return this->spatialOrder()>1 && + mLimiter != Scheme::NO_LIMITER; } + + /// @return the grain-size used for multi-threading + /// @note A grainsize of 0 implies serial execution + size_t getGrainSize() const { return mGrainSize; } + + /// @brief Set the grain-size used for multi-threading + /// @note A grainsize of 0 disables multi-threading + /// @warning A small grainsize can degrade performance, + /// both in terms of time and memory footprint! + void setGrainSize(size_t grainsize) { mGrainSize = grainsize; } + + /// @return the number of sub-steps per integration (always larger + /// than or equal to 1). + int getSubSteps() const { return mSubSteps; } + + /// @brief Set the number of sub-steps per integration. + /// @note The only reason to increase the sub-step above its + /// default value of one is to reduce the memory footprint + /// due to significant dilation. Values smaller than 1 will + /// be clamped to 1! + void setSubSteps(int substeps) { mSubSteps = math::Max(1, substeps); } + + /// @brief Return the maximum magnitude of the velocity in the + /// advection velocity field defined during construction. + double getMaxVelocity() const { return mMaxVelocity; } + + /// @return Returns the maximum distance in voxel units of @a inGrid + /// that a particle can travel in the time-step @a dt when advected + /// in the velocity field defined during construction. + /// + /// @details This method is useful when dilating sparse volume + /// grids to pad boundary regions. Excessive dilation can be + /// computationally expensive so use this method to prevent + /// or warn against run-away computation. + /// + /// @throw RuntimeError if @a inGrid does not have uniform voxels. + template + int getMaxDistance(const VolumeGridT& inGrid, double dt) const + { + if (!inGrid.hasUniformVoxels()) { + OPENVDB_THROW(RuntimeError, "Volume grid does not have uniform voxels!"); + } + const double d = mMaxVelocity*math::Abs(dt)/inGrid.voxelSize()[0]; + return static_cast( math::RoundUp(d) ); + } + + /// @return Returns a new grid that is the result of passive advection + /// of all the active values the input grid by @a timeStep. + /// + /// @param inGrid The input grid to be advected (unmodified) + /// @param timeStep Time-step of the Runge-Kutta integrator. + /// + /// @details This method will advect all of the active values in + /// the input @a inGrid. To achieve this a + /// deep-copy is dilated to account for the material + /// transport. This dilation step can be slow for large + /// time steps @a dt or a velocity field with large magnitudes. + /// + /// @warning If the VolumeSamplerT is of higher order than one + /// (i.e. tri-linear interpolation) instabilities are + /// known to occure. To suppress those monotonicity + /// constrains or flux-limiters need to be applies. + /// + /// @throw RuntimeError if @a inGrid does not have uniform voxels. + template//only C++11 allows for a default argument + typename VolumeGridT::Ptr advect(const VolumeGridT& inGrid, double timeStep) + { + typename VolumeGridT::Ptr outGrid = inGrid.deepCopy(); + const double dt = timeStep/mSubSteps; + const int n = this->getMaxDistance(inGrid, dt); + dilateActiveValues( outGrid->tree(), n, NN_FACE, EXPAND_TILES); + this->template cook(*outGrid, inGrid, dt); + for (int step = 1; step < mSubSteps; ++step) { + typename VolumeGridT::Ptr tmpGrid = outGrid->deepCopy(); + dilateActiveValues( tmpGrid->tree(), n, NN_FACE, EXPAND_TILES); + this->template cook(*tmpGrid, *outGrid, dt); + outGrid.swap( tmpGrid ); + } + + return outGrid; + } + + /// @return Returns a new grid that is the result of + /// passive advection of the active values in @a inGrid + /// that intersect the active values in @c mask. The time + /// of the output grid is incremented by @a timeStep. + /// + /// @param inGrid The input grid to be advected (unmodified). + /// @param mask The mask of active values defining the active voxels + /// in @c inGrid on which to perform advection. Only + /// if a value is active in both grids will it be modified. + /// @param timeStep Time-step for a single Runge-Kutta integration step. + /// + /// + /// @details This method will advect all of the active values in + /// the input @a inGrid that intersects with the + /// active values in @a mask. To achieve this a + /// deep-copy is dilated to account for the material + /// transport and finally cropped to the intersection + /// with @a mask. The dilation step can be slow for large + /// time steps @a dt or fast moving velocity fields. + /// + /// @warning If the VolumeSamplerT is of higher order the one + /// (i.e. tri-linear interpolation) instabilities are + /// known to occure. To suppress those monotonicity + /// constrains or flux-limiters need to be applies. + /// + /// @throw RuntimeError if @a inGrid is not aligned with @a mask + /// or if its voxels are not uniform. + template//only C++11 allows for a default argument + typename VolumeGridT::Ptr advect(const VolumeGridT& inGrid, const MaskGridT& mask, double timeStep) + { + if (inGrid.transform() != mask.transform()) { + OPENVDB_THROW(RuntimeError, "Volume grid and mask grid are misaligned! Consider " + "resampling either of the two grids into the index space of the other."); + } + typename VolumeGridT::Ptr outGrid = inGrid.deepCopy(); + const double dt = timeStep/mSubSteps; + const int n = this->getMaxDistance(inGrid, dt); + dilateActiveValues( outGrid->tree(), n, NN_FACE, EXPAND_TILES); + outGrid->topologyIntersection( mask ); + pruneInactive( outGrid->tree(), mGrainSize>0, mGrainSize ); + this->template cook(*outGrid, inGrid, dt); + outGrid->topologyUnion( inGrid ); + + for (int step = 1; step < mSubSteps; ++step) { + typename VolumeGridT::Ptr tmpGrid = outGrid->deepCopy(); + dilateActiveValues( tmpGrid->tree(), n, NN_FACE, EXPAND_TILES); + tmpGrid->topologyIntersection( mask ); + pruneInactive( tmpGrid->tree(), mGrainSize>0, mGrainSize ); + this->template cook(*tmpGrid, *outGrid, dt); + tmpGrid->topologyUnion( inGrid ); + outGrid.swap( tmpGrid ); + } + return outGrid; + } + +private: + // disallow copy construction and copy by assignment! + VolumeAdvection(const VolumeAdvection&);// not implemented + VolumeAdvection& operator=(const VolumeAdvection&);// not implemented + + void start(const char* str) const + { + if (mInterrupter) mInterrupter->start(str); + } + void stop() const + { + if (mInterrupter) mInterrupter->end(); + } + bool interrupt() const + { + if (mInterrupter && util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return true; + } + return false; + } + + template + void cook(VolumeGridT& outGrid, const VolumeGridT& inGrid, double dt) + { + switch (mIntegrator) { + case Scheme::SEMI: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::MID: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::RK3: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::RK4: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::BFECC: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::MAC: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + default: + OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); + } + pruneInactive(outGrid.tree(), mGrainSize>0, mGrainSize); + } + + // Private class that implements the multi-threaded advection + template struct Advect; + + // Private member data of VolumeAdvection + const VelocityGridT& mVelGrid; + double mMaxVelocity; + InterrupterType* mInterrupter; + Scheme::SemiLagrangian mIntegrator; + Scheme::Limiter mLimiter; + size_t mGrainSize; + int mSubSteps; +};//end of VolumeAdvection class + +// Private class that implements the multi-threaded advection +template +template +struct VolumeAdvection::Advect +{ + using TreeT = typename VolumeGridT::TreeType; + using AccT = typename VolumeGridT::ConstAccessor; + using ValueT = typename TreeT::ValueType; + using LeafManagerT = typename tree::LeafManager; + using LeafNodeT = typename LeafManagerT::LeafNodeType; + using LeafRangeT = typename LeafManagerT::LeafRange; + using VelocityIntegratorT = VelocityIntegrator; + using RealT = typename VelocityIntegratorT::ElementType; + using VoxelIterT = typename TreeT::LeafNodeType::ValueOnIter; + + Advect(const VolumeGridT& inGrid, const VolumeAdvection& parent) + : mTask(nullptr) + , mInGrid(&inGrid) + , mVelocityInt(parent.mVelGrid) + , mParent(&parent) + { + } + inline void cook(const LeafRangeT& range) + { + if (mParent->mGrainSize > 0) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + } + void operator()(const LeafRangeT& range) const + { + assert(mTask); + mTask(const_cast(this), range); + } + void cook(VolumeGridT& outGrid, double time_step) + { + namespace ph = std::placeholders; + + mParent->start("Advecting volume"); + LeafManagerT manager(outGrid.tree(), mParent->spatialOrder()==2 ? 1 : 0); + const LeafRangeT range = manager.leafRange(mParent->mGrainSize); + const RealT dt = static_cast(-time_step);//method of characteristics backtracks + if (mParent->mIntegrator == Scheme::MAC) { + mTask = std::bind(&Advect::rk, ph::_1, ph::_2, dt, 0, mInGrid);//out[0]=forward + this->cook(range); + mTask = std::bind(&Advect::rk, ph::_1, ph::_2,-dt, 1, &outGrid);//out[1]=backward + this->cook(range); + mTask = std::bind(&Advect::mac, ph::_1, ph::_2);//out[0] = out[0] + (in[0] - out[1])/2 + this->cook(range); + } else if (mParent->mIntegrator == Scheme::BFECC) { + mTask = std::bind(&Advect::rk, ph::_1, ph::_2, dt, 0, mInGrid);//out[0]=forward + this->cook(range); + mTask = std::bind(&Advect::rk, ph::_1, ph::_2,-dt, 1, &outGrid);//out[1]=backward + this->cook(range); + mTask = std::bind(&Advect::bfecc, ph::_1, ph::_2);//out[0] = (3*in[0] - out[1])/2 + this->cook(range); + mTask = std::bind(&Advect::rk, ph::_1, ph::_2, dt, 1, &outGrid);//out[1]=forward + this->cook(range); + manager.swapLeafBuffer(1);// out[0] = out[1] + } else {// SEMI, MID, RK3 and RK4 + mTask = std::bind(&Advect::rk, ph::_1, ph::_2, dt, 0, mInGrid);//forward + this->cook(range); + } + + if (mParent->spatialOrder()==2) manager.removeAuxBuffers(); + + mTask = std::bind(&Advect::limiter, ph::_1, ph::_2, dt);// out[0] = limiter( out[0] ) + this->cook(range); + + mParent->stop(); + } + // Last step of the MacCormack scheme: out[0] = out[0] + (in[0] - out[1])/2 + void mac(const LeafRangeT& range) const + { + if (mParent->interrupt()) return; + assert( mParent->mIntegrator == Scheme::MAC ); + AccT acc = mInGrid->getAccessor(); + for (typename LeafRangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueT* out0 = leafIter.buffer( 0 ).data();// forward + const ValueT* out1 = leafIter.buffer( 1 ).data();// backward + const LeafNodeT* leaf = acc.probeConstLeaf( leafIter->origin() ); + if (leaf != nullptr) { + const ValueT* in0 = leaf->buffer().data(); + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Index i = voxelIter.pos(); + out0[i] += RealT(0.5) * ( in0[i] - out1[i] ); + } + } else { + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Index i = voxelIter.pos(); + out0[i] += RealT(0.5) * ( acc.getValue(voxelIter.getCoord()) - out1[i] ); + }//loop over active voxels + } + }//loop over leaf nodes + } + // Intermediate step in the BFECC scheme: out[0] = (3*in[0] - out[1])/2 + void bfecc(const LeafRangeT& range) const + { + if (mParent->interrupt()) return; + assert( mParent->mIntegrator == Scheme::BFECC ); + AccT acc = mInGrid->getAccessor(); + for (typename LeafRangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueT* out0 = leafIter.buffer( 0 ).data();// forward + const ValueT* out1 = leafIter.buffer( 1 ).data();// backward + const LeafNodeT* leaf = acc.probeConstLeaf(leafIter->origin()); + if (leaf != nullptr) { + const ValueT* in0 = leaf->buffer().data(); + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Index i = voxelIter.pos(); + out0[i] = RealT(0.5)*( RealT(3)*in0[i] - out1[i] ); + }//loop over active voxels + } else { + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Index i = voxelIter.pos(); + out0[i] = RealT(0.5)*( RealT(3)*acc.getValue(voxelIter.getCoord()) - out1[i] ); + }//loop over active voxels + } + }//loop over leaf nodes + } + // Semi-Lagrangian integration with Runge-Kutta of various orders (1->4) + void rk(const LeafRangeT& range, RealT dt, size_t n, const VolumeGridT* grid) const + { + if (mParent->interrupt()) return; + const math::Transform& xform = mInGrid->transform(); + AccT acc = grid->getAccessor(); + for (typename LeafRangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueT* phi = leafIter.buffer( n ).data(); + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + ValueT& value = phi[voxelIter.pos()]; + Vec3d wPos = xform.indexToWorld(voxelIter.getCoord()); + mVelocityInt.template rungeKutta(dt, wPos); + value = SamplerT::sample(acc, xform.worldToIndex(wPos)); + }//loop over active voxels + }//loop over leaf nodes + } + void limiter(const LeafRangeT& range, RealT dt) const + { + if (mParent->interrupt()) return; + const bool doLimiter = mParent->isLimiterOn(); + const bool doClamp = mParent->mLimiter == Scheme::CLAMP; + ValueT data[2][2][2], vMin, vMax; + const math::Transform& xform = mInGrid->transform(); + AccT acc = mInGrid->getAccessor(); + const ValueT backg = mInGrid->background(); + for (typename LeafRangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueT* phi = leafIter.buffer( 0 ).data(); + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + ValueT& value = phi[voxelIter.pos()]; + + if ( doLimiter ) { + assert(OrderRK == 1); + Vec3d wPos = xform.indexToWorld(voxelIter.getCoord()); + mVelocityInt.template rungeKutta<1, Vec3d>(dt, wPos);// Explicit Euler + Vec3d iPos = xform.worldToIndex(wPos); + Coord ijk = Coord::floor( iPos ); + BoxSampler::getValues(data, acc, ijk); + BoxSampler::extrema(data, vMin, vMax); + if ( doClamp ) { + value = math::Clamp( value, vMin, vMax); + } else if (value < vMin || value > vMax ) { + iPos -= Vec3R(ijk[0], ijk[1], ijk[2]);//unit coordinates + value = BoxSampler::trilinearInterpolation( data, iPos ); + } + } + + if (math::isApproxEqual(value, backg, math::Delta::value())) { + value = backg; + leafIter->setValueOff( voxelIter.pos() ); + } + }//loop over active voxels + }//loop over leaf nodes + } + // Public member data of the private Advect class + + typename std::function mTask; + const VolumeGridT* mInGrid; + const VelocityIntegratorT mVelocityInt;// lightweight! + const VolumeAdvection* mParent; +};// end of private member class Advect + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VOLUME_ADVECT_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/VolumeToMesh.h b/openvdb/tools/VolumeToMesh.h new file mode 100644 index 00000000..375403e0 --- /dev/null +++ b/openvdb/tools/VolumeToMesh.h @@ -0,0 +1,5262 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file VolumeToMesh.h +/// +/// @brief Extract polygonal surfaces from scalar volumes. +/// +/// @author Mihai Alden + +#ifndef OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED + +#include +#include // for ISGradient +#include +#include // for INVALID_IDX + +#include +#include +#include +#include + +#include // for std::isfinite() +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +//////////////////////////////////////// + + +// Wrapper functions for the VolumeToMesh converter + + +/// @brief Uniformly mesh any scalar grid that has a continuous isosurface. +/// +/// @param grid a scalar grid to mesh +/// @param points output list of world space points +/// @param quads output quad index list +/// @param isovalue determines which isosurface to mesh +/// +/// @throw TypeError if @a grid does not have a scalar value type +template +inline void +volumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& quads, + double isovalue = 0.0); + + +/// @brief Adaptively mesh any scalar grid that has a continuous isosurface. +/// +/// @param grid a scalar grid to mesh +/// @param points output list of world space points +/// @param triangles output triangle index list +/// @param quads output quad index list +/// @param isovalue determines which isosurface to mesh +/// @param adaptivity surface adaptivity threshold [0 to 1] +/// @param relaxDisorientedTriangles toggle relaxing disoriented triangles during +/// adaptive meshing. +/// +/// @throw TypeError if @a grid does not have a scalar value type +template +inline void +volumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& triangles, + std::vector& quads, + double isovalue = 0.0, + double adaptivity = 0.0, + bool relaxDisorientedTriangles = true); + + +//////////////////////////////////////// + + +/// @brief Polygon flags, used for reference based meshing. +enum { POLYFLAG_EXTERIOR = 0x1, POLYFLAG_FRACTURE_SEAM = 0x2, POLYFLAG_SUBDIVIDED = 0x4 }; + + +/// @brief Collection of quads and triangles +class PolygonPool +{ +public: + + inline PolygonPool(); + inline PolygonPool(const size_t numQuads, const size_t numTriangles); + + inline void copy(const PolygonPool& rhs); + + inline void resetQuads(size_t size); + inline void clearQuads(); + + inline void resetTriangles(size_t size); + inline void clearTriangles(); + + + // polygon accessor methods + + const size_t& numQuads() const { return mNumQuads; } + + openvdb::Vec4I& quad(size_t n) { return mQuads[n]; } + const openvdb::Vec4I& quad(size_t n) const { return mQuads[n]; } + + + const size_t& numTriangles() const { return mNumTriangles; } + + openvdb::Vec3I& triangle(size_t n) { return mTriangles[n]; } + const openvdb::Vec3I& triangle(size_t n) const { return mTriangles[n]; } + + + // polygon flags accessor methods + + char& quadFlags(size_t n) { return mQuadFlags[n]; } + const char& quadFlags(size_t n) const { return mQuadFlags[n]; } + + char& triangleFlags(size_t n) { return mTriangleFlags[n]; } + const char& triangleFlags(size_t n) const { return mTriangleFlags[n]; } + + + // reduce the polygon containers, n has to + // be smaller than the current container size. + + inline bool trimQuads(const size_t n, bool reallocate = false); + inline bool trimTrinagles(const size_t n, bool reallocate = false); + +private: + // disallow copy by assignment + void operator=(const PolygonPool&) {} + + size_t mNumQuads, mNumTriangles; + std::unique_ptr mQuads; + std::unique_ptr mTriangles; + std::unique_ptr mQuadFlags, mTriangleFlags; +}; + + +/// @{ +/// @brief Point and primitive list types. +using PointList = std::unique_ptr; +using PolygonPoolList = std::unique_ptr; +/// @} + + +//////////////////////////////////////// + + +/// @brief Mesh any scalar grid that has a continuous isosurface. +struct VolumeToMesh +{ + + /// @param isovalue Determines which isosurface to mesh. + /// @param adaptivity Adaptivity threshold [0 to 1] + /// @param relaxDisorientedTriangles Toggle relaxing disoriented triangles during + /// adaptive meshing. + VolumeToMesh(double isovalue = 0, double adaptivity = 0, bool relaxDisorientedTriangles = true); + + ////////// + + /// @{ + // Mesh data accessors + + size_t pointListSize() const { return mPointListSize; } + PointList& pointList() { return mPoints; } + const PointList& pointList() const { return mPoints; } + + size_t polygonPoolListSize() const { return mPolygonPoolListSize; } + PolygonPoolList& polygonPoolList() { return mPolygons; } + const PolygonPoolList& polygonPoolList() const { return mPolygons; } + + std::vector& pointFlags() { return mPointFlags; } + const std::vector& pointFlags() const { return mPointFlags; } + /// @} + + + ////////// + + + /// @brief Main call + /// @note Call with scalar typed grid. + template + void operator()(const InputGridType&); + + + ////////// + + + /// @brief When surfacing fractured SDF fragments, the original unfractured + /// SDF grid can be used to eliminate seam lines and tag polygons that are + /// coincident with the reference surface with the @c POLYFLAG_EXTERIOR + /// flag and polygons that are in proximity to the seam lines with the + /// @c POLYFLAG_FRACTURE_SEAM flag. (The performance cost for using this + /// reference based scheme compared to the regular meshing scheme is + /// approximately 15% for the first fragment and neglect-able for + /// subsequent fragments.) + /// + /// @note Attributes from the original asset such as uv coordinates, normals etc. + /// are typically transfered to polygons that are marked with the + /// @c POLYFLAG_EXTERIOR flag. Polygons that are not marked with this flag + /// are interior to reference surface and might need projected UV coordinates + /// or a different material. Polygons marked as @c POLYFLAG_FRACTURE_SEAM can + /// be used to drive secondary elements such as debris and dust in a FX pipeline. + /// + /// @param grid reference surface grid of @c GridT type. + /// @param secAdaptivity Secondary adaptivity threshold [0 to 1]. Used in regions + /// that do not exist in the reference grid. (Parts of the + /// fragment surface that are not coincident with the + /// reference surface.) + void setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity = 0); + + + /// @param mask A boolean grid whose active topology defines the region to mesh. + /// @param invertMask Toggle to mesh the complement of the mask. + /// @note The mask's tree configuration has to match @c GridT's tree configuration. + void setSurfaceMask(const GridBase::ConstPtr& mask, bool invertMask = false); + + /// @param grid A scalar grid used as a spatial multiplier for the adaptivity threshold. + /// @note The grid's tree configuration has to match @c GridT's tree configuration. + void setSpatialAdaptivity(const GridBase::ConstPtr& grid); + + + /// @param tree A boolean tree whose active topology defines the adaptivity mask. + /// @note The tree configuration has to match @c GridT's tree configuration. + void setAdaptivityMask(const TreeBase::ConstPtr& tree); + +private: + // Disallow copying + VolumeToMesh(const VolumeToMesh&); + VolumeToMesh& operator=(const VolumeToMesh&); + + + PointList mPoints; + PolygonPoolList mPolygons; + + size_t mPointListSize, mSeamPointListSize, mPolygonPoolListSize; + double mIsovalue, mPrimAdaptivity, mSecAdaptivity; + + GridBase::ConstPtr mRefGrid, mSurfaceMaskGrid, mAdaptivityGrid; + TreeBase::ConstPtr mAdaptivityMaskTree; + + TreeBase::Ptr mRefSignTree, mRefIdxTree; + + bool mInvertSurfaceMask, mRelaxDisorientedTriangles; + + std::unique_ptr mQuantizedSeamPoints; + std::vector mPointFlags; +}; // struct VolumeToMesh + + +//////////////////////////////////////// + + +/// @brief Given a set of tangent elements, @c points with corresponding @c normals, +/// this method returns the intersection point of all tangent elements. +/// +/// @note Used to extract surfaces with sharp edges and corners from volume data, +/// see the following paper for details: "Feature Sensitive Surface +/// Extraction from Volume Data, Kobbelt et al. 2001". +inline Vec3d findFeaturePoint( + const std::vector& points, + const std::vector& normals) +{ + using Mat3d = math::Mat3d; + + Vec3d avgPos(0.0); + + if (points.empty()) return avgPos; + + for (size_t n = 0, N = points.size(); n < N; ++n) { + avgPos += points[n]; + } + + avgPos /= double(points.size()); + + // Unique components of the 3x3 A^TA matrix, where A is + // the matrix of normals. + double m00=0,m01=0,m02=0, + m11=0,m12=0, + m22=0; + + // The rhs vector, A^Tb, where b = n dot p + Vec3d rhs(0.0); + + for (size_t n = 0, N = points.size(); n < N; ++n) { + + const Vec3d& n_ref = normals[n]; + + // A^TA + m00 += n_ref[0] * n_ref[0]; // diagonal + m11 += n_ref[1] * n_ref[1]; + m22 += n_ref[2] * n_ref[2]; + + m01 += n_ref[0] * n_ref[1]; // Upper-tri + m02 += n_ref[0] * n_ref[2]; + m12 += n_ref[1] * n_ref[2]; + + // A^Tb (centered around the origin) + rhs += n_ref * n_ref.dot(points[n] - avgPos); + } + + Mat3d A(m00,m01,m02, + m01,m11,m12, + m02,m12,m22); + + /* + // Inverse + const double det = A.det(); + if (det > 0.01) { + Mat3d A_inv = A.adjoint(); + A_inv *= (1.0 / det); + + return avgPos + A_inv * rhs; + } + */ + + // Compute the pseudo inverse + + math::Mat3d eigenVectors; + Vec3d eigenValues; + + diagonalizeSymmetricMatrix(A, eigenVectors, eigenValues, 300); + + Mat3d D = Mat3d::identity(); + + + double tolerance = std::max(std::abs(eigenValues[0]), std::abs(eigenValues[1])); + tolerance = std::max(tolerance, std::abs(eigenValues[2])); + tolerance *= 0.01; + + int clamped = 0; + for (int i = 0; i < 3; ++i ) { + if (std::abs(eigenValues[i]) < tolerance) { + D[i][i] = 0.0; + ++clamped; + } else { + D[i][i] = 1.0 / eigenValues[i]; + } + } + + // Assemble the pseudo inverse and calc. the intersection point + if (clamped < 3) { + Mat3d pseudoInv = eigenVectors * D * eigenVectors.transpose(); + return avgPos + pseudoInv * rhs; + } + + return avgPos; +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +// Internal utility objects and implementation details + + +namespace volume_to_mesh_internal { + +template +struct FillArray +{ + FillArray(ValueType* array, const ValueType& v) : mArray(array), mValue(v) { } + + void operator()(const tbb::blocked_range& range) const { + const ValueType v = mValue; + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + mArray[n] = v; + } + } + + ValueType * const mArray; + const ValueType mValue; +}; + + +template +inline void +fillArray(ValueType* array, const ValueType& val, const size_t length) +{ + const auto grainSize = std::max( + length / tbb::task_scheduler_init::default_num_threads(), 1024); + const tbb::blocked_range range(0, length, grainSize); + tbb::parallel_for(range, FillArray(array, val), tbb::simple_partitioner()); +} + + +/// @brief Bit-flags used to classify cells. +enum { SIGNS = 0xFF, EDGES = 0xE00, INSIDE = 0x100, + XEDGE = 0x200, YEDGE = 0x400, ZEDGE = 0x800, SEAM = 0x1000}; + + +/// @brief Used to quickly determine if a given cell is adaptable. +const bool sAdaptable[256] = { + 1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1, + 1,0,1,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,0,1, + 1,0,0,0,1,0,1,1,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,0,1,1,1,0,1,1,0,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,0,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,1,0,0,0,1, + 1,0,0,0,1,0,1,0,1,1,0,0,1,1,1,1,1,1,0,0,1,0,0,0,1,1,0,0,1,1,0,1, + 1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1}; + + +/// @brief Contains the ambiguous face index for certain cell configuration. +const unsigned char sAmbiguousFace[256] = { + 0,0,0,0,0,5,0,0,0,0,5,0,0,0,0,0,0,0,1,0,0,5,1,0,4,0,0,0,4,0,0,0, + 0,1,0,0,2,0,0,0,0,1,5,0,2,0,0,0,0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,0, + 0,0,2,2,0,5,0,0,3,3,0,0,0,0,0,0,6,6,0,0,6,0,0,0,0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,4,0,4,3,0,3,0,0,0,5,0,0,0,0,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0, + 6,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + +/// @brief Lookup table for different cell sign configurations. The first entry specifies +/// the total number of points that need to be generated inside a cell and the +/// remaining 12 entries indicate different edge groups. +const unsigned char sEdgeGroupTable[256][13] = { + {0,0,0,0,0,0,0,0,0,0,0,0,0},{1,1,0,0,1,0,0,0,0,1,0,0,0},{1,1,1,0,0,0,0,0,0,0,1,0,0}, + {1,0,1,0,1,0,0,0,0,1,1,0,0},{1,0,1,1,0,0,0,0,0,0,0,1,0},{1,1,1,1,1,0,0,0,0,1,0,1,0}, + {1,1,0,1,0,0,0,0,0,0,1,1,0},{1,0,0,1,1,0,0,0,0,1,1,1,0},{1,0,0,1,1,0,0,0,0,0,0,0,1}, + {1,1,0,1,0,0,0,0,0,1,0,0,1},{1,1,1,1,1,0,0,0,0,0,1,0,1},{1,0,1,1,0,0,0,0,0,1,1,0,1}, + {1,0,1,0,1,0,0,0,0,0,0,1,1},{1,1,1,0,0,0,0,0,0,1,0,1,1},{1,1,0,0,1,0,0,0,0,0,1,1,1}, + {1,0,0,0,0,0,0,0,0,1,1,1,1},{1,0,0,0,0,1,0,0,1,1,0,0,0},{1,1,0,0,1,1,0,0,1,0,0,0,0}, + {1,1,1,0,0,1,0,0,1,1,1,0,0},{1,0,1,0,1,1,0,0,1,0,1,0,0},{2,0,1,1,0,2,0,0,2,2,0,1,0}, + {1,1,1,1,1,1,0,0,1,0,0,1,0},{1,1,0,1,0,1,0,0,1,1,1,1,0},{1,0,0,1,1,1,0,0,1,0,1,1,0}, + {1,0,0,1,1,1,0,0,1,1,0,0,1},{1,1,0,1,0,1,0,0,1,0,0,0,1},{2,2,1,1,2,1,0,0,1,2,1,0,1}, + {1,0,1,1,0,1,0,0,1,0,1,0,1},{1,0,1,0,1,1,0,0,1,1,0,1,1},{1,1,1,0,0,1,0,0,1,0,0,1,1}, + {2,1,0,0,1,2,0,0,2,1,2,2,2},{1,0,0,0,0,1,0,0,1,0,1,1,1},{1,0,0,0,0,1,1,0,0,0,1,0,0}, + {1,1,0,0,1,1,1,0,0,1,1,0,0},{1,1,1,0,0,1,1,0,0,0,0,0,0},{1,0,1,0,1,1,1,0,0,1,0,0,0}, + {1,0,1,1,0,1,1,0,0,0,1,1,0},{2,2,2,1,1,1,1,0,0,1,2,1,0},{1,1,0,1,0,1,1,0,0,0,0,1,0}, + {1,0,0,1,1,1,1,0,0,1,0,1,0},{2,0,0,2,2,1,1,0,0,0,1,0,2},{1,1,0,1,0,1,1,0,0,1,1,0,1}, + {1,1,1,1,1,1,1,0,0,0,0,0,1},{1,0,1,1,0,1,1,0,0,1,0,0,1},{1,0,1,0,1,1,1,0,0,0,1,1,1}, + {2,1,1,0,0,2,2,0,0,2,1,2,2},{1,1,0,0,1,1,1,0,0,0,0,1,1},{1,0,0,0,0,1,1,0,0,1,0,1,1}, + {1,0,0,0,0,0,1,0,1,1,1,0,0},{1,1,0,0,1,0,1,0,1,0,1,0,0},{1,1,1,0,0,0,1,0,1,1,0,0,0}, + {1,0,1,0,1,0,1,0,1,0,0,0,0},{1,0,1,1,0,0,1,0,1,1,1,1,0},{2,1,1,2,2,0,2,0,2,0,1,2,0}, + {1,1,0,1,0,0,1,0,1,1,0,1,0},{1,0,0,1,1,0,1,0,1,0,0,1,0},{1,0,0,1,1,0,1,0,1,1,1,0,1}, + {1,1,0,1,0,0,1,0,1,0,1,0,1},{2,1,2,2,1,0,2,0,2,1,0,0,2},{1,0,1,1,0,0,1,0,1,0,0,0,1}, + {2,0,2,0,2,0,1,0,1,2,2,1,1},{2,2,2,0,0,0,1,0,1,0,2,1,1},{2,2,0,0,2,0,1,0,1,2,0,1,1}, + {1,0,0,0,0,0,1,0,1,0,0,1,1},{1,0,0,0,0,0,1,1,0,0,0,1,0},{2,1,0,0,1,0,2,2,0,1,0,2,0}, + {1,1,1,0,0,0,1,1,0,0,1,1,0},{1,0,1,0,1,0,1,1,0,1,1,1,0},{1,0,1,1,0,0,1,1,0,0,0,0,0}, + {1,1,1,1,1,0,1,1,0,1,0,0,0},{1,1,0,1,0,0,1,1,0,0,1,0,0},{1,0,0,1,1,0,1,1,0,1,1,0,0}, + {1,0,0,1,1,0,1,1,0,0,0,1,1},{1,1,0,1,0,0,1,1,0,1,0,1,1},{2,1,2,2,1,0,1,1,0,0,1,2,1}, + {2,0,1,1,0,0,2,2,0,2,2,1,2},{1,0,1,0,1,0,1,1,0,0,0,0,1},{1,1,1,0,0,0,1,1,0,1,0,0,1}, + {1,1,0,0,1,0,1,1,0,0,1,0,1},{1,0,0,0,0,0,1,1,0,1,1,0,1},{1,0,0,0,0,1,1,1,1,1,0,1,0}, + {1,1,0,0,1,1,1,1,1,0,0,1,0},{2,1,1,0,0,2,2,1,1,1,2,1,0},{2,0,2,0,2,1,1,2,2,0,1,2,0}, + {1,0,1,1,0,1,1,1,1,1,0,0,0},{2,2,2,1,1,2,2,1,1,0,0,0,0},{2,2,0,2,0,1,1,2,2,2,1,0,0}, + {2,0,0,1,1,2,2,1,1,0,2,0,0},{2,0,0,1,1,1,1,2,2,1,0,1,2},{2,2,0,2,0,2,2,1,1,0,0,2,1}, + {4,3,2,2,3,4,4,1,1,3,4,2,1},{3,0,2,2,0,1,1,3,3,0,1,2,3},{2,0,2,0,2,2,2,1,1,2,0,0,1}, + {2,1,1,0,0,1,1,2,2,0,0,0,2},{3,1,0,0,1,2,2,3,3,1,2,0,3},{2,0,0,0,0,1,1,2,2,0,1,0,2}, + {1,0,0,0,0,1,0,1,0,0,1,1,0},{1,1,0,0,1,1,0,1,0,1,1,1,0},{1,1,1,0,0,1,0,1,0,0,0,1,0}, + {1,0,1,0,1,1,0,1,0,1,0,1,0},{1,0,1,1,0,1,0,1,0,0,1,0,0},{2,1,1,2,2,2,0,2,0,2,1,0,0}, + {1,1,0,1,0,1,0,1,0,0,0,0,0},{1,0,0,1,1,1,0,1,0,1,0,0,0},{1,0,0,1,1,1,0,1,0,0,1,1,1}, + {2,2,0,2,0,1,0,1,0,1,2,2,1},{2,2,1,1,2,2,0,2,0,0,0,1,2},{2,0,2,2,0,1,0,1,0,1,0,2,1}, + {1,0,1,0,1,1,0,1,0,0,1,0,1},{2,2,2,0,0,1,0,1,0,1,2,0,1},{1,1,0,0,1,1,0,1,0,0,0,0,1}, + {1,0,0,0,0,1,0,1,0,1,0,0,1},{1,0,0,0,0,0,0,1,1,1,1,1,0},{1,1,0,0,1,0,0,1,1,0,1,1,0}, + {1,1,1,0,0,0,0,1,1,1,0,1,0},{1,0,1,0,1,0,0,1,1,0,0,1,0},{1,0,1,1,0,0,0,1,1,1,1,0,0}, + {2,2,2,1,1,0,0,1,1,0,2,0,0},{1,1,0,1,0,0,0,1,1,1,0,0,0},{1,0,0,1,1,0,0,1,1,0,0,0,0}, + {2,0,0,2,2,0,0,1,1,2,2,2,1},{2,1,0,1,0,0,0,2,2,0,1,1,2},{3,2,1,1,2,0,0,3,3,2,0,1,3}, + {2,0,1,1,0,0,0,2,2,0,0,1,2},{2,0,1,0,1,0,0,2,2,1,1,0,2},{2,1,1,0,0,0,0,2,2,0,1,0,2}, + {2,1,0,0,1,0,0,2,2,1,0,0,2},{1,0,0,0,0,0,0,1,1,0,0,0,1},{1,0,0,0,0,0,0,1,1,0,0,0,1}, + {1,1,0,0,1,0,0,1,1,1,0,0,1},{2,1,1,0,0,0,0,2,2,0,1,0,2},{1,0,1,0,1,0,0,1,1,1,1,0,1}, + {1,0,1,1,0,0,0,1,1,0,0,1,1},{2,1,1,2,2,0,0,1,1,1,0,1,2},{1,1,0,1,0,0,0,1,1,0,1,1,1}, + {2,0,0,1,1,0,0,2,2,2,2,2,1},{1,0,0,1,1,0,0,1,1,0,0,0,0},{1,1,0,1,0,0,0,1,1,1,0,0,0}, + {1,1,1,1,1,0,0,1,1,0,1,0,0},{1,0,1,1,0,0,0,1,1,1,1,0,0},{1,0,1,0,1,0,0,1,1,0,0,1,0}, + {1,1,1,0,0,0,0,1,1,1,0,1,0},{1,1,0,0,1,0,0,1,1,0,1,1,0},{1,0,0,0,0,0,0,1,1,1,1,1,0}, + {1,0,0,0,0,1,0,1,0,1,0,0,1},{1,1,0,0,1,1,0,1,0,0,0,0,1},{1,1,1,0,0,1,0,1,0,1,1,0,1}, + {1,0,1,0,1,1,0,1,0,0,1,0,1},{1,0,1,1,0,1,0,1,0,1,0,1,1},{2,2,2,1,1,2,0,2,0,0,0,2,1}, + {2,1,0,1,0,2,0,2,0,1,2,2,1},{2,0,0,2,2,1,0,1,0,0,1,1,2},{1,0,0,1,1,1,0,1,0,1,0,0,0}, + {1,1,0,1,0,1,0,1,0,0,0,0,0},{2,1,2,2,1,2,0,2,0,1,2,0,0},{1,0,1,1,0,1,0,1,0,0,1,0,0}, + {1,0,1,0,1,1,0,1,0,1,0,1,0},{1,1,1,0,0,1,0,1,0,0,0,1,0},{2,2,0,0,2,1,0,1,0,2,1,1,0}, + {1,0,0,0,0,1,0,1,0,0,1,1,0},{1,0,0,0,0,1,1,1,1,0,1,0,1},{2,1,0,0,1,2,1,1,2,2,1,0,1}, + {1,1,1,0,0,1,1,1,1,0,0,0,1},{2,0,2,0,2,1,2,2,1,1,0,0,2},{2,0,1,1,0,1,2,2,1,0,1,2,1}, + {4,1,1,3,3,2,4,4,2,2,1,4,3},{2,2,0,2,0,2,1,1,2,0,0,1,2},{3,0,0,1,1,2,3,3,2,2,0,3,1}, + {1,0,0,1,1,1,1,1,1,0,1,0,0},{2,2,0,2,0,1,2,2,1,1,2,0,0},{2,2,1,1,2,2,1,1,2,0,0,0,0}, + {2,0,1,1,0,2,1,1,2,2,0,0,0},{2,0,2,0,2,2,1,1,2,0,2,1,0},{3,1,1,0,0,3,2,2,3,3,1,2,0}, + {2,1,0,0,1,1,2,2,1,0,0,2,0},{2,0,0,0,0,2,1,1,2,2,0,1,0},{1,0,0,0,0,0,1,1,0,1,1,0,1}, + {1,1,0,0,1,0,1,1,0,0,1,0,1},{1,1,1,0,0,0,1,1,0,1,0,0,1},{1,0,1,0,1,0,1,1,0,0,0,0,1}, + {2,0,2,2,0,0,1,1,0,2,2,1,2},{3,1,1,2,2,0,3,3,0,0,1,3,2},{2,1,0,1,0,0,2,2,0,1,0,2,1}, + {2,0,0,1,1,0,2,2,0,0,0,2,1},{1,0,0,1,1,0,1,1,0,1,1,0,0},{1,1,0,1,0,0,1,1,0,0,1,0,0}, + {2,2,1,1,2,0,1,1,0,2,0,0,0},{1,0,1,1,0,0,1,1,0,0,0,0,0},{2,0,1,0,1,0,2,2,0,1,1,2,0}, + {2,1,1,0,0,0,2,2,0,0,1,2,0},{2,1,0,0,1,0,2,2,0,1,0,2,0},{1,0,0,0,0,0,1,1,0,0,0,1,0}, + {1,0,0,0,0,0,1,0,1,0,0,1,1},{1,1,0,0,1,0,1,0,1,1,0,1,1},{1,1,1,0,0,0,1,0,1,0,1,1,1}, + {2,0,2,0,2,0,1,0,1,1,1,2,2},{1,0,1,1,0,0,1,0,1,0,0,0,1},{2,2,2,1,1,0,2,0,2,2,0,0,1}, + {1,1,0,1,0,0,1,0,1,0,1,0,1},{2,0,0,2,2,0,1,0,1,1,1,0,2},{1,0,0,1,1,0,1,0,1,0,0,1,0}, + {1,1,0,1,0,0,1,0,1,1,0,1,0},{2,2,1,1,2,0,2,0,2,0,2,1,0},{2,0,2,2,0,0,1,0,1,1,1,2,0}, + {1,0,1,0,1,0,1,0,1,0,0,0,0},{1,1,1,0,0,0,1,0,1,1,0,0,0},{1,1,0,0,1,0,1,0,1,0,1,0,0}, + {1,0,0,0,0,0,1,0,1,1,1,0,0},{1,0,0,0,0,1,1,0,0,1,0,1,1},{1,1,0,0,1,1,1,0,0,0,0,1,1}, + {2,2,2,0,0,1,1,0,0,2,1,2,2},{2,0,1,0,1,2,2,0,0,0,2,1,1},{1,0,1,1,0,1,1,0,0,1,0,0,1}, + {2,1,1,2,2,1,1,0,0,0,0,0,2},{2,1,0,1,0,2,2,0,0,1,2,0,1},{2,0,0,2,2,1,1,0,0,0,1,0,2}, + {1,0,0,1,1,1,1,0,0,1,0,1,0},{1,1,0,1,0,1,1,0,0,0,0,1,0},{3,1,2,2,1,3,3,0,0,1,3,2,0}, + {2,0,1,1,0,2,2,0,0,0,2,1,0},{1,0,1,0,1,1,1,0,0,1,0,0,0},{1,1,1,0,0,1,1,0,0,0,0,0,0}, + {2,2,0,0,2,1,1,0,0,2,1,0,0},{1,0,0,0,0,1,1,0,0,0,1,0,0},{1,0,0,0,0,1,0,0,1,0,1,1,1}, + {2,2,0,0,2,1,0,0,1,1,2,2,2},{1,1,1,0,0,1,0,0,1,0,0,1,1},{2,0,1,0,1,2,0,0,2,2,0,1,1}, + {1,0,1,1,0,1,0,0,1,0,1,0,1},{3,1,1,3,3,2,0,0,2,2,1,0,3},{1,1,0,1,0,1,0,0,1,0,0,0,1}, + {2,0,0,2,2,1,0,0,1,1,0,0,2},{1,0,0,1,1,1,0,0,1,0,1,1,0},{2,1,0,1,0,2,0,0,2,2,1,1,0}, + {2,1,2,2,1,1,0,0,1,0,0,2,0},{2,0,1,1,0,2,0,0,2,2,0,1,0},{1,0,1,0,1,1,0,0,1,0,1,0,0}, + {2,1,1,0,0,2,0,0,2,2,1,0,0},{1,1,0,0,1,1,0,0,1,0,0,0,0},{1,0,0,0,0,1,0,0,1,1,0,0,0}, + {1,0,0,0,0,0,0,0,0,1,1,1,1},{1,1,0,0,1,0,0,0,0,0,1,1,1},{1,1,1,0,0,0,0,0,0,1,0,1,1}, + {1,0,1,0,1,0,0,0,0,0,0,1,1},{1,0,1,1,0,0,0,0,0,1,1,0,1},{2,1,1,2,2,0,0,0,0,0,1,0,2}, + {1,1,0,1,0,0,0,0,0,1,0,0,1},{1,0,0,1,1,0,0,0,0,0,0,0,1},{1,0,0,1,1,0,0,0,0,1,1,1,0}, + {1,1,0,1,0,0,0,0,0,0,1,1,0},{2,1,2,2,1,0,0,0,0,1,0,2,0},{1,0,1,1,0,0,0,0,0,0,0,1,0}, + {1,0,1,0,1,0,0,0,0,1,1,0,0},{1,1,1,0,0,0,0,0,0,0,1,0,0},{1,1,0,0,1,0,0,0,0,1,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0}}; + + +//////////////////////////////////////// + +inline bool +isPlanarQuad( + const Vec3d& p0, const Vec3d& p1, + const Vec3d& p2, const Vec3d& p3, + double epsilon = 0.001) +{ + // compute representative plane + Vec3d normal = (p2-p0).cross(p1-p3); + normal.normalize(); + const Vec3d centroid = (p0 + p1 + p2 + p3); + const double d = centroid.dot(normal) * 0.25; + + + // test vertice distance to plane + double absDist = std::abs(p0.dot(normal) - d); + if (absDist > epsilon) return false; + + absDist = std::abs(p1.dot(normal) - d); + if (absDist > epsilon) return false; + + absDist = std::abs(p2.dot(normal) - d); + if (absDist > epsilon) return false; + + absDist = std::abs(p3.dot(normal) - d); + if (absDist > epsilon) return false; + + return true; +} + + +//////////////////////////////////////// + + +/// @{ +/// @brief Utility methods for point quantization. + +enum { + MASK_FIRST_10_BITS = 0x000003FF, + MASK_DIRTY_BIT = 0x80000000, + MASK_INVALID_BIT = 0x40000000 +}; + +inline uint32_t +packPoint(const Vec3d& v) +{ + uint32_t data = 0; + + // values are expected to be in the [0.0 to 1.0] range. + assert(!(v.x() > 1.0) && !(v.y() > 1.0) && !(v.z() > 1.0)); + assert(!(v.x() < 0.0) && !(v.y() < 0.0) && !(v.z() < 0.0)); + + data |= (uint32_t(v.x() * 1023.0) & MASK_FIRST_10_BITS) << 20; + data |= (uint32_t(v.y() * 1023.0) & MASK_FIRST_10_BITS) << 10; + data |= (uint32_t(v.z() * 1023.0) & MASK_FIRST_10_BITS); + + return data; +} + +inline Vec3d +unpackPoint(uint32_t data) +{ + Vec3d v; + v.z() = double(data & MASK_FIRST_10_BITS) * 0.0009775171; + data = data >> 10; + v.y() = double(data & MASK_FIRST_10_BITS) * 0.0009775171; + data = data >> 10; + v.x() = double(data & MASK_FIRST_10_BITS) * 0.0009775171; + + return v; +} + +/// @} + +//////////////////////////////////////// + +template +inline bool isBoolValue() { return false; } + +template<> +inline bool isBoolValue() { return true; } + + + +template +inline bool isInsideValue(T value, T isovalue) { return value < isovalue; } + +template<> +inline bool isInsideValue(bool value, bool /*isovalue*/) { return value; } + + +template +inline void +getCellVertexValues(const AccessorT& accessor, Coord ijk, + math::Tuple<8, typename AccessorT::ValueType>& values) +{ + values[0] = accessor.getValue(ijk); // i, j, k + ++ijk[0]; + values[1] = accessor.getValue(ijk); // i+1, j, k + ++ijk[2]; + values[2] = accessor.getValue(ijk); // i+1, j, k+1 + --ijk[0]; + values[3] = accessor.getValue(ijk); // i, j, k+1 + --ijk[2]; ++ijk[1]; + values[4] = accessor.getValue(ijk); // i, j+1, k + ++ijk[0]; + values[5] = accessor.getValue(ijk); // i+1, j+1, k + ++ijk[2]; + values[6] = accessor.getValue(ijk); // i+1, j+1, k+1 + --ijk[0]; + values[7] = accessor.getValue(ijk); // i, j+1, k+1 +} + + +template +inline void +getCellVertexValues(const LeafT& leaf, const Index offset, + math::Tuple<8, typename LeafT::ValueType>& values) +{ + values[0] = leaf.getValue(offset); // i, j, k + values[3] = leaf.getValue(offset + 1); // i, j, k+1 + values[4] = leaf.getValue(offset + LeafT::DIM); // i, j+1, k + values[7] = leaf.getValue(offset + LeafT::DIM + 1); // i, j+1, k+1 + values[1] = leaf.getValue(offset + (LeafT::DIM * LeafT::DIM)); // i+1, j, k + values[2] = leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + 1); // i+1, j, k+1 + values[5] = leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM); // i+1, j+1, k + values[6] = leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM + 1); // i+1, j+1, k+1 +} + + +template +inline uint8_t +computeSignFlags(const math::Tuple<8, ValueType>& values, const ValueType iso) +{ + unsigned signs = 0; + signs |= isInsideValue(values[0], iso) ? 1u : 0u; + signs |= isInsideValue(values[1], iso) ? 2u : 0u; + signs |= isInsideValue(values[2], iso) ? 4u : 0u; + signs |= isInsideValue(values[3], iso) ? 8u : 0u; + signs |= isInsideValue(values[4], iso) ? 16u : 0u; + signs |= isInsideValue(values[5], iso) ? 32u : 0u; + signs |= isInsideValue(values[6], iso) ? 64u : 0u; + signs |= isInsideValue(values[7], iso) ? 128u : 0u; + return uint8_t(signs); +} + + +/// @brief General method that computes the cell-sign configuration at the given +/// @c ijk coordinate. +template +inline uint8_t +evalCellSigns(const AccessorT& accessor, const Coord& ijk, typename AccessorT::ValueType iso) +{ + unsigned signs = 0; + Coord coord = ijk; // i, j, k + if (isInsideValue(accessor.getValue(coord), iso)) signs |= 1u; + coord[0] += 1; // i+1, j, k + if (isInsideValue(accessor.getValue(coord), iso)) signs |= 2u; + coord[2] += 1; // i+1, j, k+1 + if (isInsideValue(accessor.getValue(coord), iso)) signs |= 4u; + coord[0] = ijk[0]; // i, j, k+1 + if (isInsideValue(accessor.getValue(coord), iso)) signs |= 8u; + coord[1] += 1; coord[2] = ijk[2]; // i, j+1, k + if (isInsideValue(accessor.getValue(coord), iso)) signs |= 16u; + coord[0] += 1; // i+1, j+1, k + if (isInsideValue(accessor.getValue(coord), iso)) signs |= 32u; + coord[2] += 1; // i+1, j+1, k+1 + if (isInsideValue(accessor.getValue(coord), iso)) signs |= 64u; + coord[0] = ijk[0]; // i, j+1, k+1 + if (isInsideValue(accessor.getValue(coord), iso)) signs |= 128u; + return uint8_t(signs); +} + + +/// @brief Leaf node optimized method that computes the cell-sign configuration +/// at the given local @c offset +template +inline uint8_t +evalCellSigns(const LeafT& leaf, const Index offset, typename LeafT::ValueType iso) +{ + unsigned signs = 0; + + // i, j, k + if (isInsideValue(leaf.getValue(offset), iso)) signs |= 1u; + + // i, j, k+1 + if (isInsideValue(leaf.getValue(offset + 1), iso)) signs |= 8u; + + // i, j+1, k + if (isInsideValue(leaf.getValue(offset + LeafT::DIM), iso)) signs |= 16u; + + // i, j+1, k+1 + if (isInsideValue(leaf.getValue(offset + LeafT::DIM + 1), iso)) signs |= 128u; + + // i+1, j, k + if (isInsideValue(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) ), iso)) signs |= 2u; + + // i+1, j, k+1 + if (isInsideValue(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + 1), iso)) signs |= 4u; + + // i+1, j+1, k + if (isInsideValue(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM), iso)) signs |= 32u; + + // i+1, j+1, k+1 + if (isInsideValue(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM + 1), iso)) signs |= 64u; + + return uint8_t(signs); +} + + +/// @brief Used to correct topological ambiguities related to two adjacent cells +/// that share an ambiguous face. +template +inline void +correctCellSigns(uint8_t& signs, uint8_t face, + const AccessorT& acc, Coord ijk, typename AccessorT::ValueType iso) +{ + switch (int(face)) { + case 1: + ijk[2] -= 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 3) signs = uint8_t(~signs); + break; + case 2: + ijk[0] += 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 4) signs = uint8_t(~signs); + break; + case 3: + ijk[2] += 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 1) signs = uint8_t(~signs); + break; + case 4: + ijk[0] -= 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 2) signs = uint8_t(~signs); + break; + case 5: + ijk[1] -= 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 6) signs = uint8_t(~signs); + break; + case 6: + ijk[1] += 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 5) signs = uint8_t(~signs); + break; + default: + break; + } +} + + +template +inline bool +isNonManifold(const AccessorT& accessor, const Coord& ijk, + typename AccessorT::ValueType isovalue, const int dim) +{ + int hDim = dim >> 1; + bool m, p[8]; // Corner signs + + Coord coord = ijk; // i, j, k + p[0] = isInsideValue(accessor.getValue(coord), isovalue); + coord[0] += dim; // i+dim, j, k + p[1] = isInsideValue(accessor.getValue(coord), isovalue); + coord[2] += dim; // i+dim, j, k+dim + p[2] = isInsideValue(accessor.getValue(coord), isovalue); + coord[0] = ijk[0]; // i, j, k+dim + p[3] = isInsideValue(accessor.getValue(coord), isovalue); + coord[1] += dim; coord[2] = ijk[2]; // i, j+dim, k + p[4] = isInsideValue(accessor.getValue(coord), isovalue); + coord[0] += dim; // i+dim, j+dim, k + p[5] = isInsideValue(accessor.getValue(coord), isovalue); + coord[2] += dim; // i+dim, j+dim, k+dim + p[6] = isInsideValue(accessor.getValue(coord), isovalue); + coord[0] = ijk[0]; // i, j+dim, k+dim + p[7] = isInsideValue(accessor.getValue(coord), isovalue); + + // Check if the corner sign configuration is ambiguous + unsigned signs = 0; + if (p[0]) signs |= 1u; + if (p[1]) signs |= 2u; + if (p[2]) signs |= 4u; + if (p[3]) signs |= 8u; + if (p[4]) signs |= 16u; + if (p[5]) signs |= 32u; + if (p[6]) signs |= 64u; + if (p[7]) signs |= 128u; + if (!sAdaptable[signs]) return true; + + // Manifold check + + // Evaluate edges + int i = ijk[0], ip = ijk[0] + hDim, ipp = ijk[0] + dim; + int j = ijk[1], jp = ijk[1] + hDim, jpp = ijk[1] + dim; + int k = ijk[2], kp = ijk[2] + hDim, kpp = ijk[2] + dim; + + // edge 1 + coord.reset(ip, j, k); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[0] != m && p[1] != m) return true; + + // edge 2 + coord.reset(ipp, j, kp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[1] != m && p[2] != m) return true; + + // edge 3 + coord.reset(ip, j, kpp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[2] != m && p[3] != m) return true; + + // edge 4 + coord.reset(i, j, kp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[0] != m && p[3] != m) return true; + + // edge 5 + coord.reset(ip, jpp, k); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[4] != m && p[5] != m) return true; + + // edge 6 + coord.reset(ipp, jpp, kp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[5] != m && p[6] != m) return true; + + // edge 7 + coord.reset(ip, jpp, kpp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[6] != m && p[7] != m) return true; + + // edge 8 + coord.reset(i, jpp, kp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[7] != m && p[4] != m) return true; + + // edge 9 + coord.reset(i, jp, k); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[0] != m && p[4] != m) return true; + + // edge 10 + coord.reset(ipp, jp, k); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[1] != m && p[5] != m) return true; + + // edge 11 + coord.reset(ipp, jp, kpp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[2] != m && p[6] != m) return true; + + + // edge 12 + coord.reset(i, jp, kpp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[3] != m && p[7] != m) return true; + + + // Evaluate faces + + // face 1 + coord.reset(ip, jp, k); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[0] != m && p[1] != m && p[4] != m && p[5] != m) return true; + + // face 2 + coord.reset(ipp, jp, kp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[1] != m && p[2] != m && p[5] != m && p[6] != m) return true; + + // face 3 + coord.reset(ip, jp, kpp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[2] != m && p[3] != m && p[6] != m && p[7] != m) return true; + + // face 4 + coord.reset(i, jp, kp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[0] != m && p[3] != m && p[4] != m && p[7] != m) return true; + + // face 5 + coord.reset(ip, j, kp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[0] != m && p[1] != m && p[2] != m && p[3] != m) return true; + + // face 6 + coord.reset(ip, jpp, kp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[4] != m && p[5] != m && p[6] != m && p[7] != m) return true; + + // test cube center + coord.reset(ip, jp, kp); + m = isInsideValue(accessor.getValue(coord), isovalue); + if (p[0] != m && p[1] != m && p[2] != m && p[3] != m && + p[4] != m && p[5] != m && p[6] != m && p[7] != m) return true; + + return false; +} + + +//////////////////////////////////////// + + +template +inline void +mergeVoxels(LeafType& leaf, const Coord& start, int dim, int regionId) +{ + Coord ijk, end = start; + end[0] += dim; + end[1] += dim; + end[2] += dim; + + for (ijk[0] = start[0]; ijk[0] < end[0]; ++ijk[0]) { + for (ijk[1] = start[1]; ijk[1] < end[1]; ++ijk[1]) { + for (ijk[2] = start[2]; ijk[2] < end[2]; ++ijk[2]) { + leaf.setValueOnly(ijk, regionId); + } + } + } +} + + +// Note that we must use ValueType::value_type or else Visual C++ gets confused +// thinking that it is a constructor. +template +inline bool +isMergable(LeafType& leaf, const Coord& start, int dim, + typename LeafType::ValueType::value_type adaptivity) +{ + if (adaptivity < 1e-6) return false; + + using VecT = typename LeafType::ValueType; + Coord ijk, end = start; + end[0] += dim; + end[1] += dim; + end[2] += dim; + + std::vector norms; + for (ijk[0] = start[0]; ijk[0] < end[0]; ++ijk[0]) { + for (ijk[1] = start[1]; ijk[1] < end[1]; ++ijk[1]) { + for (ijk[2] = start[2]; ijk[2] < end[2]; ++ijk[2]) { + + if(!leaf.isValueOn(ijk)) continue; + norms.push_back(leaf.getValue(ijk)); + } + } + } + + size_t N = norms.size(); + for (size_t ni = 0; ni < N; ++ni) { + VecT n_i = norms[ni]; + for (size_t nj = 0; nj < N; ++nj) { + VecT n_j = norms[nj]; + if ((1.0 - n_i.dot(n_j)) > adaptivity) return false; + } + } + return true; +} + + +//////////////////////////////////////// + + +/// linear interpolation. +inline double evalZeroCrossing(double v0, double v1, double iso) { return (iso - v0) / (v1 - v0); } + + +/// @brief Extracts the eight corner values for leaf inclusive cells. +template +inline void +collectCornerValues(const LeafT& leaf, const Index offset, std::vector& values) +{ + values[0] = double(leaf.getValue(offset)); // i, j, k + values[3] = double(leaf.getValue(offset + 1)); // i, j, k+1 + values[4] = double(leaf.getValue(offset + LeafT::DIM)); // i, j+1, k + values[7] = double(leaf.getValue(offset + LeafT::DIM + 1)); // i, j+1, k+1 + values[1] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM))); // i+1, j, k + values[2] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + 1)); // i+1, j, k+1 + values[5] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM)); // i+1, j+1, k + values[6] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM + 1)); // i+1, j+1, k+1 +} + + +/// @brief Extracts the eight corner values for a cell starting at the given @ijk coordinate. +template +inline void +collectCornerValues(const AccessorT& acc, const Coord& ijk, std::vector& values) +{ + Coord coord = ijk; + values[0] = double(acc.getValue(coord)); // i, j, k + + coord[0] += 1; + values[1] = double(acc.getValue(coord)); // i+1, j, k + + coord[2] += 1; + values[2] = double(acc.getValue(coord)); // i+i, j, k+1 + + coord[0] = ijk[0]; + values[3] = double(acc.getValue(coord)); // i, j, k+1 + + coord[1] += 1; coord[2] = ijk[2]; + values[4] = double(acc.getValue(coord)); // i, j+1, k + + coord[0] += 1; + values[5] = double(acc.getValue(coord)); // i+1, j+1, k + + coord[2] += 1; + values[6] = double(acc.getValue(coord)); // i+1, j+1, k+1 + + coord[0] = ijk[0]; + values[7] = double(acc.getValue(coord)); // i, j+1, k+1 +} + + +/// @brief Computes the average cell point for a given edge group. +inline Vec3d +computePoint(const std::vector& values, unsigned char signs, + unsigned char edgeGroup, double iso) +{ + Vec3d avg(0.0, 0.0, 0.0); + int samples = 0; + + if (sEdgeGroupTable[signs][1] == edgeGroup) { // Edged: 0 - 1 + avg[0] += evalZeroCrossing(values[0], values[1], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][2] == edgeGroup) { // Edged: 1 - 2 + avg[0] += 1.0; + avg[2] += evalZeroCrossing(values[1], values[2], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][3] == edgeGroup) { // Edged: 3 - 2 + avg[0] += evalZeroCrossing(values[3], values[2], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][4] == edgeGroup) { // Edged: 0 - 3 + avg[2] += evalZeroCrossing(values[0], values[3], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][5] == edgeGroup) { // Edged: 4 - 5 + avg[0] += evalZeroCrossing(values[4], values[5], iso); + avg[1] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][6] == edgeGroup) { // Edged: 5 - 6 + avg[0] += 1.0; + avg[1] += 1.0; + avg[2] += evalZeroCrossing(values[5], values[6], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][7] == edgeGroup) { // Edged: 7 - 6 + avg[0] += evalZeroCrossing(values[7], values[6], iso); + avg[1] += 1.0; + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][8] == edgeGroup) { // Edged: 4 - 7 + avg[1] += 1.0; + avg[2] += evalZeroCrossing(values[4], values[7], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][9] == edgeGroup) { // Edged: 0 - 4 + avg[1] += evalZeroCrossing(values[0], values[4], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][10] == edgeGroup) { // Edged: 1 - 5 + avg[0] += 1.0; + avg[1] += evalZeroCrossing(values[1], values[5], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][11] == edgeGroup) { // Edged: 2 - 6 + avg[0] += 1.0; + avg[1] += evalZeroCrossing(values[2], values[6], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][12] == edgeGroup) { // Edged: 3 - 7 + avg[1] += evalZeroCrossing(values[3], values[7], iso); + avg[2] += 1.0; + ++samples; + } + + if (samples > 1) { + double w = 1.0 / double(samples); + avg[0] *= w; + avg[1] *= w; + avg[2] *= w; + } + + return avg; +} + + +/// @brief Computes the average cell point for a given edge group, ignoring edge +/// samples present in the @c signsMask configuration. +inline int +computeMaskedPoint(Vec3d& avg, const std::vector& values, unsigned char signs, + unsigned char signsMask, unsigned char edgeGroup, double iso) +{ + avg = Vec3d(0.0, 0.0, 0.0); + int samples = 0; + + if (sEdgeGroupTable[signs][1] == edgeGroup + && sEdgeGroupTable[signsMask][1] == 0) { // Edged: 0 - 1 + avg[0] += evalZeroCrossing(values[0], values[1], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][2] == edgeGroup + && sEdgeGroupTable[signsMask][2] == 0) { // Edged: 1 - 2 + avg[0] += 1.0; + avg[2] += evalZeroCrossing(values[1], values[2], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][3] == edgeGroup + && sEdgeGroupTable[signsMask][3] == 0) { // Edged: 3 - 2 + avg[0] += evalZeroCrossing(values[3], values[2], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][4] == edgeGroup + && sEdgeGroupTable[signsMask][4] == 0) { // Edged: 0 - 3 + avg[2] += evalZeroCrossing(values[0], values[3], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][5] == edgeGroup + && sEdgeGroupTable[signsMask][5] == 0) { // Edged: 4 - 5 + avg[0] += evalZeroCrossing(values[4], values[5], iso); + avg[1] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][6] == edgeGroup + && sEdgeGroupTable[signsMask][6] == 0) { // Edged: 5 - 6 + avg[0] += 1.0; + avg[1] += 1.0; + avg[2] += evalZeroCrossing(values[5], values[6], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][7] == edgeGroup + && sEdgeGroupTable[signsMask][7] == 0) { // Edged: 7 - 6 + avg[0] += evalZeroCrossing(values[7], values[6], iso); + avg[1] += 1.0; + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][8] == edgeGroup + && sEdgeGroupTable[signsMask][8] == 0) { // Edged: 4 - 7 + avg[1] += 1.0; + avg[2] += evalZeroCrossing(values[4], values[7], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][9] == edgeGroup + && sEdgeGroupTable[signsMask][9] == 0) { // Edged: 0 - 4 + avg[1] += evalZeroCrossing(values[0], values[4], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][10] == edgeGroup + && sEdgeGroupTable[signsMask][10] == 0) { // Edged: 1 - 5 + avg[0] += 1.0; + avg[1] += evalZeroCrossing(values[1], values[5], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][11] == edgeGroup + && sEdgeGroupTable[signsMask][11] == 0) { // Edged: 2 - 6 + avg[0] += 1.0; + avg[1] += evalZeroCrossing(values[2], values[6], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][12] == edgeGroup + && sEdgeGroupTable[signsMask][12] == 0) { // Edged: 3 - 7 + avg[1] += evalZeroCrossing(values[3], values[7], iso); + avg[2] += 1.0; + ++samples; + } + + if (samples > 1) { + double w = 1.0 / double(samples); + avg[0] *= w; + avg[1] *= w; + avg[2] *= w; + } + + return samples; +} + + +/// @brief Computes the average cell point for a given edge group, by computing +/// convex weights based on the distance from the sample point @c p. +inline Vec3d +computeWeightedPoint(const Vec3d& p, const std::vector& values, + unsigned char signs, unsigned char edgeGroup, double iso) +{ + std::vector samples; + samples.reserve(8); + + std::vector weights; + weights.reserve(8); + + Vec3d avg(0.0, 0.0, 0.0); + + if (sEdgeGroupTable[signs][1] == edgeGroup) { // Edged: 0 - 1 + avg[0] = evalZeroCrossing(values[0], values[1], iso); + avg[1] = 0.0; + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][2] == edgeGroup) { // Edged: 1 - 2 + avg[0] = 1.0; + avg[1] = 0.0; + avg[2] = evalZeroCrossing(values[1], values[2], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][3] == edgeGroup) { // Edged: 3 - 2 + avg[0] = evalZeroCrossing(values[3], values[2], iso); + avg[1] = 0.0; + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][4] == edgeGroup) { // Edged: 0 - 3 + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = evalZeroCrossing(values[0], values[3], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][5] == edgeGroup) { // Edged: 4 - 5 + avg[0] = evalZeroCrossing(values[4], values[5], iso); + avg[1] = 1.0; + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][6] == edgeGroup) { // Edged: 5 - 6 + avg[0] = 1.0; + avg[1] = 1.0; + avg[2] = evalZeroCrossing(values[5], values[6], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][7] == edgeGroup) { // Edged: 7 - 6 + avg[0] = evalZeroCrossing(values[7], values[6], iso); + avg[1] = 1.0; + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][8] == edgeGroup) { // Edged: 4 - 7 + avg[0] = 0.0; + avg[1] = 1.0; + avg[2] = evalZeroCrossing(values[4], values[7], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][9] == edgeGroup) { // Edged: 0 - 4 + avg[0] = 0.0; + avg[1] = evalZeroCrossing(values[0], values[4], iso); + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][10] == edgeGroup) { // Edged: 1 - 5 + avg[0] = 1.0; + avg[1] = evalZeroCrossing(values[1], values[5], iso); + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][11] == edgeGroup) { // Edged: 2 - 6 + avg[0] = 1.0; + avg[1] = evalZeroCrossing(values[2], values[6], iso); + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][12] == edgeGroup) { // Edged: 3 - 7 + avg[0] = 0.0; + avg[1] = evalZeroCrossing(values[3], values[7], iso); + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + + double minWeight = std::numeric_limits::max(); + double maxWeight = -std::numeric_limits::max(); + + for (size_t i = 0, I = weights.size(); i < I; ++i) { + minWeight = std::min(minWeight, weights[i]); + maxWeight = std::max(maxWeight, weights[i]); + } + + const double offset = maxWeight + minWeight * 0.1; + for (size_t i = 0, I = weights.size(); i < I; ++i) { + weights[i] = offset - weights[i]; + } + + + double weightSum = 0.0; + for (size_t i = 0, I = weights.size(); i < I; ++i) { + weightSum += weights[i]; + } + + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + + if (samples.size() > 1) { + for (size_t i = 0, I = samples.size(); i < I; ++i) { + avg += samples[i] * (weights[i] / weightSum); + } + } else { + avg = samples.front(); + } + + return avg; +} + + +/// @brief Computes the average cell points defined by the sign configuration +/// @c signs and the given corner values @c values. +inline void +computeCellPoints(std::vector& points, + const std::vector& values, unsigned char signs, double iso) +{ + for (size_t n = 1, N = sEdgeGroupTable[signs][0] + 1; n < N; ++n) { + points.push_back(computePoint(values, signs, uint8_t(n), iso)); + } +} + + +/// @brief Given a sign configuration @c lhsSigns and an edge group @c groupId, +/// finds the corresponding edge group in a different sign configuration +/// @c rhsSigns. Returns -1 if no match is found. +inline int +matchEdgeGroup(unsigned char groupId, unsigned char lhsSigns, unsigned char rhsSigns) +{ + int id = -1; + for (size_t i = 1; i <= 12; ++i) { + if (sEdgeGroupTable[lhsSigns][i] == groupId && sEdgeGroupTable[rhsSigns][i] != 0) { + id = sEdgeGroupTable[rhsSigns][i]; + break; + } + } + return id; +} + + +/// @brief Computes the average cell points defined by the sign configuration +/// @c signs and the given corner values @c values. Combines data from +/// two different level sets to eliminate seam lines when meshing +/// fractured segments. +inline void +computeCellPoints(std::vector& points, std::vector& weightedPointMask, + const std::vector& lhsValues, const std::vector& rhsValues, + unsigned char lhsSigns, unsigned char rhsSigns, + double iso, size_t pointIdx, const uint32_t * seamPointArray) +{ + for (size_t n = 1, N = sEdgeGroupTable[lhsSigns][0] + 1; n < N; ++n) { + + int id = matchEdgeGroup(uint8_t(n), lhsSigns, rhsSigns); + + if (id != -1) { + + const unsigned char e = uint8_t(id); + const uint32_t& quantizedPoint = seamPointArray[pointIdx + (id - 1)]; + + if ((quantizedPoint & MASK_DIRTY_BIT) && !(quantizedPoint & MASK_INVALID_BIT)) { + Vec3d p = unpackPoint(quantizedPoint); + points.push_back(computeWeightedPoint(p, rhsValues, rhsSigns, e, iso)); + weightedPointMask.push_back(true); + } else { + points.push_back(computePoint(rhsValues, rhsSigns, e, iso)); + weightedPointMask.push_back(false); + } + + } else { + points.push_back(computePoint(lhsValues, lhsSigns, uint8_t(n), iso)); + weightedPointMask.push_back(false); + } + } +} + + +template +struct ComputePoints +{ + using InputLeafNodeType = typename InputTreeType::LeafNodeType; + using InputValueType = typename InputLeafNodeType::ValueType; + + using Int16TreeType = typename InputTreeType::template ValueConverter::Type; + using Int16LeafNodeType = typename Int16TreeType::LeafNodeType; + + using Index32TreeType = typename InputTreeType::template ValueConverter::Type; + using Index32LeafNodeType = typename Index32TreeType::LeafNodeType; + + ComputePoints(Vec3s * pointArray, + const InputTreeType& inputTree, + const std::vector& pointIndexLeafNodes, + const std::vector& signFlagsLeafNodes, + const std::unique_ptr& leafNodeOffsets, + const math::Transform& xform, + double iso); + + void setRefData(const InputTreeType& refInputTree, + const Index32TreeType& refPointIndexTree, + const Int16TreeType& refSignFlagsTree, + const uint32_t * quantizedSeamLinePoints, + uint8_t * seamLinePointsFlags); + + void operator()(const tbb::blocked_range&) const; + +private: + Vec3s * const mPoints; + InputTreeType const * const mInputTree; + Index32LeafNodeType * const * const mPointIndexNodes; + Int16LeafNodeType const * const * const mSignFlagsNodes; + Index32 const * const mNodeOffsets; + math::Transform const mTransform; + double const mIsovalue; + // reference meshing data + InputTreeType const * mRefInputTree; + Index32TreeType const * mRefPointIndexTree; + Int16TreeType const * mRefSignFlagsTree; + uint32_t const * mQuantizedSeamLinePoints; + uint8_t * mSeamLinePointsFlags; +}; // struct ComputePoints + + +template +ComputePoints::ComputePoints( + Vec3s * pointArray, + const InputTreeType& inputTree, + const std::vector& pointIndexLeafNodes, + const std::vector& signFlagsLeafNodes, + const std::unique_ptr& leafNodeOffsets, + const math::Transform& xform, + double iso) + : mPoints(pointArray) + , mInputTree(&inputTree) + , mPointIndexNodes(pointIndexLeafNodes.empty() ? nullptr : &pointIndexLeafNodes.front()) + , mSignFlagsNodes(signFlagsLeafNodes.empty() ? nullptr : &signFlagsLeafNodes.front()) + , mNodeOffsets(leafNodeOffsets.get()) + , mTransform(xform) + , mIsovalue(iso) + , mRefInputTree(nullptr) + , mRefPointIndexTree(nullptr) + , mRefSignFlagsTree(nullptr) + , mQuantizedSeamLinePoints(nullptr) + , mSeamLinePointsFlags(nullptr) +{ +} + +template +void +ComputePoints::setRefData( + const InputTreeType& refInputTree, + const Index32TreeType& refPointIndexTree, + const Int16TreeType& refSignFlagsTree, + const uint32_t * quantizedSeamLinePoints, + uint8_t * seamLinePointsFlags) +{ + mRefInputTree = &refInputTree; + mRefPointIndexTree = &refPointIndexTree; + mRefSignFlagsTree = &refSignFlagsTree; + mQuantizedSeamLinePoints = quantizedSeamLinePoints; + mSeamLinePointsFlags = seamLinePointsFlags; +} + +template +void +ComputePoints::operator()(const tbb::blocked_range& range) const +{ + using InputTreeAccessor = tree::ValueAccessor; + using Index32TreeAccessor = tree::ValueAccessor; + using Int16TreeAccessor = tree::ValueAccessor; + + using IndexType = typename Index32TreeType::ValueType; + + using IndexArray = std::vector; + using IndexArrayMap = std::map; + + InputTreeAccessor inputAcc(*mInputTree); + + Vec3d xyz; + Coord ijk; + std::vector points(4); + std::vector weightedPointMask(4); + std::vector values(8), refValues(8); + const double iso = mIsovalue; + + // reference data accessors + + std::unique_ptr refInputAcc; + std::unique_ptr refPointIndexAcc; + std::unique_ptr refSignFlagsAcc; + + const bool hasReferenceData = mRefInputTree && mRefPointIndexTree && mRefSignFlagsTree; + + if (hasReferenceData) { + refInputAcc.reset(new InputTreeAccessor(*mRefInputTree)); + refPointIndexAcc.reset(new Index32TreeAccessor(*mRefPointIndexTree)); + refSignFlagsAcc.reset(new Int16TreeAccessor(*mRefSignFlagsTree)); + } + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + Index32LeafNodeType& pointIndexNode = *mPointIndexNodes[n]; + const Coord& origin = pointIndexNode.origin(); + + const Int16LeafNodeType& signFlagsNode = *mSignFlagsNodes[n]; + const InputLeafNodeType * inputNode = inputAcc.probeConstLeaf(origin); + + // get reference data + const InputLeafNodeType * refInputNode = nullptr; + const Index32LeafNodeType * refPointIndexNode = nullptr; + const Int16LeafNodeType * refSignFlagsNode = nullptr; + + if (hasReferenceData) { + refInputNode = refInputAcc->probeConstLeaf(origin); + refPointIndexNode = refPointIndexAcc->probeConstLeaf(origin); + refSignFlagsNode = refSignFlagsAcc->probeConstLeaf(origin); + } + + IndexType pointOffset = IndexType(mNodeOffsets[n]); + IndexArrayMap regions; + + for (auto it = pointIndexNode.beginValueOn(); it; ++it) { + const Index offset = it.pos(); + + const IndexType id = it.getValue(); + if (id != 0) { + if (id != IndexType(util::INVALID_IDX)) { + regions[id].push_back(offset); + } + continue; + } + + pointIndexNode.setValueOnly(offset, pointOffset); + + const Int16 flags = signFlagsNode.getValue(offset); + uint8_t signs = uint8_t(SIGNS & flags); + uint8_t refSigns = 0; + + if ((flags & SEAM) && refPointIndexNode && refSignFlagsNode) { + if (refSignFlagsNode->isValueOn(offset)) { + refSigns = uint8_t(SIGNS & refSignFlagsNode->getValue(offset)); + } + } + + ijk = Index32LeafNodeType::offsetToLocalCoord(offset); + + const bool inclusiveCell = inputNode && + ijk[0] < int(Index32LeafNodeType::DIM - 1) && + ijk[1] < int(Index32LeafNodeType::DIM - 1) && + ijk[2] < int(Index32LeafNodeType::DIM - 1); + + ijk += origin; + + if (inclusiveCell) collectCornerValues(*inputNode, offset, values); + else collectCornerValues(inputAcc, ijk, values); + + points.clear(); + weightedPointMask.clear(); + + if (refSigns == 0) { + + computeCellPoints(points, values, signs, iso); + + } else { + if (inclusiveCell && refInputNode) { + collectCornerValues(*refInputNode, offset, refValues); + } else { + collectCornerValues(*refInputAcc, ijk, refValues); + } + computeCellPoints(points, weightedPointMask, values, refValues, signs, refSigns, + iso, refPointIndexNode->getValue(offset), mQuantizedSeamLinePoints); + } + + xyz[0] = double(ijk[0]); + xyz[1] = double(ijk[1]); + xyz[2] = double(ijk[2]); + + for (size_t i = 0, I = points.size(); i < I; ++i) { + + Vec3d& point = points[i]; + + // Checks for both NaN and inf vertex positions, i.e. any value that is not finite. + if (!std::isfinite(point[0]) || + !std::isfinite(point[1]) || + !std::isfinite(point[2])) + { + OPENVDB_THROW(ValueError, + "VolumeToMesh encountered NaNs or infs in the input VDB!" + " Hint: Check the input and consider using the \"Diagnostics\" tool " + "to detect and resolve the NaNs."); + } + + point += xyz; + point = mTransform.indexToWorld(point); + + Vec3s& pos = mPoints[pointOffset]; + pos[0] = float(point[0]); + pos[1] = float(point[1]); + pos[2] = float(point[2]); + + if (mSeamLinePointsFlags && !weightedPointMask.empty() && weightedPointMask[i]) { + mSeamLinePointsFlags[pointOffset] = uint8_t(1); + } + + ++pointOffset; + } + } + + // generate collapsed region points + for (typename IndexArrayMap::iterator it = regions.begin(); it != regions.end(); ++it) { + + Vec3d avg(0.0), point(0.0); + int count = 0; + + const IndexArray& voxels = it->second; + for (size_t i = 0, I = voxels.size(); i < I; ++i) { + + const Index offset = voxels[i]; + ijk = Index32LeafNodeType::offsetToLocalCoord(offset); + + const bool inclusiveCell = inputNode && + ijk[0] < int(Index32LeafNodeType::DIM - 1) && + ijk[1] < int(Index32LeafNodeType::DIM - 1) && + ijk[2] < int(Index32LeafNodeType::DIM - 1); + + ijk += origin; + + pointIndexNode.setValueOnly(offset, pointOffset); + + uint8_t signs = uint8_t(SIGNS & signFlagsNode.getValue(offset)); + + if (inclusiveCell) collectCornerValues(*inputNode, offset, values); + else collectCornerValues(inputAcc, ijk, values); + + points.clear(); + computeCellPoints(points, values, signs, iso); + + avg[0] += double(ijk[0]) + points[0][0]; + avg[1] += double(ijk[1]) + points[0][1]; + avg[2] += double(ijk[2]) + points[0][2]; + + ++count; + } + + if (count > 1) { + double w = 1.0 / double(count); + avg[0] *= w; + avg[1] *= w; + avg[2] *= w; + } + + avg = mTransform.indexToWorld(avg); + + Vec3s& pos = mPoints[pointOffset]; + pos[0] = float(avg[0]); + pos[1] = float(avg[1]); + pos[2] = float(avg[2]); + + ++pointOffset; + } + } +} // ComputePoints::operator() + + +//////////////////////////////////////// + + +template +struct SeamLineWeights +{ + using InputLeafNodeType = typename InputTreeType::LeafNodeType; + using InputValueType = typename InputLeafNodeType::ValueType; + + using Int16TreeType = typename InputTreeType::template ValueConverter::Type; + using Int16LeafNodeType = typename Int16TreeType::LeafNodeType; + + using Index32TreeType = typename InputTreeType::template ValueConverter::Type; + using Index32LeafNodeType = typename Index32TreeType::LeafNodeType; + + SeamLineWeights(const std::vector& signFlagsLeafNodes, + const InputTreeType& inputTree, + const Index32TreeType& refPointIndexTree, + const Int16TreeType& refSignFlagsTree, + uint32_t * quantizedPoints, + InputValueType iso) + : mSignFlagsNodes(signFlagsLeafNodes.empty() ? nullptr : &signFlagsLeafNodes.front()) + , mInputTree(&inputTree) + , mRefPointIndexTree(&refPointIndexTree) + , mRefSignFlagsTree(&refSignFlagsTree) + , mQuantizedPoints(quantizedPoints) + , mIsovalue(iso) + { + } + + void operator()(const tbb::blocked_range& range) const + { + tree::ValueAccessor inputTreeAcc(*mInputTree); + tree::ValueAccessor pointIndexTreeAcc(*mRefPointIndexTree); + tree::ValueAccessor signFlagsTreeAcc(*mRefSignFlagsTree); + + std::vector values(8); + const double iso = double(mIsovalue); + Coord ijk; + Vec3d pos; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + const Int16LeafNodeType& signFlagsNode = *mSignFlagsNodes[n]; + const Coord& origin = signFlagsNode.origin(); + + const Int16LeafNodeType * refSignNode = signFlagsTreeAcc.probeConstLeaf(origin); + if (!refSignNode) continue; + + const Index32LeafNodeType* refPointIndexNode = + pointIndexTreeAcc.probeConstLeaf(origin); + if (!refPointIndexNode) continue; + + const InputLeafNodeType * inputNode = inputTreeAcc.probeConstLeaf(origin); + + for (typename Int16LeafNodeType::ValueOnCIter it = signFlagsNode.cbeginValueOn(); + it; ++it) + { + const Index offset = it.pos(); + + ijk = Index32LeafNodeType::offsetToLocalCoord(offset); + + const bool inclusiveCell = inputNode && + ijk[0] < int(Index32LeafNodeType::DIM - 1) && + ijk[1] < int(Index32LeafNodeType::DIM - 1) && + ijk[2] < int(Index32LeafNodeType::DIM - 1); + + ijk += origin; + + if ((it.getValue() & SEAM) && refSignNode->isValueOn(offset)) { + + uint8_t lhsSigns = uint8_t(SIGNS & it.getValue()); + uint8_t rhsSigns = uint8_t(SIGNS & refSignNode->getValue(offset)); + + + if (inclusiveCell) { + collectCornerValues(*inputNode, offset, values); + } else { + collectCornerValues(inputTreeAcc, ijk, values); + } + + + for (unsigned i = 1, I = sEdgeGroupTable[lhsSigns][0] + 1; i < I; ++i) { + + int id = matchEdgeGroup(uint8_t(i), lhsSigns, rhsSigns); + + if (id != -1) { + + uint32_t& data = mQuantizedPoints[ + refPointIndexNode->getValue(offset) + (id - 1)]; + + if (!(data & MASK_DIRTY_BIT)) { + + int smaples = computeMaskedPoint( + pos, values, lhsSigns, rhsSigns, uint8_t(i), iso); + + if (smaples > 0) data = packPoint(pos); + else data = MASK_INVALID_BIT; + + data |= MASK_DIRTY_BIT; + } + } + } // end point group loop + } + + } // end value on loop + + } // end leaf node loop + } + +private: + Int16LeafNodeType const * const * const mSignFlagsNodes; + InputTreeType const * const mInputTree; + Index32TreeType const * const mRefPointIndexTree; + Int16TreeType const * const mRefSignFlagsTree; + uint32_t * const mQuantizedPoints; + InputValueType const mIsovalue; +}; // struct SeamLineWeights + + +template +struct SetSeamLineFlags +{ + using LeafNodeType = typename TreeType::LeafNodeType; + + SetSeamLineFlags(const std::vector& signFlagsLeafNodes, + const TreeType& refSignFlagsTree) + : mSignFlagsNodes(signFlagsLeafNodes.empty() ? nullptr : &signFlagsLeafNodes.front()) + , mRefSignFlagsTree(&refSignFlagsTree) + { + } + + void operator()(const tbb::blocked_range& range) const + { + tree::ValueAccessor refSignFlagsTreeAcc(*mRefSignFlagsTree); + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + LeafNodeType& signFlagsNode = *mSignFlagsNodes[n]; + const Coord& origin = signFlagsNode.origin(); + + const LeafNodeType * refSignNode = refSignFlagsTreeAcc.probeConstLeaf(origin); + if (!refSignNode) continue; + + for (auto it = signFlagsNode.cbeginValueOn(); it; ++it) { + const Index offset = it.pos(); + + uint8_t rhsSigns = uint8_t(refSignNode->getValue(offset) & SIGNS); + + if (sEdgeGroupTable[rhsSigns][0] > 0) { + + const typename LeafNodeType::ValueType value = it.getValue(); + uint8_t lhsSigns = uint8_t(value & SIGNS); + + if (rhsSigns != lhsSigns) { + signFlagsNode.setValueOnly(offset, value | SEAM); + } + } + + } // end value on loop + + } // end leaf node loop + } + +private: + LeafNodeType * const * const mSignFlagsNodes; + TreeType const * const mRefSignFlagsTree; +}; // struct SetSeamLineFlags + + +template +struct TransferSeamLineFlags +{ + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + using SignDataTreeType = typename BoolTreeType::template ValueConverter::Type; + using SignDataLeafNodeType = typename SignDataTreeType::LeafNodeType; + + TransferSeamLineFlags(const std::vector& signFlagsLeafNodes, + const BoolTreeType& maskTree) + : mSignFlagsNodes(signFlagsLeafNodes.empty() ? nullptr : &signFlagsLeafNodes.front()) + , mMaskTree(&maskTree) + { + } + + void operator()(const tbb::blocked_range& range) const + { + tree::ValueAccessor maskAcc(*mMaskTree); + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + SignDataLeafNodeType& signFlagsNode = *mSignFlagsNodes[n]; + const Coord& origin = signFlagsNode.origin(); + + const BoolLeafNodeType * maskNode = maskAcc.probeConstLeaf(origin); + if (!maskNode) continue; + + using ValueOnCIter = typename SignDataLeafNodeType::ValueOnCIter; + + for (ValueOnCIter it = signFlagsNode.cbeginValueOn(); it; ++it) { + const Index offset = it.pos(); + + if (maskNode->isValueOn(offset)) { + signFlagsNode.setValueOnly(offset, it.getValue() | SEAM); + } + + } // end value on loop + + } // end leaf node loop + } + +private: + SignDataLeafNodeType * const * const mSignFlagsNodes; + BoolTreeType const * const mMaskTree; +}; // struct TransferSeamLineFlags + + +template +struct MaskSeamLineVoxels +{ + using LeafNodeType = typename TreeType::LeafNodeType; + using BoolTreeType = typename TreeType::template ValueConverter::Type; + + MaskSeamLineVoxels(const std::vector& signFlagsLeafNodes, + const TreeType& signFlagsTree, + BoolTreeType& mask) + : mSignFlagsNodes(signFlagsLeafNodes.empty() ? nullptr : &signFlagsLeafNodes.front()) + , mSignFlagsTree(&signFlagsTree) + , mTempMask(false) + , mMask(&mask) + { + } + + MaskSeamLineVoxels(MaskSeamLineVoxels& rhs, tbb::split) + : mSignFlagsNodes(rhs.mSignFlagsNodes) + , mSignFlagsTree(rhs.mSignFlagsTree) + , mTempMask(false) + , mMask(&mTempMask) + { + } + + void join(MaskSeamLineVoxels& rhs) { mMask->merge(*rhs.mMask); } + + void operator()(const tbb::blocked_range& range) + { + using ValueOnCIter = typename LeafNodeType::ValueOnCIter; + using ValueType = typename LeafNodeType::ValueType; + + tree::ValueAccessor signFlagsAcc(*mSignFlagsTree); + tree::ValueAccessor maskAcc(*mMask); + Coord ijk(0, 0, 0); + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + LeafNodeType& signFlagsNode = *mSignFlagsNodes[n]; + + + for (ValueOnCIter it = signFlagsNode.cbeginValueOn(); it; ++it) { + + const ValueType flags = it.getValue(); + + if (!(flags & SEAM) && (flags & EDGES)) { + + ijk = it.getCoord(); + + bool isSeamLineVoxel = false; + + if (flags & XEDGE) { + ijk[1] -= 1; + isSeamLineVoxel = (signFlagsAcc.getValue(ijk) & SEAM); + ijk[2] -= 1; + isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM); + ijk[1] += 1; + isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM); + ijk[2] += 1; + } + + if (!isSeamLineVoxel && flags & YEDGE) { + ijk[2] -= 1; + isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM); + ijk[0] -= 1; + isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM); + ijk[2] += 1; + isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM); + ijk[0] += 1; + } + + if (!isSeamLineVoxel && flags & ZEDGE) { + ijk[1] -= 1; + isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM); + ijk[0] -= 1; + isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM); + ijk[1] += 1; + isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM); + ijk[0] += 1; + } + + if (isSeamLineVoxel) { + maskAcc.setValue(it.getCoord(), true); + } + } + } // end value on loop + + } // end leaf node loop + } + +private: + LeafNodeType * const * const mSignFlagsNodes; + TreeType const * const mSignFlagsTree; + BoolTreeType mTempMask; + BoolTreeType * const mMask; +}; // struct MaskSeamLineVoxels + + +template +inline void +markSeamLineData(SignDataTreeType& signFlagsTree, const SignDataTreeType& refSignFlagsTree) +{ + using SignDataType = typename SignDataTreeType::ValueType; + using SignDataLeafNodeType = typename SignDataTreeType::LeafNodeType; + using BoolTreeType = typename SignDataTreeType::template ValueConverter::Type; + + std::vector signFlagsLeafNodes; + signFlagsTree.getNodes(signFlagsLeafNodes); + + const tbb::blocked_range nodeRange(0, signFlagsLeafNodes.size()); + + tbb::parallel_for(nodeRange, + SetSeamLineFlags(signFlagsLeafNodes, refSignFlagsTree)); + + BoolTreeType seamLineMaskTree(false); + + MaskSeamLineVoxels + maskSeamLine(signFlagsLeafNodes, signFlagsTree, seamLineMaskTree); + + tbb::parallel_reduce(nodeRange, maskSeamLine); + + tbb::parallel_for(nodeRange, + TransferSeamLineFlags(signFlagsLeafNodes, seamLineMaskTree)); +} + + +//////////////////////////////////////// + + +template +struct MergeVoxelRegions +{ + using InputTreeType = typename InputGridType::TreeType; + using InputLeafNodeType = typename InputTreeType::LeafNodeType; + using InputValueType = typename InputLeafNodeType::ValueType; + + using FloatTreeType = typename InputTreeType::template ValueConverter::Type; + using FloatLeafNodeType = typename FloatTreeType::LeafNodeType; + using FloatGridType = Grid; + + using Int16TreeType = typename InputTreeType::template ValueConverter::Type; + using Int16LeafNodeType = typename Int16TreeType::LeafNodeType; + + using Index32TreeType = typename InputTreeType::template ValueConverter::Type; + using Index32LeafNodeType = typename Index32TreeType::LeafNodeType; + + using BoolTreeType = typename InputTreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + MergeVoxelRegions(const InputGridType& inputGrid, + const Index32TreeType& pointIndexTree, + const std::vector& pointIndexLeafNodes, + const std::vector& signFlagsLeafNodes, + InputValueType iso, + float adaptivity, + bool invertSurfaceOrientation); + + void setSpatialAdaptivity(const FloatGridType& grid) + { + mSpatialAdaptivityTree = &grid.tree(); + mSpatialAdaptivityTransform = &grid.transform(); + } + + void setAdaptivityMask(const BoolTreeType& mask) + { + mMaskTree = &mask; + } + + void setRefSignFlagsData(const Int16TreeType& signFlagsData, float internalAdaptivity) + { + mRefSignFlagsTree = &signFlagsData; + mInternalAdaptivity = internalAdaptivity; + } + + void operator()(const tbb::blocked_range&) const; + +private: + InputTreeType const * const mInputTree; + math::Transform const * const mInputTransform; + + Index32TreeType const * const mPointIndexTree; + Index32LeafNodeType * const * const mPointIndexNodes; + Int16LeafNodeType const * const * const mSignFlagsNodes; + + InputValueType mIsovalue; + float mSurfaceAdaptivity, mInternalAdaptivity; + bool mInvertSurfaceOrientation; + + FloatTreeType const * mSpatialAdaptivityTree; + BoolTreeType const * mMaskTree; + Int16TreeType const * mRefSignFlagsTree; + math::Transform const * mSpatialAdaptivityTransform; +}; // struct MergeVoxelRegions + + +template +MergeVoxelRegions::MergeVoxelRegions( + const InputGridType& inputGrid, + const Index32TreeType& pointIndexTree, + const std::vector& pointIndexLeafNodes, + const std::vector& signFlagsLeafNodes, + InputValueType iso, + float adaptivity, + bool invertSurfaceOrientation) + : mInputTree(&inputGrid.tree()) + , mInputTransform(&inputGrid.transform()) + , mPointIndexTree(&pointIndexTree) + , mPointIndexNodes(pointIndexLeafNodes.empty() ? nullptr : &pointIndexLeafNodes.front()) + , mSignFlagsNodes(signFlagsLeafNodes.empty() ? nullptr : &signFlagsLeafNodes.front()) + , mIsovalue(iso) + , mSurfaceAdaptivity(adaptivity) + , mInternalAdaptivity(adaptivity) + , mInvertSurfaceOrientation(invertSurfaceOrientation) + , mSpatialAdaptivityTree(nullptr) + , mMaskTree(nullptr) + , mRefSignFlagsTree(nullptr) + , mSpatialAdaptivityTransform(nullptr) +{ +} + + +template +void +MergeVoxelRegions::operator()(const tbb::blocked_range& range) const +{ + using Vec3sType = math::Vec3; + using Vec3sLeafNodeType = typename InputLeafNodeType::template ValueConverter::Type; + + using InputTreeAccessor = tree::ValueAccessor; + using FloatTreeAccessor = tree::ValueAccessor; + using Index32TreeAccessor = tree::ValueAccessor; + using Int16TreeAccessor = tree::ValueAccessor; + using BoolTreeAccessor = tree::ValueAccessor; + + std::unique_ptr spatialAdaptivityAcc; + if (mSpatialAdaptivityTree && mSpatialAdaptivityTransform) { + spatialAdaptivityAcc.reset(new FloatTreeAccessor(*mSpatialAdaptivityTree)); + } + + std::unique_ptr maskAcc; + if (mMaskTree) { + maskAcc.reset(new BoolTreeAccessor(*mMaskTree)); + } + + std::unique_ptr refSignFlagsAcc; + if (mRefSignFlagsTree) { + refSignFlagsAcc.reset(new Int16TreeAccessor(*mRefSignFlagsTree)); + } + + InputTreeAccessor inputAcc(*mInputTree); + Index32TreeAccessor pointIndexAcc(*mPointIndexTree); + + BoolLeafNodeType mask; + + const bool invertGradientDir = mInvertSurfaceOrientation || isBoolValue(); + std::unique_ptr gradientNode; + + Coord ijk, end; + const int LeafDim = InputLeafNodeType::DIM; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + mask.setValuesOff(); + + const Int16LeafNodeType& signFlagsNode = *mSignFlagsNodes[n]; + Index32LeafNodeType& pointIndexNode = *mPointIndexNodes[n]; + + const Coord& origin = pointIndexNode.origin(); + + end[0] = origin[0] + LeafDim; + end[1] = origin[1] + LeafDim; + end[2] = origin[2] + LeafDim; + + // Mask off seam line adjacent voxels + if (maskAcc) { + const BoolLeafNodeType* maskLeaf = maskAcc->probeConstLeaf(origin); + if (maskLeaf != nullptr) { + for (typename BoolLeafNodeType::ValueOnCIter it = maskLeaf->cbeginValueOn(); + it; ++it) + { + mask.setActiveState(it.getCoord() & ~1u, true); + } + } + } + + float adaptivity = (refSignFlagsAcc && !refSignFlagsAcc->probeConstLeaf(origin)) ? + mInternalAdaptivity : mSurfaceAdaptivity; + + bool useGradients = adaptivity < 1.0f; + + // Set region adaptivity + FloatLeafNodeType adaptivityLeaf(origin, adaptivity); + + if (spatialAdaptivityAcc) { + useGradients = false; + for (Index offset = 0; offset < FloatLeafNodeType::NUM_VALUES; ++offset) { + ijk = adaptivityLeaf.offsetToGlobalCoord(offset); + ijk = mSpatialAdaptivityTransform->worldToIndexCellCentered( + mInputTransform->indexToWorld(ijk)); + float weight = spatialAdaptivityAcc->getValue(ijk); + float adaptivityValue = weight * adaptivity; + if (adaptivityValue < 1.0f) useGradients = true; + adaptivityLeaf.setValueOnly(offset, adaptivityValue); + } + } + + // Mask off ambiguous voxels + for (auto it = signFlagsNode.cbeginValueOn(); it; ++it) { + const Int16 flags = it.getValue(); + const unsigned char signs = static_cast(SIGNS & int(flags)); + + if ((flags & SEAM) || !sAdaptable[signs] || sEdgeGroupTable[signs][0] > 1) { + + mask.setActiveState(it.getCoord() & ~1u, true); + + } else if (flags & EDGES) { + + bool maskRegion = false; + + ijk = it.getCoord(); + if (!pointIndexAcc.isValueOn(ijk)) maskRegion = true; + + if (!maskRegion && flags & XEDGE) { + ijk[1] -= 1; + if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true; + ijk[2] -= 1; + if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true; + ijk[1] += 1; + if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true; + ijk[2] += 1; + } + + if (!maskRegion && flags & YEDGE) { + ijk[2] -= 1; + if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true; + ijk[0] -= 1; + if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true; + ijk[2] += 1; + if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true; + ijk[0] += 1; + } + + if (!maskRegion && flags & ZEDGE) { + ijk[1] -= 1; + if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true; + ijk[0] -= 1; + if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true; + ijk[1] += 1; + if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true; + ijk[0] += 1; + } + + if (maskRegion) { + mask.setActiveState(it.getCoord() & ~1u, true); + } + } + } + + // Mask off topologically ambiguous 2x2x2 voxel sub-blocks + int dim = 2; + for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) { + for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) { + for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) { + if (!mask.isValueOn(ijk) && isNonManifold(inputAcc, ijk, mIsovalue, dim)) { + mask.setActiveState(ijk, true); + } + } + } + } + + // Compute the gradient for the remaining voxels + + if (useGradients) { + + if (gradientNode) { + gradientNode->setValuesOff(); + } else { + gradientNode.reset(new Vec3sLeafNodeType()); + } + + for (auto it = signFlagsNode.cbeginValueOn(); it; ++it) { + ijk = it.getCoord(); + if (!mask.isValueOn(ijk & ~1u)) { + Vec3sType dir(math::ISGradient::result(inputAcc, ijk)); + dir.normalize(); + + if (invertGradientDir) { + dir = -dir; + } + + gradientNode->setValueOn(it.pos(), dir); + } + } + } + + // Merge regions + int regionId = 1; + for ( ; dim <= LeafDim; dim = dim << 1) { + const unsigned coordMask = ~((dim << 1) - 1); + for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) { + for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) { + for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) { + + adaptivity = adaptivityLeaf.getValue(ijk); + + if (mask.isValueOn(ijk) + || isNonManifold(inputAcc, ijk, mIsovalue, dim) + || (useGradients && !isMergable(*gradientNode, ijk, dim, adaptivity))) + { + mask.setActiveState(ijk & coordMask, true); + } else { + mergeVoxels(pointIndexNode, ijk, dim, regionId++); + } + } + } + } + } + } +} // MergeVoxelRegions::operator() + + +//////////////////////////////////////// + + +// Constructs qudas +struct UniformPrimBuilder +{ + UniformPrimBuilder(): mIdx(0), mPolygonPool(nullptr) {} + + void init(const size_t upperBound, PolygonPool& quadPool) + { + mPolygonPool = &quadPool; + mPolygonPool->resetQuads(upperBound); + mIdx = 0; + } + + template + void addPrim(const math::Vec4& verts, bool reverse, char flags = 0) + { + if (!reverse) { + mPolygonPool->quad(mIdx) = verts; + } else { + Vec4I& quad = mPolygonPool->quad(mIdx); + quad[0] = verts[3]; + quad[1] = verts[2]; + quad[2] = verts[1]; + quad[3] = verts[0]; + } + mPolygonPool->quadFlags(mIdx) = flags; + ++mIdx; + } + + void done() + { + mPolygonPool->trimQuads(mIdx); + } + +private: + size_t mIdx; + PolygonPool* mPolygonPool; +}; + + +// Constructs qudas and triangles +struct AdaptivePrimBuilder +{ + AdaptivePrimBuilder() : mQuadIdx(0), mTriangleIdx(0), mPolygonPool(nullptr) {} + + void init(const size_t upperBound, PolygonPool& polygonPool) + { + mPolygonPool = &polygonPool; + mPolygonPool->resetQuads(upperBound); + mPolygonPool->resetTriangles(upperBound); + + mQuadIdx = 0; + mTriangleIdx = 0; + } + + template + void addPrim(const math::Vec4& verts, bool reverse, char flags = 0) + { + if (verts[0] != verts[1] && verts[0] != verts[2] && verts[0] != verts[3] + && verts[1] != verts[2] && verts[1] != verts[3] && verts[2] != verts[3]) { + mPolygonPool->quadFlags(mQuadIdx) = flags; + addQuad(verts, reverse); + } else if ( + verts[0] == verts[3] && + verts[1] != verts[2] && + verts[1] != verts[0] && + verts[2] != verts[0]) { + mPolygonPool->triangleFlags(mTriangleIdx) = flags; + addTriangle(verts[0], verts[1], verts[2], reverse); + } else if ( + verts[1] == verts[2] && + verts[0] != verts[3] && + verts[0] != verts[1] && + verts[3] != verts[1]) { + mPolygonPool->triangleFlags(mTriangleIdx) = flags; + addTriangle(verts[0], verts[1], verts[3], reverse); + } else if ( + verts[0] == verts[1] && + verts[2] != verts[3] && + verts[2] != verts[0] && + verts[3] != verts[0]) { + mPolygonPool->triangleFlags(mTriangleIdx) = flags; + addTriangle(verts[0], verts[2], verts[3], reverse); + } else if ( + verts[2] == verts[3] && + verts[0] != verts[1] && + verts[0] != verts[2] && + verts[1] != verts[2]) { + mPolygonPool->triangleFlags(mTriangleIdx) = flags; + addTriangle(verts[0], verts[1], verts[2], reverse); + } + } + + + void done() + { + mPolygonPool->trimQuads(mQuadIdx, /*reallocate=*/true); + mPolygonPool->trimTrinagles(mTriangleIdx, /*reallocate=*/true); + } + +private: + + template + void addQuad(const math::Vec4& verts, bool reverse) + { + if (!reverse) { + mPolygonPool->quad(mQuadIdx) = verts; + } else { + Vec4I& quad = mPolygonPool->quad(mQuadIdx); + quad[0] = verts[3]; + quad[1] = verts[2]; + quad[2] = verts[1]; + quad[3] = verts[0]; + } + ++mQuadIdx; + } + + void addTriangle(unsigned v0, unsigned v1, unsigned v2, bool reverse) + { + Vec3I& prim = mPolygonPool->triangle(mTriangleIdx); + + prim[1] = v1; + + if (!reverse) { + prim[0] = v0; + prim[2] = v2; + } else { + prim[0] = v2; + prim[2] = v0; + } + ++mTriangleIdx; + } + + size_t mQuadIdx, mTriangleIdx; + PolygonPool *mPolygonPool; +}; + + +template +inline void +constructPolygons( + bool invertSurfaceOrientation, + Int16 flags, + Int16 refFlags, + const Vec3i& offsets, + const Coord& ijk, + const SignAccT& signAcc, + const IdxAccT& idxAcc, + PrimBuilder& mesher) +{ + using IndexType = typename IdxAccT::ValueType; + + IndexType v0 = IndexType(util::INVALID_IDX); + const bool isActive = idxAcc.probeValue(ijk, v0); + if (isActive == false || v0 == IndexType(util::INVALID_IDX)) return; + + char tag[2]; + tag[0] = (flags & SEAM) ? POLYFLAG_FRACTURE_SEAM : 0; + tag[1] = tag[0] | char(POLYFLAG_EXTERIOR); + + bool isInside = flags & INSIDE; + + isInside = invertSurfaceOrientation ? !isInside : isInside; + + Coord coord = ijk; + math::Vec4 quad(0,0,0,0); + + if (flags & XEDGE) { + + quad[0] = v0 + offsets[0]; + + // i, j-1, k + coord[1]--; + bool activeValues = idxAcc.probeValue(coord, quad[1]); + uint8_t cell = uint8_t(SIGNS & signAcc.getValue(coord)); + quad[1] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][5] - 1 : 0; + + // i, j-1, k-1 + coord[2]--; + activeValues = activeValues && idxAcc.probeValue(coord, quad[2]); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + quad[2] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][7] - 1 : 0; + + // i, j, k-1 + coord[1]++; + activeValues = activeValues && idxAcc.probeValue(coord, quad[3]); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + quad[3] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][3] - 1 : 0; + + if (activeValues) { + mesher.addPrim(quad, isInside, tag[bool(refFlags & XEDGE)]); + } + + coord[2]++; // i, j, k + } + + + if (flags & YEDGE) { + + quad[0] = v0 + offsets[1]; + + // i, j, k-1 + coord[2]--; + bool activeValues = idxAcc.probeValue(coord, quad[1]); + uint8_t cell = uint8_t(SIGNS & signAcc.getValue(coord)); + quad[1] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][12] - 1 : 0; + + // i-1, j, k-1 + coord[0]--; + activeValues = activeValues && idxAcc.probeValue(coord, quad[2]); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + quad[2] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][11] - 1 : 0; + + // i-1, j, k + coord[2]++; + activeValues = activeValues && idxAcc.probeValue(coord, quad[3]); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + quad[3] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][10] - 1 : 0; + + if (activeValues) { + mesher.addPrim(quad, isInside, tag[bool(refFlags & YEDGE)]); + } + + coord[0]++; // i, j, k + } + + + if (flags & ZEDGE) { + + quad[0] = v0 + offsets[2]; + + // i, j-1, k + coord[1]--; + bool activeValues = idxAcc.probeValue(coord, quad[1]); + uint8_t cell = uint8_t(SIGNS & signAcc.getValue(coord)); + quad[1] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][8] - 1 : 0; + + // i-1, j-1, k + coord[0]--; + activeValues = activeValues && idxAcc.probeValue(coord, quad[2]); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + quad[2] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][6] - 1 : 0; + + // i-1, j, k + coord[1]++; + activeValues = activeValues && idxAcc.probeValue(coord, quad[3]); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + quad[3] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][2] - 1 : 0; + + if (activeValues) { + mesher.addPrim(quad, !isInside, tag[bool(refFlags & ZEDGE)]); + } + } +} + + +//////////////////////////////////////// + + +template +struct MaskTileBorders +{ + using InputValueType = typename InputTreeType::ValueType; + using BoolTreeType = typename InputTreeType::template ValueConverter::Type; + + + MaskTileBorders(const InputTreeType& inputTree, InputValueType iso, + BoolTreeType& mask, const Vec4i* tileArray) + : mInputTree(&inputTree) + , mIsovalue(iso) + , mTempMask(false) + , mMask(&mask) + , mTileArray(tileArray) + { + } + + MaskTileBorders(MaskTileBorders& rhs, tbb::split) + : mInputTree(rhs.mInputTree) + , mIsovalue(rhs.mIsovalue) + , mTempMask(false) + , mMask(&mTempMask) + , mTileArray(rhs.mTileArray) + { + } + + void join(MaskTileBorders& rhs) { mMask->merge(*rhs.mMask); } + + void operator()(const tbb::blocked_range&); + +private: + InputTreeType const * const mInputTree; + InputValueType const mIsovalue; + BoolTreeType mTempMask; + BoolTreeType * const mMask; + Vec4i const * const mTileArray; +}; // MaskTileBorders + + +template +void +MaskTileBorders::operator()(const tbb::blocked_range& range) +{ + tree::ValueAccessor inputTreeAcc(*mInputTree); + + CoordBBox region, bbox; + Coord ijk, nijk; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + const Vec4i& tile = mTileArray[n]; + + bbox.min()[0] = tile[0]; + bbox.min()[1] = tile[1]; + bbox.min()[2] = tile[2]; + bbox.max() = bbox.min(); + bbox.max().offset(tile[3]); + + InputValueType value = mInputTree->background(); + + const bool isInside = isInsideValue(inputTreeAcc.getValue(bbox.min()), mIsovalue); + const int valueDepth = inputTreeAcc.getValueDepth(bbox.min()); + + // eval x-edges + + ijk = bbox.max(); + nijk = ijk; + ++nijk[0]; + + bool processRegion = true; + if (valueDepth >= inputTreeAcc.getValueDepth(nijk)) { + processRegion = isInside != isInsideValue(inputTreeAcc.getValue(nijk), mIsovalue); + } + + if (processRegion) { + region = bbox; + region.expand(1); + region.min()[0] = region.max()[0] = ijk[0]; + mMask->fill(region, false); + } + + + ijk = bbox.min(); + --ijk[0]; + + processRegion = true; + if (valueDepth >= inputTreeAcc.getValueDepth(ijk)) { + processRegion = (!inputTreeAcc.probeValue(ijk, value) + && isInside != isInsideValue(value, mIsovalue)); + } + + if (processRegion) { + region = bbox; + region.expand(1); + region.min()[0] = region.max()[0] = ijk[0]; + mMask->fill(region, false); + } + + + // eval y-edges + + ijk = bbox.max(); + nijk = ijk; + ++nijk[1]; + + processRegion = true; + if (valueDepth >= inputTreeAcc.getValueDepth(nijk)) { + processRegion = isInside != isInsideValue(inputTreeAcc.getValue(nijk), mIsovalue); + } + + if (processRegion) { + region = bbox; + region.expand(1); + region.min()[1] = region.max()[1] = ijk[1]; + mMask->fill(region, false); + } + + + ijk = bbox.min(); + --ijk[1]; + + processRegion = true; + if (valueDepth >= inputTreeAcc.getValueDepth(ijk)) { + processRegion = (!inputTreeAcc.probeValue(ijk, value) + && isInside != isInsideValue(value, mIsovalue)); + } + + if (processRegion) { + region = bbox; + region.expand(1); + region.min()[1] = region.max()[1] = ijk[1]; + mMask->fill(region, false); + } + + + // eval z-edges + + ijk = bbox.max(); + nijk = ijk; + ++nijk[2]; + + processRegion = true; + if (valueDepth >= inputTreeAcc.getValueDepth(nijk)) { + processRegion = isInside != isInsideValue(inputTreeAcc.getValue(nijk), mIsovalue); + } + + if (processRegion) { + region = bbox; + region.expand(1); + region.min()[2] = region.max()[2] = ijk[2]; + mMask->fill(region, false); + } + + ijk = bbox.min(); + --ijk[2]; + + processRegion = true; + if (valueDepth >= inputTreeAcc.getValueDepth(ijk)) { + processRegion = (!inputTreeAcc.probeValue(ijk, value) + && isInside != isInsideValue(value, mIsovalue)); + } + + if (processRegion) { + region = bbox; + region.expand(1); + region.min()[2] = region.max()[2] = ijk[2]; + mMask->fill(region, false); + } + } +} // MaskTileBorders::operator() + + +template +inline void +maskActiveTileBorders(const InputTreeType& inputTree, typename InputTreeType::ValueType iso, + typename InputTreeType::template ValueConverter::Type& mask) +{ + typename InputTreeType::ValueOnCIter tileIter(inputTree); + tileIter.setMaxDepth(InputTreeType::ValueOnCIter::LEAF_DEPTH - 1); + + size_t tileCount = 0; + for ( ; tileIter; ++tileIter) { + ++tileCount; + } + + if (tileCount > 0) { + std::unique_ptr tiles(new Vec4i[tileCount]); + + CoordBBox bbox; + size_t index = 0; + + tileIter = inputTree.cbeginValueOn(); + tileIter.setMaxDepth(InputTreeType::ValueOnCIter::LEAF_DEPTH - 1); + + for (; tileIter; ++tileIter) { + Vec4i& tile = tiles[index++]; + tileIter.getBoundingBox(bbox); + tile[0] = bbox.min()[0]; + tile[1] = bbox.min()[1]; + tile[2] = bbox.min()[2]; + tile[3] = bbox.max()[0] - bbox.min()[0]; + } + + MaskTileBorders op(inputTree, iso, mask, tiles.get()); + tbb::parallel_reduce(tbb::blocked_range(0, tileCount), op); + } +} + + +//////////////////////////////////////// + + +// Utility class for the volumeToMesh wrapper +class PointListCopy +{ +public: + PointListCopy(const PointList& pointsIn, std::vector& pointsOut) + : mPointsIn(pointsIn) , mPointsOut(pointsOut) + { + } + + void operator()(const tbb::blocked_range& range) const + { + for (size_t n = range.begin(); n < range.end(); ++n) { + mPointsOut[n] = mPointsIn[n]; + } + } + +private: + const PointList& mPointsIn; + std::vector& mPointsOut; +}; + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +struct LeafNodeVoxelOffsets +{ + using IndexVector = std::vector; + + template + void constructOffsetList(); + + /// Return internal core voxel offsets. + const IndexVector& core() const { return mCore; } + + + /// Return front face voxel offsets. + const IndexVector& minX() const { return mMinX; } + + /// Return back face voxel offsets. + const IndexVector& maxX() const { return mMaxX; } + + + /// Return bottom face voxel offsets. + const IndexVector& minY() const { return mMinY; } + + /// Return top face voxel offsets. + const IndexVector& maxY() const { return mMaxY; } + + + /// Return left face voxel offsets. + const IndexVector& minZ() const { return mMinZ; } + + /// Return right face voxel offsets. + const IndexVector& maxZ() const { return mMaxZ; } + + + /// Return voxel offsets with internal neighbours in x + 1. + const IndexVector& internalNeighborsX() const { return mInternalNeighborsX; } + + /// Return voxel offsets with internal neighbours in y + 1. + const IndexVector& internalNeighborsY() const { return mInternalNeighborsY; } + + /// Return voxel offsets with internal neighbours in z + 1. + const IndexVector& internalNeighborsZ() const { return mInternalNeighborsZ; } + + +private: + IndexVector mCore, mMinX, mMaxX, mMinY, mMaxY, mMinZ, mMaxZ, + mInternalNeighborsX, mInternalNeighborsY, mInternalNeighborsZ; +}; // struct LeafNodeOffsets + + +template +inline void +LeafNodeVoxelOffsets::constructOffsetList() +{ + // internal core voxels + mCore.clear(); + mCore.reserve((LeafNodeType::DIM - 2) * (LeafNodeType::DIM - 2)); + + for (Index x = 1; x < (LeafNodeType::DIM - 1); ++x) { + const Index offsetX = x << (2 * LeafNodeType::LOG2DIM); + for (Index y = 1; y < (LeafNodeType::DIM - 1); ++y) { + const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM); + for (Index z = 1; z < (LeafNodeType::DIM - 1); ++z) { + mCore.push_back(offsetXY + z); + } + } + } + + // internal neighbors in x + 1 + mInternalNeighborsX.clear(); + mInternalNeighborsX.reserve(LeafNodeType::SIZE - (LeafNodeType::DIM * LeafNodeType::DIM)); + + for (Index x = 0; x < (LeafNodeType::DIM - 1); ++x) { + const Index offsetX = x << (2 * LeafNodeType::LOG2DIM); + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM); + for (Index z = 0; z < LeafNodeType::DIM; ++z) { + mInternalNeighborsX.push_back(offsetXY + z); + } + } + } + + // internal neighbors in y + 1 + mInternalNeighborsY.clear(); + mInternalNeighborsY.reserve(LeafNodeType::SIZE - (LeafNodeType::DIM * LeafNodeType::DIM)); + + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + const Index offsetX = x << (2 * LeafNodeType::LOG2DIM); + for (Index y = 0; y < (LeafNodeType::DIM - 1); ++y) { + const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM); + for (Index z = 0; z < LeafNodeType::DIM; ++z) { + mInternalNeighborsY.push_back(offsetXY + z); + } + } + } + + // internal neighbors in z + 1 + mInternalNeighborsZ.clear(); + mInternalNeighborsZ.reserve(LeafNodeType::SIZE - (LeafNodeType::DIM * LeafNodeType::DIM)); + + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + const Index offsetX = x << (2 * LeafNodeType::LOG2DIM); + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM); + for (Index z = 0; z < (LeafNodeType::DIM - 1); ++z) { + mInternalNeighborsZ.push_back(offsetXY + z); + } + } + } + + // min x + mMinX.clear(); + mMinX.reserve(LeafNodeType::DIM * LeafNodeType::DIM); + { + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + const Index offsetXY = (y << LeafNodeType::LOG2DIM); + for (Index z = 0; z < LeafNodeType::DIM; ++z) { + mMinX.push_back(offsetXY + z); + } + } + } + + // max x + mMaxX.clear(); + mMaxX.reserve(LeafNodeType::DIM * LeafNodeType::DIM); + { + const Index offsetX = (LeafNodeType::DIM - 1) << (2 * LeafNodeType::LOG2DIM); + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM); + for (Index z = 0; z < LeafNodeType::DIM; ++z) { + mMaxX.push_back(offsetXY + z); + } + } + } + + // min y + mMinY.clear(); + mMinY.reserve(LeafNodeType::DIM * LeafNodeType::DIM); + { + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + const Index offsetX = x << (2 * LeafNodeType::LOG2DIM); + for (Index z = 0; z < (LeafNodeType::DIM - 1); ++z) { + mMinY.push_back(offsetX + z); + } + } + } + + // max y + mMaxY.clear(); + mMaxY.reserve(LeafNodeType::DIM * LeafNodeType::DIM); + { + const Index offsetY = (LeafNodeType::DIM - 1) << LeafNodeType::LOG2DIM; + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + const Index offsetX = x << (2 * LeafNodeType::LOG2DIM); + for (Index z = 0; z < (LeafNodeType::DIM - 1); ++z) { + mMaxY.push_back(offsetX + offsetY + z); + } + } + } + + // min z + mMinZ.clear(); + mMinZ.reserve(LeafNodeType::DIM * LeafNodeType::DIM); + { + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + const Index offsetX = x << (2 * LeafNodeType::LOG2DIM); + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM); + mMinZ.push_back(offsetXY); + } + } + } + + // max z + mMaxZ.clear(); + mMaxZ.reserve(LeafNodeType::DIM * LeafNodeType::DIM); + { + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + const Index offsetX = x << (2 * LeafNodeType::LOG2DIM); + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM); + mMaxZ.push_back(offsetXY + (LeafNodeType::DIM - 1)); + } + } + } +} + + +//////////////////////////////////////// + + +/// Utility method to marks all voxels that share an edge. +template +struct VoxelEdgeAccessor { + + enum { AXIS = _AXIS }; + AccessorT& acc; + + VoxelEdgeAccessor(AccessorT& _acc) : acc(_acc) {} + + void set(Coord ijk) { + if (_AXIS == 0) { // x + 1 edge + acc.setActiveState(ijk); + --ijk[1]; // set i, j-1, k + acc.setActiveState(ijk); + --ijk[2]; // set i, j-1, k-1 + acc.setActiveState(ijk); + ++ijk[1]; // set i, j, k-1 + acc.setActiveState(ijk); + } else if (_AXIS == 1) { // y + 1 edge + acc.setActiveState(ijk); + --ijk[2]; // set i, j, k-1 + acc.setActiveState(ijk); + --ijk[0]; // set i-1, j, k-1 + acc.setActiveState(ijk); + ++ijk[2]; // set i-1, j, k + acc.setActiveState(ijk); + } else { // z + 1 edge + acc.setActiveState(ijk); + --ijk[1]; // set i, j-1, k + acc.setActiveState(ijk); + --ijk[0]; // set i-1, j-1, k + acc.setActiveState(ijk); + ++ijk[1]; // set i-1, j, k + acc.setActiveState(ijk); + } + } +}; + + +/// Utility method to check for sign changes along the x + 1, y + 1 or z + 1 directions. +/// The direction is determined by the @a edgeAcc parameter. Only voxels that have internal +/// neighbours are evaluated. +template +void +evalInternalVoxelEdges(VoxelEdgeAcc& edgeAcc, const LeafNode& leafnode, + const LeafNodeVoxelOffsets& voxels, const typename LeafNode::ValueType iso) +{ + Index nvo = 1; // neighbour voxel offset, z + 1 direction assumed initially. + const std::vector* offsets = &voxels.internalNeighborsZ(); + + if (VoxelEdgeAcc::AXIS == 0) { // x + 1 direction + nvo = LeafNode::DIM * LeafNode::DIM; + offsets = &voxels.internalNeighborsX(); + } else if (VoxelEdgeAcc::AXIS == 1) { // y + 1 direction + nvo = LeafNode::DIM; + offsets = &voxels.internalNeighborsY(); + } + + for (size_t n = 0, N = offsets->size(); n < N; ++n) { + const Index& pos = (*offsets)[n]; + bool isActive = leafnode.isValueOn(pos) || leafnode.isValueOn(pos + nvo); + if (isActive && (isInsideValue(leafnode.getValue(pos), iso) != + isInsideValue(leafnode.getValue(pos + nvo), iso))) { + edgeAcc.set(leafnode.offsetToGlobalCoord(pos)); + } + } +} + + +/// Utility method to check for sign changes along the x + 1, y + 1 or z + 1 directions. +/// The direction is determined by the @a edgeAcc parameter. All voxels that reside in the +/// specified leafnode face: back, top or right are evaluated. +template +void +evalExtrenalVoxelEdges(VoxelEdgeAcc& edgeAcc, TreeAcc& acc, const LeafNode& lhsNode, + const LeafNodeVoxelOffsets& voxels, const typename LeafNode::ValueType iso) +{ + const std::vector* lhsOffsets = &voxels.maxX(); + const std::vector* rhsOffsets = &voxels.minX(); + Coord ijk = lhsNode.origin(); + + if (VoxelEdgeAcc::AXIS == 0) { // back leafnode face + ijk[0] += LeafNode::DIM; + } else if (VoxelEdgeAcc::AXIS == 1) { // top leafnode face + ijk[1] += LeafNode::DIM; + lhsOffsets = &voxels.maxY(); + rhsOffsets = &voxels.minY(); + } else if (VoxelEdgeAcc::AXIS == 2) { // right leafnode face + ijk[2] += LeafNode::DIM; + lhsOffsets = &voxels.maxZ(); + rhsOffsets = &voxels.minZ(); + } + + typename LeafNode::ValueType value; + const LeafNode* rhsNodePt = acc.probeConstLeaf(ijk); + + if (rhsNodePt) { + for (size_t n = 0, N = lhsOffsets->size(); n < N; ++n) { + const Index& pos = (*lhsOffsets)[n]; + bool isActive = lhsNode.isValueOn(pos) || rhsNodePt->isValueOn((*rhsOffsets)[n]); + if (isActive && (isInsideValue(lhsNode.getValue(pos), iso) != + isInsideValue(rhsNodePt->getValue((*rhsOffsets)[n]), iso))) { + edgeAcc.set(lhsNode.offsetToGlobalCoord(pos)); + } + } + } else if (!acc.probeValue(ijk, value)) { + const bool inside = isInsideValue(value, iso); + for (size_t n = 0, N = lhsOffsets->size(); n < N; ++n) { + const Index& pos = (*lhsOffsets)[n]; + if (lhsNode.isValueOn(pos) && (inside != isInsideValue(lhsNode.getValue(pos), iso))) { + edgeAcc.set(lhsNode.offsetToGlobalCoord(pos)); + } + } + } +} + + +/// Utility method to check for sign changes along the x - 1, y - 1 or z - 1 directions. +/// The direction is determined by the @a edgeAcc parameter. All voxels that reside in the +/// specified leafnode face: front, bottom or left are evaluated. +template +void +evalExtrenalVoxelEdgesInv(VoxelEdgeAcc& edgeAcc, TreeAcc& acc, const LeafNode& leafnode, + const LeafNodeVoxelOffsets& voxels, const typename LeafNode::ValueType iso) +{ + Coord ijk = leafnode.origin(); + if (VoxelEdgeAcc::AXIS == 0) --ijk[0]; // front leafnode face + else if (VoxelEdgeAcc::AXIS == 1) --ijk[1]; // bottom leafnode face + else if (VoxelEdgeAcc::AXIS == 2) --ijk[2]; // left leafnode face + + typename LeafNode::ValueType value; + if (!acc.probeConstLeaf(ijk) && !acc.probeValue(ijk, value)) { + + const std::vector* offsets = &voxels.internalNeighborsX(); + if (VoxelEdgeAcc::AXIS == 1) offsets = &voxels.internalNeighborsY(); + else if (VoxelEdgeAcc::AXIS == 2) offsets = &voxels.internalNeighborsZ(); + + const bool inside = isInsideValue(value, iso); + for (size_t n = 0, N = offsets->size(); n < N; ++n) { + + const Index& pos = (*offsets)[n]; + if (leafnode.isValueOn(pos) + && (inside != isInsideValue(leafnode.getValue(pos), iso))) + { + ijk = leafnode.offsetToGlobalCoord(pos); + if (VoxelEdgeAcc::AXIS == 0) --ijk[0]; + else if (VoxelEdgeAcc::AXIS == 1) --ijk[1]; + else if (VoxelEdgeAcc::AXIS == 2) --ijk[2]; + + edgeAcc.set(ijk); + } + } + } +} + + + +template +struct IdentifyIntersectingVoxels +{ + using InputLeafNodeType = typename InputTreeType::LeafNodeType; + using InputValueType = typename InputLeafNodeType::ValueType; + + using BoolTreeType = typename InputTreeType::template ValueConverter::Type; + + IdentifyIntersectingVoxels( + const InputTreeType& inputTree, + const std::vector& inputLeafNodes, + BoolTreeType& intersectionTree, + InputValueType iso); + + IdentifyIntersectingVoxels(IdentifyIntersectingVoxels&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(const IdentifyIntersectingVoxels& rhs) { + mIntersectionAccessor.tree().merge(rhs.mIntersectionAccessor.tree()); + } + +private: + tree::ValueAccessor mInputAccessor; + InputLeafNodeType const * const * const mInputNodes; + + BoolTreeType mIntersectionTree; + tree::ValueAccessor mIntersectionAccessor; + + LeafNodeVoxelOffsets mOffsetData; + const LeafNodeVoxelOffsets* mOffsets; + + InputValueType mIsovalue; +}; // struct IdentifyIntersectingVoxels + + +template +IdentifyIntersectingVoxels::IdentifyIntersectingVoxels( + const InputTreeType& inputTree, + const std::vector& inputLeafNodes, + BoolTreeType& intersectionTree, + InputValueType iso) + : mInputAccessor(inputTree) + , mInputNodes(inputLeafNodes.empty() ? nullptr : &inputLeafNodes.front()) + , mIntersectionTree(false) + , mIntersectionAccessor(intersectionTree) + , mOffsetData() + , mOffsets(&mOffsetData) + , mIsovalue(iso) +{ + mOffsetData.constructOffsetList(); +} + + +template +IdentifyIntersectingVoxels::IdentifyIntersectingVoxels( + IdentifyIntersectingVoxels& rhs, tbb::split) + : mInputAccessor(rhs.mInputAccessor.tree()) + , mInputNodes(rhs.mInputNodes) + , mIntersectionTree(false) + , mIntersectionAccessor(mIntersectionTree) // use local tree. + , mOffsetData() + , mOffsets(rhs.mOffsets) // reference data from main instance. + , mIsovalue(rhs.mIsovalue) +{ +} + + +template +void +IdentifyIntersectingVoxels::operator()(const tbb::blocked_range& range) +{ + VoxelEdgeAccessor, 0> xEdgeAcc(mIntersectionAccessor); + VoxelEdgeAccessor, 1> yEdgeAcc(mIntersectionAccessor); + VoxelEdgeAccessor, 2> zEdgeAcc(mIntersectionAccessor); + + for (size_t n = range.begin(); n != range.end(); ++n) { + + const InputLeafNodeType& node = *mInputNodes[n]; + + // internal x + 1 voxel edges + evalInternalVoxelEdges(xEdgeAcc, node, *mOffsets, mIsovalue); + // internal y + 1 voxel edges + evalInternalVoxelEdges(yEdgeAcc, node, *mOffsets, mIsovalue); + // internal z + 1 voxel edges + evalInternalVoxelEdges(zEdgeAcc, node, *mOffsets, mIsovalue); + + // external x + 1 voxels edges (back face) + evalExtrenalVoxelEdges(xEdgeAcc, mInputAccessor, node, *mOffsets, mIsovalue); + // external y + 1 voxels edges (top face) + evalExtrenalVoxelEdges(yEdgeAcc, mInputAccessor, node, *mOffsets, mIsovalue); + // external z + 1 voxels edges (right face) + evalExtrenalVoxelEdges(zEdgeAcc, mInputAccessor, node, *mOffsets, mIsovalue); + + // The remaining edges are only checked if the leafnode neighbour, in the + // corresponding direction, is an inactive tile. + + // external x - 1 voxels edges (front face) + evalExtrenalVoxelEdgesInv(xEdgeAcc, mInputAccessor, node, *mOffsets, mIsovalue); + // external y - 1 voxels edges (bottom face) + evalExtrenalVoxelEdgesInv(yEdgeAcc, mInputAccessor, node, *mOffsets, mIsovalue); + // external z - 1 voxels edges (left face) + evalExtrenalVoxelEdgesInv(zEdgeAcc, mInputAccessor, node, *mOffsets, mIsovalue); + } +} // IdentifyIntersectingVoxels::operator() + + +template +inline void +identifySurfaceIntersectingVoxels( + typename InputTreeType::template ValueConverter::Type& intersectionTree, + const InputTreeType& inputTree, + typename InputTreeType::ValueType isovalue) +{ + using InputLeafNodeType = typename InputTreeType::LeafNodeType; + + std::vector inputLeafNodes; + inputTree.getNodes(inputLeafNodes); + + IdentifyIntersectingVoxels op( + inputTree, inputLeafNodes, intersectionTree, isovalue); + + tbb::parallel_reduce(tbb::blocked_range(0, inputLeafNodes.size()), op); + + maskActiveTileBorders(inputTree, isovalue, intersectionTree); +} + + +//////////////////////////////////////// + + +template +struct MaskIntersectingVoxels +{ + using InputLeafNodeType = typename InputTreeType::LeafNodeType; + using InputValueType = typename InputLeafNodeType::ValueType; + + using BoolTreeType = typename InputTreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + MaskIntersectingVoxels( + const InputTreeType& inputTree, + const std::vector& nodes, + BoolTreeType& intersectionTree, + InputValueType iso); + + MaskIntersectingVoxels(MaskIntersectingVoxels&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(const MaskIntersectingVoxels& rhs) { + mIntersectionAccessor.tree().merge(rhs.mIntersectionAccessor.tree()); + } + +private: + tree::ValueAccessor mInputAccessor; + BoolLeafNodeType const * const * const mNodes; + + BoolTreeType mIntersectionTree; + tree::ValueAccessor mIntersectionAccessor; + + InputValueType mIsovalue; +}; // struct MaskIntersectingVoxels + + +template +MaskIntersectingVoxels::MaskIntersectingVoxels( + const InputTreeType& inputTree, + const std::vector& nodes, + BoolTreeType& intersectionTree, + InputValueType iso) + : mInputAccessor(inputTree) + , mNodes(nodes.empty() ? nullptr : &nodes.front()) + , mIntersectionTree(false) + , mIntersectionAccessor(intersectionTree) + , mIsovalue(iso) +{ +} + + +template +MaskIntersectingVoxels::MaskIntersectingVoxels( + MaskIntersectingVoxels& rhs, tbb::split) + : mInputAccessor(rhs.mInputAccessor.tree()) + , mNodes(rhs.mNodes) + , mIntersectionTree(false) + , mIntersectionAccessor(mIntersectionTree) // use local tree. + , mIsovalue(rhs.mIsovalue) +{ +} + + +template +void +MaskIntersectingVoxels::operator()(const tbb::blocked_range& range) +{ + VoxelEdgeAccessor, 0> xEdgeAcc(mIntersectionAccessor); + VoxelEdgeAccessor, 1> yEdgeAcc(mIntersectionAccessor); + VoxelEdgeAccessor, 2> zEdgeAcc(mIntersectionAccessor); + + Coord ijk(0, 0, 0); + InputValueType iso(mIsovalue); + + for (size_t n = range.begin(); n != range.end(); ++n) { + + const BoolLeafNodeType& node = *mNodes[n]; + + for (typename BoolLeafNodeType::ValueOnCIter it = node.cbeginValueOn(); it; ++it) { + + if (!it.getValue()) { + + ijk = it.getCoord(); + + const bool inside = isInsideValue(mInputAccessor.getValue(ijk), iso); + + if (inside != isInsideValue(mInputAccessor.getValue(ijk.offsetBy(1, 0, 0)), iso)) { + xEdgeAcc.set(ijk); + } + + if (inside != isInsideValue(mInputAccessor.getValue(ijk.offsetBy(0, 1, 0)), iso)) { + yEdgeAcc.set(ijk); + } + + if (inside != isInsideValue(mInputAccessor.getValue(ijk.offsetBy(0, 0, 1)), iso)) { + zEdgeAcc.set(ijk); + } + } + } + } +} // MaskIntersectingVoxels::operator() + + +template +struct MaskBorderVoxels +{ + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + MaskBorderVoxels(const BoolTreeType& maskTree, + const std::vector& maskNodes, + BoolTreeType& borderTree) + : mMaskTree(&maskTree) + , mMaskNodes(maskNodes.empty() ? nullptr : &maskNodes.front()) + , mTmpBorderTree(false) + , mBorderTree(&borderTree) + { + } + + MaskBorderVoxels(MaskBorderVoxels& rhs, tbb::split) + : mMaskTree(rhs.mMaskTree) + , mMaskNodes(rhs.mMaskNodes) + , mTmpBorderTree(false) + , mBorderTree(&mTmpBorderTree) + { + } + + void join(MaskBorderVoxels& rhs) { mBorderTree->merge(*rhs.mBorderTree); } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor maskAcc(*mMaskTree); + tree::ValueAccessor borderAcc(*mBorderTree); + Coord ijk(0, 0, 0); + + for (size_t n = range.begin(); n != range.end(); ++n) { + + const BoolLeafNodeType& node = *mMaskNodes[n]; + + for (typename BoolLeafNodeType::ValueOnCIter it = node.cbeginValueOn(); it; ++it) { + + ijk = it.getCoord(); + + const bool lhs = it.getValue(); + bool rhs = lhs; + + bool isEdgeVoxel = false; + + ijk[2] += 1; // i, j, k+1 + isEdgeVoxel = (maskAcc.probeValue(ijk, rhs) && lhs != rhs); + + ijk[1] += 1; // i, j+1, k+1 + isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs); + + ijk[0] += 1; // i+1, j+1, k+1 + isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs); + + ijk[1] -= 1; // i+1, j, k+1 + isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs); + + + ijk[2] -= 1; // i+1, j, k + isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs); + + ijk[1] += 1; // i+1, j+1, k + isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs); + + ijk[0] -= 1; // i, j+1, k + isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs); + + if (isEdgeVoxel) { + ijk[1] -= 1; // i, j, k + borderAcc.setValue(ijk, true); + } + } + } + } + +private: + BoolTreeType const * const mMaskTree; + BoolLeafNodeType const * const * const mMaskNodes; + + BoolTreeType mTmpBorderTree; + BoolTreeType * const mBorderTree; +}; // struct MaskBorderVoxels + + +template +struct SyncMaskValues +{ + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + SyncMaskValues(const std::vector& nodes, const BoolTreeType& mask) + : mNodes(nodes.empty() ? nullptr : &nodes.front()) + , mMaskTree(&mask) + { + } + + void operator()(const tbb::blocked_range& range) const + { + using ValueOnIter = typename BoolLeafNodeType::ValueOnIter; + + tree::ValueAccessor maskTreeAcc(*mMaskTree); + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + BoolLeafNodeType& node = *mNodes[n]; + + const BoolLeafNodeType * maskNode = maskTreeAcc.probeConstLeaf(node.origin()); + + if (maskNode) { + for (ValueOnIter it = node.beginValueOn(); it; ++it) { + const Index pos = it.pos(); + if (maskNode->getValue(pos)) { + node.setValueOnly(pos, true); + } + } + } + } + } + +private: + BoolLeafNodeType * const * const mNodes; + BoolTreeType const * const mMaskTree; +}; // struct SyncMaskValues + + +//////////////////////////////////////// + + +template +struct MaskSurface +{ + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + MaskSurface(const std::vector& nodes, const BoolTreeType& mask, + const math::Transform& inputTransform, const math::Transform& maskTransform, bool invert) + : mNodes(nodes.empty() ? nullptr : &nodes.front()) + , mMaskTree(&mask) + , mInputTransform(inputTransform) + , mMaskTransform(maskTransform) + , mInvertMask(invert) + { + } + + void operator()(const tbb::blocked_range& range) const + { + using ValueOnIter = typename BoolLeafNodeType::ValueOnIter; + + tree::ValueAccessor maskTreeAcc(*mMaskTree); + + const bool matchingTransforms = mInputTransform == mMaskTransform; + const bool maskState = mInvertMask; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + BoolLeafNodeType& node = *mNodes[n]; + + if (matchingTransforms) { + + const BoolLeafNodeType * maskNode = maskTreeAcc.probeConstLeaf(node.origin()); + + if (maskNode) { + + for (ValueOnIter it = node.beginValueOn(); it; ++it) { + const Index pos = it.pos(); + if (maskNode->isValueOn(pos) == maskState) { + node.setValueOnly(pos, true); + } + } + + } else { + + if (maskTreeAcc.isValueOn(node.origin()) == maskState) { + for (ValueOnIter it = node.beginValueOn(); it; ++it) { + node.setValueOnly(it.pos(), true); + } + } + + } + + } else { + + Coord ijk(0, 0, 0); + + for (ValueOnIter it = node.beginValueOn(); it; ++it) { + + ijk = mMaskTransform.worldToIndexCellCentered( + mInputTransform.indexToWorld(it.getCoord())); + + if (maskTreeAcc.isValueOn(ijk) == maskState) { + node.setValueOnly(it.pos(), true); + } + } + + } + } + } + +private: + BoolLeafNodeType * const * const mNodes; + BoolTreeType const * const mMaskTree; + math::Transform const mInputTransform; + math::Transform const mMaskTransform; + bool const mInvertMask; +}; // struct MaskSurface + + +template +inline void +applySurfaceMask( + typename InputGridType::TreeType::template ValueConverter::Type& intersectionTree, + typename InputGridType::TreeType::template ValueConverter::Type& borderTree, + const InputGridType& inputGrid, + const GridBase::ConstPtr& maskGrid, + bool invertMask, + typename InputGridType::ValueType isovalue) +{ + using InputTreeType = typename InputGridType::TreeType; + using BoolTreeType = typename InputTreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + using BoolGridType = Grid; + + if (maskGrid && maskGrid->type() == BoolGridType::gridType()) { + + const math::Transform& transform = inputGrid.transform(); + const InputTreeType& inputTree = inputGrid.tree(); + + const BoolGridType * surfaceMask = static_cast(maskGrid.get()); + + const BoolTreeType& maskTree = surfaceMask->tree(); + const math::Transform& maskTransform = surfaceMask->transform(); + + + // mark masked voxels + + std::vector intersectionLeafNodes; + intersectionTree.getNodes(intersectionLeafNodes); + + tbb::parallel_for(tbb::blocked_range(0, intersectionLeafNodes.size()), + MaskSurface( + intersectionLeafNodes, maskTree, transform, maskTransform, invertMask)); + + + // mask surface-mask border + + MaskBorderVoxels borderOp( + intersectionTree, intersectionLeafNodes, borderTree); + tbb::parallel_reduce(tbb::blocked_range(0, intersectionLeafNodes.size()), borderOp); + + + // recompute isosurface intersection mask + + BoolTreeType tmpIntersectionTree(false); + + MaskIntersectingVoxels op( + inputTree, intersectionLeafNodes, tmpIntersectionTree, isovalue); + + tbb::parallel_reduce(tbb::blocked_range(0, intersectionLeafNodes.size()), op); + + std::vector tmpIntersectionLeafNodes; + tmpIntersectionTree.getNodes(tmpIntersectionLeafNodes); + + tbb::parallel_for(tbb::blocked_range(0, tmpIntersectionLeafNodes.size()), + SyncMaskValues(tmpIntersectionLeafNodes, intersectionTree)); + + intersectionTree.clear(); + intersectionTree.merge(tmpIntersectionTree); + } +} + + +//////////////////////////////////////// + + +template +struct ComputeAuxiliaryData +{ + using InputLeafNodeType = typename InputTreeType::LeafNodeType; + using InputValueType = typename InputLeafNodeType::ValueType; + + using BoolLeafNodeType = tree::LeafNode; + + using Int16TreeType = typename InputTreeType::template ValueConverter::Type; + using Index32TreeType = typename InputTreeType::template ValueConverter::Type; + + + ComputeAuxiliaryData(const InputTreeType& inputTree, + const std::vector& intersectionLeafNodes, + Int16TreeType& signFlagsTree, + Index32TreeType& pointIndexTree, + InputValueType iso); + + ComputeAuxiliaryData(ComputeAuxiliaryData&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(const ComputeAuxiliaryData& rhs) { + mSignFlagsAccessor.tree().merge(rhs.mSignFlagsAccessor.tree()); + mPointIndexAccessor.tree().merge(rhs.mPointIndexAccessor.tree()); + } + +private: + tree::ValueAccessor mInputAccessor; + BoolLeafNodeType const * const * const mIntersectionNodes; + + Int16TreeType mSignFlagsTree; + tree::ValueAccessor mSignFlagsAccessor; + Index32TreeType mPointIndexTree; + tree::ValueAccessor mPointIndexAccessor; + + const InputValueType mIsovalue; +}; + + +template +ComputeAuxiliaryData::ComputeAuxiliaryData( + const InputTreeType& inputTree, + const std::vector& intersectionLeafNodes, + Int16TreeType& signFlagsTree, + Index32TreeType& pointIndexTree, + InputValueType iso) + : mInputAccessor(inputTree) + , mIntersectionNodes(&intersectionLeafNodes.front()) + , mSignFlagsTree(0) + , mSignFlagsAccessor(signFlagsTree) + , mPointIndexTree(std::numeric_limits::max()) + , mPointIndexAccessor(pointIndexTree) + , mIsovalue(iso) +{ + pointIndexTree.root().setBackground(std::numeric_limits::max(), false); +} + + +template +ComputeAuxiliaryData::ComputeAuxiliaryData(ComputeAuxiliaryData& rhs, tbb::split) + : mInputAccessor(rhs.mInputAccessor.tree()) + , mIntersectionNodes(rhs.mIntersectionNodes) + , mSignFlagsTree(0) + , mSignFlagsAccessor(mSignFlagsTree) + , mPointIndexTree(std::numeric_limits::max()) + , mPointIndexAccessor(mPointIndexTree) + , mIsovalue(rhs.mIsovalue) +{ +} + + +template +void +ComputeAuxiliaryData::operator()(const tbb::blocked_range& range) +{ + using Int16LeafNodeType = typename Int16TreeType::LeafNodeType; + + Coord ijk; + math::Tuple<8, InputValueType> cellVertexValues; + typename std::unique_ptr signsNodePt(new Int16LeafNodeType(ijk, 0)); + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + const BoolLeafNodeType& maskNode = *mIntersectionNodes[n]; + const Coord& origin = maskNode.origin(); + + const InputLeafNodeType *leafPt = mInputAccessor.probeConstLeaf(origin); + + if (!signsNodePt.get()) signsNodePt.reset(new Int16LeafNodeType(origin, 0)); + else signsNodePt->setOrigin(origin); + + bool updatedNode = false; + + for (typename BoolLeafNodeType::ValueOnCIter it = maskNode.cbeginValueOn(); it; ++it) { + + const Index pos = it.pos(); + ijk = BoolLeafNodeType::offsetToLocalCoord(pos); + + if (leafPt && + ijk[0] < int(BoolLeafNodeType::DIM - 1) && + ijk[1] < int(BoolLeafNodeType::DIM - 1) && + ijk[2] < int(BoolLeafNodeType::DIM - 1) ) { + getCellVertexValues(*leafPt, pos, cellVertexValues); + } else { + getCellVertexValues(mInputAccessor, origin + ijk, cellVertexValues); + } + + uint8_t signFlags = computeSignFlags(cellVertexValues, mIsovalue); + + if (signFlags != 0 && signFlags != 0xFF) { + + const bool inside = signFlags & 0x1; + + int edgeFlags = inside ? INSIDE : 0; + + if (!it.getValue()) { + edgeFlags |= inside != ((signFlags & 0x02) != 0) ? XEDGE : 0; + edgeFlags |= inside != ((signFlags & 0x10) != 0) ? YEDGE : 0; + edgeFlags |= inside != ((signFlags & 0x08) != 0) ? ZEDGE : 0; + } + + const uint8_t ambiguousCellFlags = sAmbiguousFace[signFlags]; + if (ambiguousCellFlags != 0) { + correctCellSigns(signFlags, ambiguousCellFlags, mInputAccessor, + origin + ijk, mIsovalue); + } + + edgeFlags |= int(signFlags); + + signsNodePt->setValueOn(pos, Int16(edgeFlags)); + updatedNode = true; + } + } + + if (updatedNode) { + typename Index32TreeType::LeafNodeType* idxNode = mPointIndexAccessor.touchLeaf(origin); + idxNode->topologyUnion(*signsNodePt); + + // zero fill + for (auto it = idxNode->beginValueOn(); it; ++it) { + idxNode->setValueOnly(it.pos(), 0); + } + + mSignFlagsAccessor.addLeaf(signsNodePt.release()); + } + } +} // ComputeAuxiliaryData::operator() + + +template +inline void +computeAuxiliaryData( + typename InputTreeType::template ValueConverter::Type& signFlagsTree, + typename InputTreeType::template ValueConverter::Type& pointIndexTree, + const typename InputTreeType::template ValueConverter::Type& intersectionTree, + const InputTreeType& inputTree, + typename InputTreeType::ValueType isovalue) +{ + using BoolTreeType = typename InputTreeType::template ValueConverter::Type; + using BoolLeafNodeType = typename BoolTreeType::LeafNodeType; + + std::vector intersectionLeafNodes; + intersectionTree.getNodes(intersectionLeafNodes); + + ComputeAuxiliaryData op( + inputTree, intersectionLeafNodes, signFlagsTree, pointIndexTree, isovalue); + + tbb::parallel_reduce(tbb::blocked_range(0, intersectionLeafNodes.size()), op); +} + + +//////////////////////////////////////// + + +template +struct LeafNodePointCount +{ + using Int16LeafNodeType = tree::LeafNode; + + LeafNodePointCount(const std::vector& leafNodes, + std::unique_ptr& leafNodeCount) + : mLeafNodes(leafNodes.empty() ? nullptr : &leafNodes.front()) + , mData(leafNodeCount.get()) + { + } + + void operator()(const tbb::blocked_range& range) const { + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + Index32 count = 0; + + Int16 const * p = mLeafNodes[n]->buffer().data(); + Int16 const * const endP = p + Int16LeafNodeType::SIZE; + + while (p < endP) { + count += Index32(sEdgeGroupTable[(SIGNS & *p)][0]); + ++p; + } + + mData[n] = count; + } + } + +private: + Int16LeafNodeType * const * const mLeafNodes; + Index32 *mData; +}; // struct LeafNodePointCount + + +template +struct AdaptiveLeafNodePointCount +{ + using Int16LeafNodeType = tree::LeafNode; + + AdaptiveLeafNodePointCount(const std::vector& pointIndexNodes, + const std::vector& signDataNodes, + std::unique_ptr& leafNodeCount) + : mPointIndexNodes(pointIndexNodes.empty() ? nullptr : &pointIndexNodes.front()) + , mSignDataNodes(signDataNodes.empty() ? nullptr : &signDataNodes.front()) + , mData(leafNodeCount.get()) + { + } + + void operator()(const tbb::blocked_range& range) const + { + using IndexType = typename PointIndexLeafNode::ValueType; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + const PointIndexLeafNode& node = *mPointIndexNodes[n]; + const Int16LeafNodeType& signNode = *mSignDataNodes[n]; + + size_t count = 0; + + std::set uniqueRegions; + + for (typename PointIndexLeafNode::ValueOnCIter it = node.cbeginValueOn(); it; ++it) { + + IndexType id = it.getValue(); + + if (id == 0) { + count += size_t(sEdgeGroupTable[(SIGNS & signNode.getValue(it.pos()))][0]); + } else if (id != IndexType(util::INVALID_IDX)) { + uniqueRegions.insert(id); + } + } + + mData[n] = Index32(count + uniqueRegions.size()); + } + } + +private: + PointIndexLeafNode const * const * const mPointIndexNodes; + Int16LeafNodeType const * const * const mSignDataNodes; + Index32 *mData; +}; // struct AdaptiveLeafNodePointCount + + +template +struct MapPoints +{ + using Int16LeafNodeType = tree::LeafNode; + + MapPoints(const std::vector& pointIndexNodes, + const std::vector& signDataNodes, + std::unique_ptr& leafNodeCount) + : mPointIndexNodes(pointIndexNodes.empty() ? nullptr : &pointIndexNodes.front()) + , mSignDataNodes(signDataNodes.empty() ? nullptr : &signDataNodes.front()) + , mData(leafNodeCount.get()) + { + } + + void operator()(const tbb::blocked_range& range) const { + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + const Int16LeafNodeType& signNode = *mSignDataNodes[n]; + PointIndexLeafNode& indexNode = *mPointIndexNodes[n]; + + Index32 pointOffset = mData[n]; + + for (auto it = indexNode.beginValueOn(); it; ++it) { + const Index pos = it.pos(); + indexNode.setValueOnly(pos, pointOffset); + const int signs = SIGNS & int(signNode.getValue(pos)); + pointOffset += Index32(sEdgeGroupTable[signs][0]); + } + } + } + +private: + PointIndexLeafNode * const * const mPointIndexNodes; + Int16LeafNodeType const * const * const mSignDataNodes; + Index32 * const mData; +}; // struct MapPoints + + + + +template +struct ComputePolygons +{ + using Int16TreeType = typename TreeType::template ValueConverter::Type; + using Int16LeafNodeType = typename Int16TreeType::LeafNodeType; + + using Index32TreeType = typename TreeType::template ValueConverter::Type; + using Index32LeafNodeType = typename Index32TreeType::LeafNodeType; + + + ComputePolygons( + const std::vector& signFlagsLeafNodes, + const Int16TreeType& signFlagsTree, + const Index32TreeType& idxTree, + PolygonPoolList& polygons, + bool invertSurfaceOrientation); + + void setRefSignTree(const Int16TreeType * r) { mRefSignFlagsTree = r; } + + void operator()(const tbb::blocked_range&) const; + +private: + Int16LeafNodeType * const * const mSignFlagsLeafNodes; + Int16TreeType const * const mSignFlagsTree; + Int16TreeType const * mRefSignFlagsTree; + Index32TreeType const * const mIndexTree; + PolygonPoolList * const mPolygonPoolList; + bool const mInvertSurfaceOrientation; +}; // struct ComputePolygons + + +template +ComputePolygons::ComputePolygons( + const std::vector& signFlagsLeafNodes, + const Int16TreeType& signFlagsTree, + const Index32TreeType& idxTree, + PolygonPoolList& polygons, + bool invertSurfaceOrientation) + : mSignFlagsLeafNodes(signFlagsLeafNodes.empty() ? nullptr : &signFlagsLeafNodes.front()) + , mSignFlagsTree(&signFlagsTree) + , mRefSignFlagsTree(nullptr) + , mIndexTree(&idxTree) + , mPolygonPoolList(&polygons) + , mInvertSurfaceOrientation(invertSurfaceOrientation) +{ +} + +template +void +ComputePolygons::operator()(const tbb::blocked_range& range) const +{ + using Int16ValueAccessor = tree::ValueAccessor; + Int16ValueAccessor signAcc(*mSignFlagsTree); + + tree::ValueAccessor idxAcc(*mIndexTree); + + const bool invertSurfaceOrientation = mInvertSurfaceOrientation; + + PrimBuilder mesher; + size_t edgeCount; + Coord ijk, origin; + + // reference data + std::unique_ptr refSignAcc; + if (mRefSignFlagsTree) refSignAcc.reset(new Int16ValueAccessor(*mRefSignFlagsTree)); + + for (size_t n = range.begin(); n != range.end(); ++n) { + + const Int16LeafNodeType& node = *mSignFlagsLeafNodes[n]; + origin = node.origin(); + + // Get an upper bound on the number of primitives. + edgeCount = 0; + typename Int16LeafNodeType::ValueOnCIter iter = node.cbeginValueOn(); + for (; iter; ++iter) { + if (iter.getValue() & XEDGE) ++edgeCount; + if (iter.getValue() & YEDGE) ++edgeCount; + if (iter.getValue() & ZEDGE) ++edgeCount; + } + + if(edgeCount == 0) continue; + + mesher.init(edgeCount, (*mPolygonPoolList)[n]); + + const Int16LeafNodeType *signleafPt = signAcc.probeConstLeaf(origin); + const Index32LeafNodeType *idxLeafPt = idxAcc.probeConstLeaf(origin); + + if (!signleafPt || !idxLeafPt) continue; + + + const Int16LeafNodeType *refSignLeafPt = nullptr; + if (refSignAcc) refSignLeafPt = refSignAcc->probeConstLeaf(origin); + + Vec3i offsets; + + for (iter = node.cbeginValueOn(); iter; ++iter) { + ijk = iter.getCoord(); + + Int16 flags = iter.getValue(); + + if (!(flags & 0xE00)) continue; + + Int16 refFlags = 0; + if (refSignLeafPt) { + refFlags = refSignLeafPt->getValue(iter.pos()); + } + + offsets[0] = 0; + offsets[1] = 0; + offsets[2] = 0; + + const uint8_t cell = uint8_t(SIGNS & flags); + + if (sEdgeGroupTable[cell][0] > 1) { + offsets[0] = (sEdgeGroupTable[cell][1] - 1); + offsets[1] = (sEdgeGroupTable[cell][9] - 1); + offsets[2] = (sEdgeGroupTable[cell][4] - 1); + } + + if (ijk[0] > origin[0] && ijk[1] > origin[1] && ijk[2] > origin[2]) { + constructPolygons(invertSurfaceOrientation, + flags, refFlags, offsets, ijk, *signleafPt, *idxLeafPt, mesher); + } else { + constructPolygons(invertSurfaceOrientation, + flags, refFlags, offsets, ijk, signAcc, idxAcc, mesher); + } + } + + mesher.done(); + } + +} // ComputePolygons::operator() + + +//////////////////////////////////////// + + +template +struct CopyArray +{ + CopyArray(T * outputArray, const T * inputArray, size_t outputOffset = 0) + : mOutputArray(outputArray), mInputArray(inputArray), mOutputOffset(outputOffset) + { + } + + void operator()(const tbb::blocked_range& inputArrayRange) const + { + const size_t offset = mOutputOffset; + for (size_t n = inputArrayRange.begin(), N = inputArrayRange.end(); n < N; ++n) { + mOutputArray[offset + n] = mInputArray[n]; + } + } + +private: + T * const mOutputArray; + T const * const mInputArray; + size_t const mOutputOffset; +}; // struct CopyArray + + +struct FlagAndCountQuadsToSubdivide +{ + FlagAndCountQuadsToSubdivide(PolygonPoolList& polygons, + const std::vector& pointFlags, + std::unique_ptr& points, + std::unique_ptr& numQuadsToDivide) + : mPolygonPoolList(&polygons) + , mPointFlags(pointFlags.empty() ? nullptr : &pointFlags.front()) + , mPoints(points.get()) + , mNumQuadsToDivide(numQuadsToDivide.get()) + { + } + + void operator()(const tbb::blocked_range& range) const + { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + PolygonPool& polygons = (*mPolygonPoolList)[n]; + + unsigned count = 0; + + // count and tag nonplanar seam line quads. + for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) { + + char& flags = polygons.quadFlags(i); + + if ((flags & POLYFLAG_FRACTURE_SEAM) && !(flags & POLYFLAG_EXTERIOR)) { + + Vec4I& quad = polygons.quad(i); + + const bool edgePoly = mPointFlags[quad[0]] || mPointFlags[quad[1]] + || mPointFlags[quad[2]] || mPointFlags[quad[3]]; + + if (!edgePoly) continue; + + const Vec3s& p0 = mPoints[quad[0]]; + const Vec3s& p1 = mPoints[quad[1]]; + const Vec3s& p2 = mPoints[quad[2]]; + const Vec3s& p3 = mPoints[quad[3]]; + + if (!isPlanarQuad(p0, p1, p2, p3, 1e-6f)) { + flags |= POLYFLAG_SUBDIVIDED; + count++; + } + } + } + + mNumQuadsToDivide[n] = count; + } + } + +private: + PolygonPoolList * const mPolygonPoolList; + uint8_t const * const mPointFlags; + Vec3s const * const mPoints; + unsigned * const mNumQuadsToDivide; +}; // struct FlagAndCountQuadsToSubdivide + + +struct SubdivideQuads +{ + SubdivideQuads(PolygonPoolList& polygons, + const std::unique_ptr& points, + size_t pointCount, + std::unique_ptr& centroids, + std::unique_ptr& numQuadsToDivide, + std::unique_ptr& centroidOffsets) + : mPolygonPoolList(&polygons) + , mPoints(points.get()) + , mCentroids(centroids.get()) + , mNumQuadsToDivide(numQuadsToDivide.get()) + , mCentroidOffsets(centroidOffsets.get()) + , mPointCount(pointCount) + { + } + + void operator()(const tbb::blocked_range& range) const + { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + PolygonPool& polygons = (*mPolygonPoolList)[n]; + + const size_t nonplanarCount = size_t(mNumQuadsToDivide[n]); + + if (nonplanarCount > 0) { + + PolygonPool tmpPolygons; + tmpPolygons.resetQuads(polygons.numQuads() - nonplanarCount); + tmpPolygons.resetTriangles(polygons.numTriangles() + size_t(4) * nonplanarCount); + + size_t offset = mCentroidOffsets[n]; + + size_t triangleIdx = 0; + + for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) { + + const char quadFlags = polygons.quadFlags(i); + if (!(quadFlags & POLYFLAG_SUBDIVIDED)) continue; + + unsigned newPointIdx = unsigned(offset + mPointCount); + + openvdb::Vec4I& quad = polygons.quad(i); + + mCentroids[offset] = (mPoints[quad[0]] + mPoints[quad[1]] + + mPoints[quad[2]] + mPoints[quad[3]]) * 0.25f; + + ++offset; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[0]; + triangle[1] = newPointIdx; + triangle[2] = quad[3]; + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + } + + ++triangleIdx; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[0]; + triangle[1] = quad[1]; + triangle[2] = newPointIdx; + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + } + + ++triangleIdx; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[1]; + triangle[1] = quad[2]; + triangle[2] = newPointIdx; + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + } + + + ++triangleIdx; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[2]; + triangle[1] = quad[3]; + triangle[2] = newPointIdx; + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + } + + ++triangleIdx; + + quad[0] = util::INVALID_IDX; // mark for deletion + } + + + for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) { + tmpPolygons.triangle(triangleIdx) = polygons.triangle(i); + tmpPolygons.triangleFlags(triangleIdx) = polygons.triangleFlags(i); + ++triangleIdx; + } + + size_t quadIdx = 0; + for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) { + openvdb::Vec4I& quad = polygons.quad(i); + + if (quad[0] != util::INVALID_IDX) { // ignore invalid quads + tmpPolygons.quad(quadIdx) = quad; + tmpPolygons.quadFlags(quadIdx) = polygons.quadFlags(i); + ++quadIdx; + } + } + + polygons.copy(tmpPolygons); + } + } + } + +private: + PolygonPoolList * const mPolygonPoolList; + Vec3s const * const mPoints; + Vec3s * const mCentroids; + unsigned * const mNumQuadsToDivide; + unsigned * const mCentroidOffsets; + size_t const mPointCount; +}; // struct SubdivideQuads + + +inline void +subdivideNonplanarSeamLineQuads( + PolygonPoolList& polygonPoolList, + size_t polygonPoolListSize, + PointList& pointList, + size_t& pointListSize, + std::vector& pointFlags) +{ + const tbb::blocked_range polygonPoolListRange(0, polygonPoolListSize); + + std::unique_ptr numQuadsToDivide(new unsigned[polygonPoolListSize]); + + tbb::parallel_for(polygonPoolListRange, + FlagAndCountQuadsToSubdivide(polygonPoolList, pointFlags, pointList, numQuadsToDivide)); + + std::unique_ptr centroidOffsets(new unsigned[polygonPoolListSize]); + + size_t centroidCount = 0; + + { + unsigned sum = 0; + for (size_t n = 0, N = polygonPoolListSize; n < N; ++n) { + centroidOffsets[n] = sum; + sum += numQuadsToDivide[n]; + } + centroidCount = size_t(sum); + } + + std::unique_ptr centroidList(new Vec3s[centroidCount]); + + tbb::parallel_for(polygonPoolListRange, + SubdivideQuads(polygonPoolList, pointList, pointListSize, + centroidList, numQuadsToDivide, centroidOffsets)); + + if (centroidCount > 0) { + + const size_t newPointListSize = centroidCount + pointListSize; + + std::unique_ptr newPointList(new openvdb::Vec3s[newPointListSize]); + + tbb::parallel_for(tbb::blocked_range(0, pointListSize), + CopyArray(newPointList.get(), pointList.get())); + + tbb::parallel_for(tbb::blocked_range(0, newPointListSize - pointListSize), + CopyArray(newPointList.get(), centroidList.get(), pointListSize)); + + pointListSize = newPointListSize; + pointList.swap(newPointList); + pointFlags.resize(pointListSize, 0); + } +} + + +struct ReviseSeamLineFlags +{ + ReviseSeamLineFlags(PolygonPoolList& polygons, + const std::vector& pointFlags) + : mPolygonPoolList(&polygons) + , mPointFlags(pointFlags.empty() ? nullptr : &pointFlags.front()) + { + } + + void operator()(const tbb::blocked_range& range) const + { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + PolygonPool& polygons = (*mPolygonPoolList)[n]; + + for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) { + + char& flags = polygons.quadFlags(i); + + if (flags & POLYFLAG_FRACTURE_SEAM) { + + openvdb::Vec4I& verts = polygons.quad(i); + + const bool hasSeamLinePoint = + mPointFlags[verts[0]] || mPointFlags[verts[1]] || + mPointFlags[verts[2]] || mPointFlags[verts[3]]; + + if (!hasSeamLinePoint) { + flags &= ~POLYFLAG_FRACTURE_SEAM; + } + } + } // end quad loop + + for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) { + + char& flags = polygons.triangleFlags(i); + + if (flags & POLYFLAG_FRACTURE_SEAM) { + + openvdb::Vec3I& verts = polygons.triangle(i); + + const bool hasSeamLinePoint = + mPointFlags[verts[0]] || mPointFlags[verts[1]] || mPointFlags[verts[2]]; + + if (!hasSeamLinePoint) { + flags &= ~POLYFLAG_FRACTURE_SEAM; + } + + } + } // end triangle loop + + } // end polygon pool loop + } + +private: + PolygonPoolList * const mPolygonPoolList; + uint8_t const * const mPointFlags; +}; // struct ReviseSeamLineFlags + + +inline void +reviseSeamLineFlags(PolygonPoolList& polygonPoolList, size_t polygonPoolListSize, + std::vector& pointFlags) +{ + tbb::parallel_for(tbb::blocked_range(0, polygonPoolListSize), + ReviseSeamLineFlags(polygonPoolList, pointFlags)); +} + + +//////////////////////////////////////// + + +template +struct MaskDisorientedTrianglePoints +{ + MaskDisorientedTrianglePoints(const InputTreeType& inputTree, const PolygonPoolList& polygons, + const PointList& pointList, std::unique_ptr& pointMask, + const math::Transform& transform, bool invertSurfaceOrientation) + : mInputTree(&inputTree) + , mPolygonPoolList(&polygons) + , mPointList(&pointList) + , mPointMask(pointMask.get()) + , mTransform(transform) + , mInvertSurfaceOrientation(invertSurfaceOrientation) + { + } + + void operator()(const tbb::blocked_range& range) const + { + using ValueType = typename InputTreeType::LeafNodeType::ValueType; + + tree::ValueAccessor inputAcc(*mInputTree); + Vec3s centroid, normal; + Coord ijk; + + const bool invertGradientDir = mInvertSurfaceOrientation || isBoolValue(); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const PolygonPool& polygons = (*mPolygonPoolList)[n]; + + for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) { + + const Vec3I& verts = polygons.triangle(i); + + const Vec3s& v0 = (*mPointList)[verts[0]]; + const Vec3s& v1 = (*mPointList)[verts[1]]; + const Vec3s& v2 = (*mPointList)[verts[2]]; + + normal = (v2 - v0).cross((v1 - v0)); + normal.normalize(); + + centroid = (v0 + v1 + v2) * (1.0f / 3.0f); + ijk = mTransform.worldToIndexCellCentered(centroid); + + Vec3s dir( math::ISGradient::result(inputAcc, ijk) ); + dir.normalize(); + + if (invertGradientDir) { + dir = -dir; + } + + // check if the angle is obtuse + if (dir.dot(normal) < -0.5f) { + // Concurrent writes to same memory address can occur, but + // all threads are writing the same value and char is atomic. + // (It is extremely rare that disoriented triangles share points, + // false sharing related performance impacts are not a concern.) + mPointMask[verts[0]] = 1; + mPointMask[verts[1]] = 1; + mPointMask[verts[2]] = 1; + } + + } // end triangle loop + + } // end polygon pool loop + } + +private: + InputTreeType const * const mInputTree; + PolygonPoolList const * const mPolygonPoolList; + PointList const * const mPointList; + uint8_t * const mPointMask; + math::Transform const mTransform; + bool const mInvertSurfaceOrientation; +}; // struct MaskDisorientedTrianglePoints + + +template +inline void +relaxDisorientedTriangles( + bool invertSurfaceOrientation, + const InputTree& inputTree, + const math::Transform& transform, + PolygonPoolList& polygonPoolList, + size_t polygonPoolListSize, + PointList& pointList, + const size_t pointListSize) +{ + const tbb::blocked_range polygonPoolListRange(0, polygonPoolListSize); + + std::unique_ptr pointMask(new uint8_t[pointListSize]); + fillArray(pointMask.get(), uint8_t(0), pointListSize); + + tbb::parallel_for(polygonPoolListRange, + MaskDisorientedTrianglePoints( + inputTree, polygonPoolList, pointList, pointMask, transform, invertSurfaceOrientation)); + + std::unique_ptr pointUpdates(new uint8_t[pointListSize]); + fillArray(pointUpdates.get(), uint8_t(0), pointListSize); + + std::unique_ptr newPoints(new Vec3s[pointListSize]); + fillArray(newPoints.get(), Vec3s(0.0f, 0.0f, 0.0f), pointListSize); + + for (size_t n = 0, N = polygonPoolListSize; n < N; ++n) { + + PolygonPool& polygons = polygonPoolList[n]; + + for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) { + openvdb::Vec4I& verts = polygons.quad(i); + + for (int v = 0; v < 4; ++v) { + + const unsigned pointIdx = verts[v]; + + if (pointMask[pointIdx] == 1) { + + newPoints[pointIdx] += + pointList[verts[0]] + pointList[verts[1]] + + pointList[verts[2]] + pointList[verts[3]]; + + pointUpdates[pointIdx] = uint8_t(pointUpdates[pointIdx] + 4); + } + } + } + + for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) { + openvdb::Vec3I& verts = polygons.triangle(i); + + for (int v = 0; v < 3; ++v) { + + const unsigned pointIdx = verts[v]; + + if (pointMask[pointIdx] == 1) { + newPoints[pointIdx] += + pointList[verts[0]] + pointList[verts[1]] + pointList[verts[2]]; + + pointUpdates[pointIdx] = uint8_t(pointUpdates[pointIdx] + 3); + } + } + } + } + + for (size_t n = 0, N = pointListSize; n < N; ++n) { + if (pointUpdates[n] > 0) { + const double weight = 1.0 / double(pointUpdates[n]); + pointList[n] = newPoints[n] * float(weight); + } + } +} + + +} // volume_to_mesh_internal namespace + + +//////////////////////////////////////// + + +inline +PolygonPool::PolygonPool() + : mNumQuads(0) + , mNumTriangles(0) + , mQuads(nullptr) + , mTriangles(nullptr) + , mQuadFlags(nullptr) + , mTriangleFlags(nullptr) +{ +} + + +inline +PolygonPool::PolygonPool(const size_t numQuads, const size_t numTriangles) + : mNumQuads(numQuads) + , mNumTriangles(numTriangles) + , mQuads(new openvdb::Vec4I[mNumQuads]) + , mTriangles(new openvdb::Vec3I[mNumTriangles]) + , mQuadFlags(new char[mNumQuads]) + , mTriangleFlags(new char[mNumTriangles]) +{ +} + + +inline void +PolygonPool::copy(const PolygonPool& rhs) +{ + resetQuads(rhs.numQuads()); + resetTriangles(rhs.numTriangles()); + + for (size_t i = 0; i < mNumQuads; ++i) { + mQuads[i] = rhs.mQuads[i]; + mQuadFlags[i] = rhs.mQuadFlags[i]; + } + + for (size_t i = 0; i < mNumTriangles; ++i) { + mTriangles[i] = rhs.mTriangles[i]; + mTriangleFlags[i] = rhs.mTriangleFlags[i]; + } +} + + +inline void +PolygonPool::resetQuads(size_t size) +{ + mNumQuads = size; + mQuads.reset(new openvdb::Vec4I[mNumQuads]); + mQuadFlags.reset(new char[mNumQuads]); +} + + +inline void +PolygonPool::clearQuads() +{ + mNumQuads = 0; + mQuads.reset(nullptr); + mQuadFlags.reset(nullptr); +} + + +inline void +PolygonPool::resetTriangles(size_t size) +{ + mNumTriangles = size; + mTriangles.reset(new openvdb::Vec3I[mNumTriangles]); + mTriangleFlags.reset(new char[mNumTriangles]); +} + + +inline void +PolygonPool::clearTriangles() +{ + mNumTriangles = 0; + mTriangles.reset(nullptr); + mTriangleFlags.reset(nullptr); +} + + +inline bool +PolygonPool::trimQuads(const size_t n, bool reallocate) +{ + if (!(n < mNumQuads)) return false; + + if (reallocate) { + + if (n == 0) { + mQuads.reset(nullptr); + } else { + + std::unique_ptr quads(new openvdb::Vec4I[n]); + std::unique_ptr flags(new char[n]); + + for (size_t i = 0; i < n; ++i) { + quads[i] = mQuads[i]; + flags[i] = mQuadFlags[i]; + } + + mQuads.swap(quads); + mQuadFlags.swap(flags); + } + } + + mNumQuads = n; + return true; +} + + +inline bool +PolygonPool::trimTrinagles(const size_t n, bool reallocate) +{ + if (!(n < mNumTriangles)) return false; + + if (reallocate) { + + if (n == 0) { + mTriangles.reset(nullptr); + } else { + + std::unique_ptr triangles(new openvdb::Vec3I[n]); + std::unique_ptr flags(new char[n]); + + for (size_t i = 0; i < n; ++i) { + triangles[i] = mTriangles[i]; + flags[i] = mTriangleFlags[i]; + } + + mTriangles.swap(triangles); + mTriangleFlags.swap(flags); + } + } + + mNumTriangles = n; + return true; +} + + +//////////////////////////////////////// + + +inline +VolumeToMesh::VolumeToMesh(double isovalue, double adaptivity, bool relaxDisorientedTriangles) + : mPoints(nullptr) + , mPolygons() + , mPointListSize(0) + , mSeamPointListSize(0) + , mPolygonPoolListSize(0) + , mIsovalue(isovalue) + , mPrimAdaptivity(adaptivity) + , mSecAdaptivity(0.0) + , mRefGrid(GridBase::ConstPtr()) + , mSurfaceMaskGrid(GridBase::ConstPtr()) + , mAdaptivityGrid(GridBase::ConstPtr()) + , mAdaptivityMaskTree(TreeBase::ConstPtr()) + , mRefSignTree(TreeBase::Ptr()) + , mRefIdxTree(TreeBase::Ptr()) + , mInvertSurfaceMask(false) + , mRelaxDisorientedTriangles(relaxDisorientedTriangles) + , mQuantizedSeamPoints(nullptr) + , mPointFlags(0) +{ +} + + +inline void +VolumeToMesh::setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity) +{ + mRefGrid = grid; + mSecAdaptivity = secAdaptivity; + + // Clear out old auxiliary data + mRefSignTree = TreeBase::Ptr(); + mRefIdxTree = TreeBase::Ptr(); + mSeamPointListSize = 0; + mQuantizedSeamPoints.reset(nullptr); +} + + +inline void +VolumeToMesh::setSurfaceMask(const GridBase::ConstPtr& mask, bool invertMask) +{ + mSurfaceMaskGrid = mask; + mInvertSurfaceMask = invertMask; +} + + +inline void +VolumeToMesh::setSpatialAdaptivity(const GridBase::ConstPtr& grid) +{ + mAdaptivityGrid = grid; +} + + +inline void +VolumeToMesh::setAdaptivityMask(const TreeBase::ConstPtr& tree) +{ + mAdaptivityMaskTree = tree; +} + + +template +inline void +VolumeToMesh::operator()(const InputGridType& inputGrid) +{ + // input data types + + using InputTreeType = typename InputGridType::TreeType; + using InputLeafNodeType = typename InputTreeType::LeafNodeType; + using InputValueType = typename InputLeafNodeType::ValueType; + + // auxiliary data types + + using FloatTreeType = typename InputTreeType::template ValueConverter::Type; + using FloatGridType = Grid; + using BoolTreeType = typename InputTreeType::template ValueConverter::Type; + using Int16TreeType = typename InputTreeType::template ValueConverter::Type; + using Int16LeafNodeType = typename Int16TreeType::LeafNodeType; + using Index32TreeType = typename InputTreeType::template ValueConverter::Type; + using Index32LeafNodeType = typename Index32TreeType::LeafNodeType; + + // clear old data + mPointListSize = 0; + mPoints.reset(); + mPolygonPoolListSize = 0; + mPolygons.reset(); + mPointFlags.clear(); + + // settings + + const math::Transform& transform = inputGrid.transform(); + const InputValueType isovalue = InputValueType(mIsovalue); + const float adaptivityThreshold = float(mPrimAdaptivity); + const bool adaptive = mPrimAdaptivity > 1e-7 || mSecAdaptivity > 1e-7; + + // The default surface orientation is setup for level set and bool/mask grids. + // Boolean grids are handled correctly by their value type. Signed distance fields, + // unsigned distance fields and fog volumes have the same value type but use different + // inside value classifications. + const bool invertSurfaceOrientation = (!volume_to_mesh_internal::isBoolValue() + && (inputGrid.getGridClass() != openvdb::GRID_LEVEL_SET)); + + // references, masks and auxiliary data + + const InputTreeType& inputTree = inputGrid.tree(); + + BoolTreeType intersectionTree(false), adaptivityMask(false); + + if (mAdaptivityMaskTree && mAdaptivityMaskTree->type() == BoolTreeType::treeType()) { + const BoolTreeType *refAdaptivityMask= + static_cast(mAdaptivityMaskTree.get()); + adaptivityMask.topologyUnion(*refAdaptivityMask); + } + + Int16TreeType signFlagsTree(0); + Index32TreeType pointIndexTree(std::numeric_limits::max()); + + + // collect auxiliary data + + volume_to_mesh_internal::identifySurfaceIntersectingVoxels( + intersectionTree, inputTree, isovalue); + + volume_to_mesh_internal::applySurfaceMask(intersectionTree, adaptivityMask, + inputGrid, mSurfaceMaskGrid, mInvertSurfaceMask, isovalue); + + if (intersectionTree.empty()) return; + + volume_to_mesh_internal::computeAuxiliaryData( + signFlagsTree, pointIndexTree, intersectionTree, inputTree, isovalue); + + intersectionTree.clear(); + + std::vector pointIndexLeafNodes; + pointIndexTree.getNodes(pointIndexLeafNodes); + + std::vector signFlagsLeafNodes; + signFlagsTree.getNodes(signFlagsLeafNodes); + + const tbb::blocked_range auxiliaryLeafNodeRange(0, signFlagsLeafNodes.size()); + + + // optionally collect auxiliary data from a reference volume. + + Int16TreeType* refSignFlagsTree = nullptr; + Index32TreeType* refPointIndexTree = nullptr; + InputTreeType const* refInputTree = nullptr; + + if (mRefGrid && mRefGrid->type() == InputGridType::gridType()) { + + const InputGridType* refGrid = static_cast(mRefGrid.get()); + refInputTree = &refGrid->tree(); + + if (!mRefSignTree && !mRefIdxTree) { + + // first time, collect and cache auxiliary data. + + typename Int16TreeType::Ptr refSignFlagsTreePt(new Int16TreeType(0)); + typename Index32TreeType::Ptr refPointIndexTreePt( + new Index32TreeType(std::numeric_limits::max())); + + BoolTreeType refIntersectionTree(false); + + volume_to_mesh_internal::identifySurfaceIntersectingVoxels( + refIntersectionTree, *refInputTree, isovalue); + + volume_to_mesh_internal::computeAuxiliaryData(*refSignFlagsTreePt, + *refPointIndexTreePt, refIntersectionTree, *refInputTree, isovalue); + + mRefSignTree = refSignFlagsTreePt; + mRefIdxTree = refPointIndexTreePt; + } + + if (mRefSignTree && mRefIdxTree) { + + // get cached auxiliary data + + refSignFlagsTree = static_cast(mRefSignTree.get()); + refPointIndexTree = static_cast(mRefIdxTree.get()); + } + + + if (refSignFlagsTree && refPointIndexTree) { + + // generate seam line sample points + + volume_to_mesh_internal::markSeamLineData(signFlagsTree, *refSignFlagsTree); + + if (mSeamPointListSize == 0) { + + // count unique points on reference surface + + std::vector refSignFlagsLeafNodes; + refSignFlagsTree->getNodes(refSignFlagsLeafNodes); + + std::unique_ptr leafNodeOffsets( + new Index32[refSignFlagsLeafNodes.size()]); + + tbb::parallel_for(tbb::blocked_range(0, refSignFlagsLeafNodes.size()), + volume_to_mesh_internal::LeafNodePointCount( + refSignFlagsLeafNodes, leafNodeOffsets)); + + { + Index32 count = 0; + for (size_t n = 0, N = refSignFlagsLeafNodes.size(); n < N; ++n) { + const Index32 tmp = leafNodeOffsets[n]; + leafNodeOffsets[n] = count; + count += tmp; + } + mSeamPointListSize = size_t(count); + } + + if (mSeamPointListSize != 0) { + + mQuantizedSeamPoints.reset(new uint32_t[mSeamPointListSize]); + + memset(mQuantizedSeamPoints.get(), 0, sizeof(uint32_t) * mSeamPointListSize); + + std::vector refPointIndexLeafNodes; + refPointIndexTree->getNodes(refPointIndexLeafNodes); + + tbb::parallel_for(tbb::blocked_range(0, refPointIndexLeafNodes.size()), + volume_to_mesh_internal::MapPoints( + refPointIndexLeafNodes, refSignFlagsLeafNodes, leafNodeOffsets)); + } + } + + if (mSeamPointListSize != 0) { + + tbb::parallel_for(auxiliaryLeafNodeRange, + volume_to_mesh_internal::SeamLineWeights( + signFlagsLeafNodes, inputTree, *refPointIndexTree, *refSignFlagsTree, + mQuantizedSeamPoints.get(), isovalue)); + } + } + } + + const bool referenceMeshing = refSignFlagsTree && refPointIndexTree && refInputTree; + + + // adapt and count unique points + + std::unique_ptr leafNodeOffsets(new Index32[signFlagsLeafNodes.size()]); + + if (adaptive) { + volume_to_mesh_internal::MergeVoxelRegions mergeOp( + inputGrid, pointIndexTree, pointIndexLeafNodes, signFlagsLeafNodes, + isovalue, adaptivityThreshold, invertSurfaceOrientation); + + if (mAdaptivityGrid && mAdaptivityGrid->type() == FloatGridType::gridType()) { + const FloatGridType* adaptivityGrid = + static_cast(mAdaptivityGrid.get()); + mergeOp.setSpatialAdaptivity(*adaptivityGrid); + } + + if (!adaptivityMask.empty()) { + mergeOp.setAdaptivityMask(adaptivityMask); + } + + if (referenceMeshing) { + mergeOp.setRefSignFlagsData(*refSignFlagsTree, float(mSecAdaptivity)); + } + + tbb::parallel_for(auxiliaryLeafNodeRange, mergeOp); + + volume_to_mesh_internal::AdaptiveLeafNodePointCount + op(pointIndexLeafNodes, signFlagsLeafNodes, leafNodeOffsets); + + tbb::parallel_for(auxiliaryLeafNodeRange, op); + + } else { + + volume_to_mesh_internal::LeafNodePointCount + op(signFlagsLeafNodes, leafNodeOffsets); + + tbb::parallel_for(auxiliaryLeafNodeRange, op); + } + + + { + Index32 pointCount = 0; + for (size_t n = 0, N = signFlagsLeafNodes.size(); n < N; ++n) { + const Index32 tmp = leafNodeOffsets[n]; + leafNodeOffsets[n] = pointCount; + pointCount += tmp; + } + + mPointListSize = size_t(pointCount); + mPoints.reset(new openvdb::Vec3s[mPointListSize]); + mPointFlags.clear(); + } + + + // compute points + + { + volume_to_mesh_internal::ComputePoints + op(mPoints.get(), inputTree, pointIndexLeafNodes, + signFlagsLeafNodes, leafNodeOffsets, transform, mIsovalue); + + if (referenceMeshing) { + mPointFlags.resize(mPointListSize); + op.setRefData(*refInputTree, *refPointIndexTree, *refSignFlagsTree, + mQuantizedSeamPoints.get(), &mPointFlags.front()); + } + + tbb::parallel_for(auxiliaryLeafNodeRange, op); + } + + + // compute polygons + + mPolygonPoolListSize = signFlagsLeafNodes.size(); + mPolygons.reset(new PolygonPool[mPolygonPoolListSize]); + + if (adaptive) { + + using PrimBuilder = volume_to_mesh_internal::AdaptivePrimBuilder; + + volume_to_mesh_internal::ComputePolygons + op(signFlagsLeafNodes, signFlagsTree, pointIndexTree, + mPolygons, invertSurfaceOrientation); + + if (referenceMeshing) { + op.setRefSignTree(refSignFlagsTree); + } + + tbb::parallel_for(auxiliaryLeafNodeRange, op); + + } else { + + using PrimBuilder = volume_to_mesh_internal::UniformPrimBuilder; + + volume_to_mesh_internal::ComputePolygons + op(signFlagsLeafNodes, signFlagsTree, pointIndexTree, + mPolygons, invertSurfaceOrientation); + + if (referenceMeshing) { + op.setRefSignTree(refSignFlagsTree); + } + + tbb::parallel_for(auxiliaryLeafNodeRange, op); + } + + + signFlagsTree.clear(); + pointIndexTree.clear(); + + + if (adaptive && mRelaxDisorientedTriangles) { + volume_to_mesh_internal::relaxDisorientedTriangles(invertSurfaceOrientation, + inputTree, transform, mPolygons, mPolygonPoolListSize, mPoints, mPointListSize); + } + + + if (referenceMeshing) { + volume_to_mesh_internal::subdivideNonplanarSeamLineQuads( + mPolygons, mPolygonPoolListSize, mPoints, mPointListSize, mPointFlags); + + volume_to_mesh_internal::reviseSeamLineFlags(mPolygons, mPolygonPoolListSize, mPointFlags); + } + +} + + +//////////////////////////////////////// + + +//{ +/// @cond OPENVDB_VOLUME_TO_MESH_INTERNAL + +/// @internal This overload is enabled only for grids with a scalar ValueType. +template +inline typename std::enable_if::value, void>::type +doVolumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& triangles, + std::vector& quads, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) +{ + VolumeToMesh mesher(isovalue, adaptivity, relaxDisorientedTriangles); + mesher(grid); + + // Preallocate the point list + points.clear(); + points.resize(mesher.pointListSize()); + + { // Copy points + volume_to_mesh_internal::PointListCopy ptnCpy(mesher.pointList(), points); + tbb::parallel_for(tbb::blocked_range(0, points.size()), ptnCpy); + mesher.pointList().reset(nullptr); + } + + PolygonPoolList& polygonPoolList = mesher.polygonPoolList(); + + { // Preallocate primitive lists + size_t numQuads = 0, numTriangles = 0; + for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) { + openvdb::tools::PolygonPool& polygons = polygonPoolList[n]; + numTriangles += polygons.numTriangles(); + numQuads += polygons.numQuads(); + } + + triangles.clear(); + triangles.resize(numTriangles); + quads.clear(); + quads.resize(numQuads); + } + + // Copy primitives + size_t qIdx = 0, tIdx = 0; + for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) { + openvdb::tools::PolygonPool& polygons = polygonPoolList[n]; + + for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) { + quads[qIdx++] = polygons.quad(i); + } + + for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) { + triangles[tIdx++] = polygons.triangle(i); + } + } +} + +/// @internal This overload is enabled only for grids that do not have a scalar ValueType. +template +inline typename std::enable_if::value, void>::type +doVolumeToMesh( + const GridType&, + std::vector&, + std::vector&, + std::vector&, + double, + double, + bool) +{ + OPENVDB_THROW(TypeError, "volume to mesh conversion is supported only for scalar grids"); +} + +/// @endcond +//} + + +template +inline void +volumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& triangles, + std::vector& quads, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) +{ + doVolumeToMesh(grid, points, triangles, quads, isovalue, adaptivity, relaxDisorientedTriangles); +} + + +template +inline void +volumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& quads, + double isovalue) +{ + std::vector triangles; + doVolumeToMesh(grid, points, triangles, quads, isovalue, 0.0, true); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED diff --git a/openvdb/tools/VolumeToSpheres.h b/openvdb/tools/VolumeToSpheres.h new file mode 100644 index 00000000..d7bce1ea --- /dev/null +++ b/openvdb/tools/VolumeToSpheres.h @@ -0,0 +1,1030 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file tools/VolumeToSpheres.h +/// +/// @brief Fill a closed level set or fog volume with adaptively-sized spheres. + +#ifndef OPENVDB_TOOLS_VOLUME_TO_SPHERES_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VOLUME_TO_SPHERES_HAS_BEEN_INCLUDED + +#include +#include +#include "Morphology.h" // for erodeVoxels() +#include "PointScatter.h" +#include "LevelSetRebuild.h" +#include "LevelSetUtil.h" +#include "VolumeToMesh.h" + +#include +#include +#include +#include +#include + +#include // for std::min(), std::max() +#include // for std::sqrt() +#include // for std::numeric_limits +#include +#include +#include // for std::pair +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Fill a closed level set or fog volume with adaptively-sized spheres. +/// +/// @param grid a scalar grid that defines the surface to be filled with spheres +/// @param spheres an output array of 4-tuples representing the fitted spheres
+/// The first three components of each tuple specify the sphere center, +/// and the fourth specifies the radius. +/// The spheres are ordered by radius, from largest to smallest. +/// @param sphereCount lower and upper bounds on the number of spheres to be generated
+/// The actual number will be somewhere within the bounds. +/// @param overlapping toggle to allow spheres to overlap/intersect +/// @param minRadius the smallest allowable sphere size, in voxel units
+/// @param maxRadius the largest allowable sphere size, in voxel units +/// @param isovalue the voxel value that determines the surface of the volume
+/// The default value of zero works for signed distance fields, +/// while fog volumes require a larger positive value +/// (0.5 is a good initial guess). +/// @param instanceCount the number of interior points to consider for the sphere placement
+/// Increasing this count increases the chances of finding optimal +/// sphere sizes. +/// @param interrupter pointer to an object adhering to the util::NullInterrupter interface +/// +/// @note The minimum sphere count takes precedence over the minimum radius. +template +inline void +fillWithSpheres( + const GridT& grid, + std::vector& spheres, + const Vec2i& sphereCount = Vec2i(1, 50), + bool overlapping = false, + float minRadius = 1.0, + float maxRadius = std::numeric_limits::max(), + float isovalue = 0.0, + int instanceCount = 10000, + InterrupterT* interrupter = nullptr); + + +/// @deprecated Use the @a sphereCount overload instead. +template +OPENVDB_DEPRECATED +inline void +fillWithSpheres( + const GridT& grid, + std::vector& spheres, + int maxSphereCount, + bool overlapping = false, + float minRadius = 1.0, + float maxRadius = std::numeric_limits::max(), + float isovalue = 0.0, + int instanceCount = 10000, + InterrupterT* interrupter = nullptr); + + +//////////////////////////////////////// + + +/// @brief Accelerated closest surface point queries for narrow band level sets +/// @details Supports queries that originate at arbitrary world-space locations, +/// is not confined to the narrow band region of the input volume geometry. +template +class ClosestSurfacePoint +{ +public: + using Ptr = std::unique_ptr; + using TreeT = typename GridT::TreeType; + using BoolTreeT = typename TreeT::template ValueConverter::Type; + using Index32TreeT = typename TreeT::template ValueConverter::Type; + using Int16TreeT = typename TreeT::template ValueConverter::Type; + + /// @brief Extract surface points and construct a spatial acceleration structure. + /// + /// @return a null pointer if the initialization fails for any reason, + /// otherwise a unique pointer to a newly-allocated ClosestSurfacePoint object. + /// + /// @param grid a scalar level set or fog volume + /// @param isovalue the voxel value that determines the surface of the volume + /// The default value of zero works for signed distance fields, + /// while fog volumes require a larger positive value + /// (0.5 is a good initial guess). + /// @param interrupter pointer to an object adhering to the util::NullInterrupter interface. + template + static inline Ptr create(const GridT& grid, float isovalue = 0.0, + InterrupterT* interrupter = nullptr); + + /// @brief Compute the distance from each input point to its closest surface point. + /// @param points input list of points in world space + /// @param distances output list of closest surface point distances + inline bool search(const std::vector& points, std::vector& distances); + + /// @brief Overwrite each input point with its closest surface point. + /// @param points input/output list of points in world space + /// @param distances output list of closest surface point distances + inline bool searchAndReplace(std::vector& points, std::vector& distances); + + /// @brief Tree accessor + const Index32TreeT& indexTree() const { return *mIdxTreePt; } + /// @brief Tree accessor + const Int16TreeT& signTree() const { return *mSignTreePt; } + +private: + using Index32LeafT = typename Index32TreeT::LeafNodeType; + using IndexRange = std::pair; + + std::vector mLeafBoundingSpheres, mNodeBoundingSpheres; + std::vector mLeafRanges; + std::vector mLeafNodes; + PointList mSurfacePointList; + size_t mPointListSize = 0, mMaxNodeLeafs = 0; + typename Index32TreeT::Ptr mIdxTreePt; + typename Int16TreeT::Ptr mSignTreePt; + + ClosestSurfacePoint() = default; + template + inline bool initialize(const GridT&, float isovalue, InterrupterT*); + inline bool search(std::vector&, std::vector&, bool transformPoints); +}; + + +//////////////////////////////////////// + + +// Internal utility methods + +namespace v2s_internal { + +struct PointAccessor +{ + PointAccessor(std::vector& points) + : mPoints(points) + { + } + + void add(const Vec3R &pos) + { + mPoints.push_back(pos); + } +private: + std::vector& mPoints; +}; + + +template +class LeafOp +{ +public: + + LeafOp(std::vector& leafBoundingSpheres, + const std::vector& leafNodes, + const math::Transform& transform, + const PointList& surfacePointList); + + void run(bool threaded = true); + + + void operator()(const tbb::blocked_range&) const; + +private: + std::vector& mLeafBoundingSpheres; + const std::vector& mLeafNodes; + const math::Transform& mTransform; + const PointList& mSurfacePointList; +}; + +template +LeafOp::LeafOp( + std::vector& leafBoundingSpheres, + const std::vector& leafNodes, + const math::Transform& transform, + const PointList& surfacePointList) + : mLeafBoundingSpheres(leafBoundingSpheres) + , mLeafNodes(leafNodes) + , mTransform(transform) + , mSurfacePointList(surfacePointList) +{ +} + +template +void +LeafOp::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(tbb::blocked_range(0, mLeafNodes.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mLeafNodes.size())); + } +} + +template +void +LeafOp::operator()(const tbb::blocked_range& range) const +{ + typename Index32LeafT::ValueOnCIter iter; + Vec3s avg; + + for (size_t n = range.begin(); n != range.end(); ++n) { + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + + int count = 0; + for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { + avg += mSurfacePointList[iter.getValue()]; + ++count; + } + if (count > 1) avg *= float(1.0 / double(count)); + + float maxDist = 0.0; + for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { + float tmpDist = (mSurfacePointList[iter.getValue()] - avg).lengthSqr(); + if (tmpDist > maxDist) maxDist = tmpDist; + } + + Vec4R& sphere = mLeafBoundingSpheres[n]; + sphere[0] = avg[0]; + sphere[1] = avg[1]; + sphere[2] = avg[2]; + sphere[3] = maxDist * 2.0; // padded radius + } +} + + +class NodeOp +{ +public: + using IndexRange = std::pair; + + NodeOp(std::vector& nodeBoundingSpheres, + const std::vector& leafRanges, + const std::vector& leafBoundingSpheres); + + inline void run(bool threaded = true); + + inline void operator()(const tbb::blocked_range&) const; + +private: + std::vector& mNodeBoundingSpheres; + const std::vector& mLeafRanges; + const std::vector& mLeafBoundingSpheres; +}; + +inline +NodeOp::NodeOp(std::vector& nodeBoundingSpheres, + const std::vector& leafRanges, + const std::vector& leafBoundingSpheres) + : mNodeBoundingSpheres(nodeBoundingSpheres) + , mLeafRanges(leafRanges) + , mLeafBoundingSpheres(leafBoundingSpheres) +{ +} + +inline void +NodeOp::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(tbb::blocked_range(0, mLeafRanges.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mLeafRanges.size())); + } +} + +inline void +NodeOp::operator()(const tbb::blocked_range& range) const +{ + Vec3d avg, pos; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + + int count = int(mLeafRanges[n].second) - int(mLeafRanges[n].first); + + for (size_t i = mLeafRanges[n].first; i < mLeafRanges[n].second; ++i) { + avg[0] += mLeafBoundingSpheres[i][0]; + avg[1] += mLeafBoundingSpheres[i][1]; + avg[2] += mLeafBoundingSpheres[i][2]; + } + + if (count > 1) avg *= float(1.0 / double(count)); + + + double maxDist = 0.0; + + for (size_t i = mLeafRanges[n].first; i < mLeafRanges[n].second; ++i) { + pos[0] = mLeafBoundingSpheres[i][0]; + pos[1] = mLeafBoundingSpheres[i][1]; + pos[2] = mLeafBoundingSpheres[i][2]; + const auto radiusSqr = mLeafBoundingSpheres[i][3]; + + double tmpDist = (pos - avg).lengthSqr() + radiusSqr; + if (tmpDist > maxDist) maxDist = tmpDist; + } + + Vec4R& sphere = mNodeBoundingSpheres[n]; + + sphere[0] = avg[0]; + sphere[1] = avg[1]; + sphere[2] = avg[2]; + sphere[3] = maxDist * 2.0; // padded radius + } +} + + +//////////////////////////////////////// + + +template +class ClosestPointDist +{ +public: + using IndexRange = std::pair; + + ClosestPointDist( + std::vector& instancePoints, + std::vector& instanceDistances, + const PointList& surfacePointList, + const std::vector& leafNodes, + const std::vector& leafRanges, + const std::vector& leafBoundingSpheres, + const std::vector& nodeBoundingSpheres, + size_t maxNodeLeafs, + bool transformPoints = false); + + + void run(bool threaded = true); + + + void operator()(const tbb::blocked_range&) const; + +private: + + void evalLeaf(size_t index, const Index32LeafT& leaf) const; + void evalNode(size_t pointIndex, size_t nodeIndex) const; + + + std::vector& mInstancePoints; + std::vector& mInstanceDistances; + + const PointList& mSurfacePointList; + + const std::vector& mLeafNodes; + const std::vector& mLeafRanges; + const std::vector& mLeafBoundingSpheres; + const std::vector& mNodeBoundingSpheres; + + std::vector mLeafDistances, mNodeDistances; + + const bool mTransformPoints; + size_t mClosestPointIndex; +};// ClosestPointDist + + +template +ClosestPointDist::ClosestPointDist( + std::vector& instancePoints, + std::vector& instanceDistances, + const PointList& surfacePointList, + const std::vector& leafNodes, + const std::vector& leafRanges, + const std::vector& leafBoundingSpheres, + const std::vector& nodeBoundingSpheres, + size_t maxNodeLeafs, + bool transformPoints) + : mInstancePoints(instancePoints) + , mInstanceDistances(instanceDistances) + , mSurfacePointList(surfacePointList) + , mLeafNodes(leafNodes) + , mLeafRanges(leafRanges) + , mLeafBoundingSpheres(leafBoundingSpheres) + , mNodeBoundingSpheres(nodeBoundingSpheres) + , mLeafDistances(maxNodeLeafs, 0.0) + , mNodeDistances(leafRanges.size(), 0.0) + , mTransformPoints(transformPoints) + , mClosestPointIndex(0) +{ +} + + +template +void +ClosestPointDist::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(tbb::blocked_range(0, mInstancePoints.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mInstancePoints.size())); + } +} + +template +void +ClosestPointDist::evalLeaf(size_t index, const Index32LeafT& leaf) const +{ + typename Index32LeafT::ValueOnCIter iter; + const Vec3s center = mInstancePoints[index]; + size_t& closestPointIndex = const_cast(mClosestPointIndex); + + for (iter = leaf.cbeginValueOn(); iter; ++iter) { + + const Vec3s& point = mSurfacePointList[iter.getValue()]; + float tmpDist = (point - center).lengthSqr(); + + if (tmpDist < mInstanceDistances[index]) { + mInstanceDistances[index] = tmpDist; + closestPointIndex = iter.getValue(); + } + } +} + + +template +void +ClosestPointDist::evalNode(size_t pointIndex, size_t nodeIndex) const +{ + if (nodeIndex >= mLeafRanges.size()) return; + + const Vec3R& pos = mInstancePoints[pointIndex]; + float minDist = mInstanceDistances[pointIndex]; + size_t minDistIdx = 0; + Vec3R center; + bool updatedDist = false; + + for (size_t i = mLeafRanges[nodeIndex].first, n = 0; + i < mLeafRanges[nodeIndex].second; ++i, ++n) + { + float& distToLeaf = const_cast(mLeafDistances[n]); + + center[0] = mLeafBoundingSpheres[i][0]; + center[1] = mLeafBoundingSpheres[i][1]; + center[2] = mLeafBoundingSpheres[i][2]; + const auto radiusSqr = mLeafBoundingSpheres[i][3]; + + distToLeaf = float(std::max(0.0, (pos - center).lengthSqr() - radiusSqr)); + + if (distToLeaf < minDist) { + minDist = distToLeaf; + minDistIdx = i; + updatedDist = true; + } + } + + if (!updatedDist) return; + + evalLeaf(pointIndex, *mLeafNodes[minDistIdx]); + + for (size_t i = mLeafRanges[nodeIndex].first, n = 0; + i < mLeafRanges[nodeIndex].second; ++i, ++n) + { + if (mLeafDistances[n] < mInstanceDistances[pointIndex] && i != minDistIdx) { + evalLeaf(pointIndex, *mLeafNodes[i]); + } + } +} + + +template +void +ClosestPointDist::operator()(const tbb::blocked_range& range) const +{ + Vec3R center; + for (size_t n = range.begin(); n != range.end(); ++n) { + + const Vec3R& pos = mInstancePoints[n]; + float minDist = mInstanceDistances[n]; + size_t minDistIdx = 0; + + for (size_t i = 0, I = mNodeDistances.size(); i < I; ++i) { + float& distToNode = const_cast(mNodeDistances[i]); + + center[0] = mNodeBoundingSpheres[i][0]; + center[1] = mNodeBoundingSpheres[i][1]; + center[2] = mNodeBoundingSpheres[i][2]; + const auto radiusSqr = mNodeBoundingSpheres[i][3]; + + distToNode = float(std::max(0.0, (pos - center).lengthSqr() - radiusSqr)); + + if (distToNode < minDist) { + minDist = distToNode; + minDistIdx = i; + } + } + + evalNode(n, minDistIdx); + + for (size_t i = 0, I = mNodeDistances.size(); i < I; ++i) { + if (mNodeDistances[i] < mInstanceDistances[n] && i != minDistIdx) { + evalNode(n, i); + } + } + + mInstanceDistances[n] = std::sqrt(mInstanceDistances[n]); + + if (mTransformPoints) mInstancePoints[n] = mSurfacePointList[mClosestPointIndex]; + } +} + + +class UpdatePoints +{ +public: + UpdatePoints( + const Vec4s& sphere, + const std::vector& points, + std::vector& distances, + std::vector& mask, + bool overlapping); + + float radius() const { return mRadius; } + int index() const { return mIndex; } + + inline void run(bool threaded = true); + + + UpdatePoints(UpdatePoints&, tbb::split); + inline void operator()(const tbb::blocked_range& range); + void join(const UpdatePoints& rhs) + { + if (rhs.mRadius > mRadius) { + mRadius = rhs.mRadius; + mIndex = rhs.mIndex; + } + } + +private: + const Vec4s& mSphere; + const std::vector& mPoints; + std::vector& mDistances; + std::vector& mMask; + bool mOverlapping; + float mRadius; + int mIndex; +}; + +inline +UpdatePoints::UpdatePoints( + const Vec4s& sphere, + const std::vector& points, + std::vector& distances, + std::vector& mask, + bool overlapping) + : mSphere(sphere) + , mPoints(points) + , mDistances(distances) + , mMask(mask) + , mOverlapping(overlapping) + , mRadius(0.0) + , mIndex(0) +{ +} + +inline +UpdatePoints::UpdatePoints(UpdatePoints& rhs, tbb::split) + : mSphere(rhs.mSphere) + , mPoints(rhs.mPoints) + , mDistances(rhs.mDistances) + , mMask(rhs.mMask) + , mOverlapping(rhs.mOverlapping) + , mRadius(rhs.mRadius) + , mIndex(rhs.mIndex) +{ +} + +inline void +UpdatePoints::run(bool threaded) +{ + if (threaded) { + tbb::parallel_reduce(tbb::blocked_range(0, mPoints.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mPoints.size())); + } +} + +inline void +UpdatePoints::operator()(const tbb::blocked_range& range) +{ + Vec3s pos; + for (size_t n = range.begin(); n != range.end(); ++n) { + if (mMask[n]) continue; + + pos.x() = float(mPoints[n].x()) - mSphere[0]; + pos.y() = float(mPoints[n].y()) - mSphere[1]; + pos.z() = float(mPoints[n].z()) - mSphere[2]; + + float dist = pos.length(); + + if (dist < mSphere[3]) { + mMask[n] = 1; + continue; + } + + if (!mOverlapping) { + mDistances[n] = std::min(mDistances[n], (dist - mSphere[3])); + } + + if (mDistances[n] > mRadius) { + mRadius = mDistances[n]; + mIndex = int(n); + } + } +} + + +} // namespace v2s_internal + + +//////////////////////////////////////// + + +template +inline void +fillWithSpheres( + const GridT& grid, + std::vector& spheres, + int maxSphereCount, + bool overlapping, + float minRadius, + float maxRadius, + float isovalue, + int instanceCount, + InterrupterT* interrupter) +{ + fillWithSpheres(grid, spheres, Vec2i(1, maxSphereCount), overlapping, + minRadius, maxRadius, isovalue, instanceCount, interrupter); +} + + +template +inline void +fillWithSpheres( + const GridT& grid, + std::vector& spheres, + const Vec2i& sphereCount, + bool overlapping, + float minRadius, + float maxRadius, + float isovalue, + int instanceCount, + InterrupterT* interrupter) +{ + spheres.clear(); + + if (grid.empty()) return; + + const int + minSphereCount = sphereCount[0], + maxSphereCount = sphereCount[1]; + if ((minSphereCount > maxSphereCount) || (maxSphereCount < 1)) { + OPENVDB_LOG_WARN("fillWithSpheres: minimum sphere count (" + << minSphereCount << ") exceeds maximum count (" << maxSphereCount << ")"); + return; + } + spheres.reserve(maxSphereCount); + + auto gridPtr = grid.copy(); // shallow copy + + if (gridPtr->getGridClass() == GRID_LEVEL_SET) { + // Clamp the isovalue to the level set's background value minus epsilon. + // (In a valid narrow-band level set, all voxels, including background voxels, + // have values less than or equal to the background value, so an isovalue + // greater than or equal to the background value would produce a mask with + // effectively infinite extent.) + isovalue = std::min(isovalue, + static_cast(gridPtr->background() - math::Tolerance::value())); + } else if (gridPtr->getGridClass() == GRID_FOG_VOLUME) { + // Clamp the isovalue of a fog volume between epsilon and one, + // again to avoid a mask with infinite extent. (Recall that + // fog volume voxel values vary from zero outside to one inside.) + isovalue = math::Clamp(isovalue, math::Tolerance::value(), 1.f); + } + + // ClosestSurfacePoint is inaccurate for small grids. + // Resample the input grid if it is too small. + auto numVoxels = gridPtr->activeVoxelCount(); + if (numVoxels < 10000) { + const auto scale = 1.0 / math::Cbrt(2.0 * 10000.0 / double(numVoxels)); + auto scaledXform = gridPtr->transform().copy(); + scaledXform->preScale(scale); + + auto newGridPtr = levelSetRebuild(*gridPtr, isovalue, + LEVEL_SET_HALF_WIDTH, LEVEL_SET_HALF_WIDTH, scaledXform.get(), interrupter); + + const auto newNumVoxels = newGridPtr->activeVoxelCount(); + if (newNumVoxels > numVoxels) { + OPENVDB_LOG_DEBUG_RUNTIME("fillWithSpheres: resampled input grid from " + << numVoxels << " voxel" << (numVoxels == 1 ? "" : "s") + << " to " << newNumVoxels << " voxel" << (newNumVoxels == 1 ? "" : "s")); + gridPtr = newGridPtr; + numVoxels = newNumVoxels; + } + } + + const bool addNarrowBandPoints = (numVoxels < 10000); + int instances = std::max(instanceCount, maxSphereCount); + + using TreeT = typename GridT::TreeType; + using BoolTreeT = typename TreeT::template ValueConverter::Type; + using Int16TreeT = typename TreeT::template ValueConverter::Type; + + using RandGen = std::mersenne_twister_engine; // mt11213b + RandGen mtRand(/*seed=*/0); + + const TreeT& tree = gridPtr->tree(); + math::Transform transform = gridPtr->transform(); + + std::vector instancePoints; + { + // Compute a mask of the voxels enclosed by the isosurface. + typename Grid::Ptr interiorMaskPtr; + if (gridPtr->getGridClass() == GRID_LEVEL_SET) { + interiorMaskPtr = sdfInteriorMask(*gridPtr, isovalue); + } else { + // For non-level-set grids, the interior mask comprises the active voxels. + interiorMaskPtr = typename Grid::Ptr(Grid::create(false)); + interiorMaskPtr->setTransform(transform.copy()); + interiorMaskPtr->tree().topologyUnion(tree); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + // If the interior mask is small and eroding it results in an empty grid, + // use the uneroded mask instead. (But if the minimum sphere count is zero, + // then eroding away the mask is acceptable.) + if (!addNarrowBandPoints || (minSphereCount <= 0)) { + erodeVoxels(interiorMaskPtr->tree(), 1); + } else { + auto& maskTree = interiorMaskPtr->tree(); + auto copyOfTree = StaticPtrCast(maskTree.copy()); + erodeVoxels(maskTree, 1); + if (maskTree.empty()) { interiorMaskPtr->setTree(copyOfTree); } + } + + // Scatter candidate sphere centroids (instancePoints) + instancePoints.reserve(instances); + v2s_internal::PointAccessor ptnAcc(instancePoints); + + const auto scatterCount = Index64(addNarrowBandPoints ? (instances / 2) : instances); + + UniformPointScatter scatter( + ptnAcc, scatterCount, mtRand, 1.0, interrupter); + scatter(*interiorMaskPtr); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + auto csp = ClosestSurfacePoint::create(*gridPtr, isovalue, interrupter); + if (!csp) return; + + // Add extra instance points in the interior narrow band. + if (instancePoints.size() < size_t(instances)) { + const Int16TreeT& signTree = csp->signTree(); + for (auto leafIt = signTree.cbeginLeaf(); leafIt; ++leafIt) { + for (auto it = leafIt->cbeginValueOn(); it; ++it) { + const int flags = int(it.getValue()); + if (!(volume_to_mesh_internal::EDGES & flags) + && (volume_to_mesh_internal::INSIDE & flags)) + { + instancePoints.push_back(transform.indexToWorld(it.getCoord())); + } + if (instancePoints.size() == size_t(instances)) break; + } + if (instancePoints.size() == size_t(instances)) break; + } + } + + if (interrupter && interrupter->wasInterrupted()) return; + + // Assign a radius to each candidate sphere. The radius is the world-space + // distance from the sphere's center to the closest surface point. + std::vector instanceRadius; + if (!csp->search(instancePoints, instanceRadius)) return; + + float largestRadius = 0.0; + int largestRadiusIdx = 0; + for (size_t n = 0, N = instancePoints.size(); n < N; ++n) { + if (instanceRadius[n] > largestRadius) { + largestRadius = instanceRadius[n]; + largestRadiusIdx = int(n); + } + } + + std::vector instanceMask(instancePoints.size(), 0); + + minRadius = float(minRadius * transform.voxelSize()[0]); + maxRadius = float(maxRadius * transform.voxelSize()[0]); + + for (size_t s = 0, S = std::min(size_t(maxSphereCount), instancePoints.size()); s < S; ++s) { + + if (interrupter && interrupter->wasInterrupted()) return; + + largestRadius = std::min(maxRadius, largestRadius); + + if ((int(s) >= minSphereCount) && (largestRadius < minRadius)) break; + + const Vec4s sphere( + float(instancePoints[largestRadiusIdx].x()), + float(instancePoints[largestRadiusIdx].y()), + float(instancePoints[largestRadiusIdx].z()), + largestRadius); + + spheres.push_back(sphere); + instanceMask[largestRadiusIdx] = 1; + + v2s_internal::UpdatePoints op( + sphere, instancePoints, instanceRadius, instanceMask, overlapping); + op.run(); + + largestRadius = op.radius(); + largestRadiusIdx = op.index(); + } +} // fillWithSpheres + + +//////////////////////////////////////// + + +template +template +inline typename ClosestSurfacePoint::Ptr +ClosestSurfacePoint::create(const GridT& grid, float isovalue, InterrupterT* interrupter) +{ + auto csp = Ptr{new ClosestSurfacePoint}; + if (!csp->initialize(grid, isovalue, interrupter)) csp.reset(); + return csp; +} + + +template +template +inline bool +ClosestSurfacePoint::initialize( + const GridT& grid, float isovalue, InterrupterT* interrupter) +{ + using Index32LeafManagerT = tree::LeafManager; + using ValueT = typename GridT::ValueType; + + const TreeT& tree = grid.tree(); + const math::Transform& transform = grid.transform(); + + { // Extract surface point cloud + + BoolTreeT mask(false); + volume_to_mesh_internal::identifySurfaceIntersectingVoxels(mask, tree, ValueT(isovalue)); + + mSignTreePt.reset(new Int16TreeT(0)); + mIdxTreePt.reset(new Index32TreeT(std::numeric_limits::max())); + + + volume_to_mesh_internal::computeAuxiliaryData( + *mSignTreePt, *mIdxTreePt, mask, tree, ValueT(isovalue)); + + if (interrupter && interrupter->wasInterrupted()) return false; + + // count unique points + + using Int16LeafNodeType = typename Int16TreeT::LeafNodeType; + using Index32LeafNodeType = typename Index32TreeT::LeafNodeType; + + std::vector signFlagsLeafNodes; + mSignTreePt->getNodes(signFlagsLeafNodes); + + const tbb::blocked_range auxiliaryLeafNodeRange(0, signFlagsLeafNodes.size()); + + std::unique_ptr leafNodeOffsets(new Index32[signFlagsLeafNodes.size()]); + + tbb::parallel_for(auxiliaryLeafNodeRange, + volume_to_mesh_internal::LeafNodePointCount + (signFlagsLeafNodes, leafNodeOffsets)); + + { + Index32 pointCount = 0; + for (size_t n = 0, N = signFlagsLeafNodes.size(); n < N; ++n) { + const Index32 tmp = leafNodeOffsets[n]; + leafNodeOffsets[n] = pointCount; + pointCount += tmp; + } + + mPointListSize = size_t(pointCount); + mSurfacePointList.reset(new Vec3s[mPointListSize]); + } + + + std::vector pointIndexLeafNodes; + mIdxTreePt->getNodes(pointIndexLeafNodes); + + tbb::parallel_for(auxiliaryLeafNodeRange, volume_to_mesh_internal::ComputePoints( + mSurfacePointList.get(), tree, pointIndexLeafNodes, + signFlagsLeafNodes, leafNodeOffsets, transform, ValueT(isovalue))); + } + + if (interrupter && interrupter->wasInterrupted()) return false; + + Index32LeafManagerT idxLeafs(*mIdxTreePt); + + using Index32RootNodeT = typename Index32TreeT::RootNodeType; + using Index32NodeChainT = typename Index32RootNodeT::NodeChainType; + static_assert(boost::mpl::size::value > 1, + "expected tree depth greater than one"); + using Index32InternalNodeT = + typename boost::mpl::at >::type; + + typename Index32TreeT::NodeCIter nIt = mIdxTreePt->cbeginNode(); + nIt.setMinDepth(Index32TreeT::NodeCIter::LEAF_DEPTH - 1); + nIt.setMaxDepth(Index32TreeT::NodeCIter::LEAF_DEPTH - 1); + + std::vector internalNodes; + + const Index32InternalNodeT* node = nullptr; + for (; nIt; ++nIt) { + nIt.getNode(node); + if (node) internalNodes.push_back(node); + } + + std::vector().swap(mLeafRanges); + mLeafRanges.resize(internalNodes.size()); + + std::vector().swap(mLeafNodes); + mLeafNodes.reserve(idxLeafs.leafCount()); + + typename Index32InternalNodeT::ChildOnCIter leafIt; + mMaxNodeLeafs = 0; + for (size_t n = 0, N = internalNodes.size(); n < N; ++n) { + + mLeafRanges[n].first = mLeafNodes.size(); + + size_t leafCount = 0; + for (leafIt = internalNodes[n]->cbeginChildOn(); leafIt; ++leafIt) { + mLeafNodes.push_back(&(*leafIt)); + ++leafCount; + } + + mMaxNodeLeafs = std::max(leafCount, mMaxNodeLeafs); + + mLeafRanges[n].second = mLeafNodes.size(); + } + + std::vector().swap(mLeafBoundingSpheres); + mLeafBoundingSpheres.resize(mLeafNodes.size()); + + v2s_internal::LeafOp leafBS( + mLeafBoundingSpheres, mLeafNodes, transform, mSurfacePointList); + leafBS.run(); + + + std::vector().swap(mNodeBoundingSpheres); + mNodeBoundingSpheres.resize(internalNodes.size()); + + v2s_internal::NodeOp nodeBS(mNodeBoundingSpheres, mLeafRanges, mLeafBoundingSpheres); + nodeBS.run(); + return true; +} // ClosestSurfacePoint::initialize + + +template +inline bool +ClosestSurfacePoint::search(std::vector& points, + std::vector& distances, bool transformPoints) +{ + distances.clear(); + distances.resize(points.size(), std::numeric_limits::infinity()); + + v2s_internal::ClosestPointDist cpd(points, distances, mSurfacePointList, + mLeafNodes, mLeafRanges, mLeafBoundingSpheres, mNodeBoundingSpheres, + mMaxNodeLeafs, transformPoints); + + cpd.run(); + + return true; +} + + +template +inline bool +ClosestSurfacePoint::search(const std::vector& points, std::vector& distances) +{ + return search(const_cast& >(points), distances, false); +} + + +template +inline bool +ClosestSurfacePoint::searchAndReplace(std::vector& points, + std::vector& distances) +{ + return search(points, distances, true); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/InternalNode.h b/openvdb/tree/InternalNode.h new file mode 100644 index 00000000..75147cb2 --- /dev/null +++ b/openvdb/tree/InternalNode.h @@ -0,0 +1,3295 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file InternalNode.h +/// +/// @brief Internal table nodes for OpenVDB trees + +#ifndef OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED + +#include +#include +#include // for io::readCompressedValues(), etc. +#include // for math::isExactlyEqual(), etc. +#include +#include +#include "Iterator.h" +#include "NodeUnion.h" +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +template struct SameInternalConfig; // forward declaration + + +template +class InternalNode +{ +public: + using ChildNodeType = _ChildNodeType; + using LeafNodeType = typename ChildNodeType::LeafNodeType; + using ValueType = typename ChildNodeType::ValueType; + using BuildType = typename ChildNodeType::BuildType; + using UnionType = NodeUnion; + using NodeMaskType = util::NodeMask; + + static const Index + LOG2DIM = Log2Dim, // log2 of tile count in one dimension + TOTAL = Log2Dim + ChildNodeType::TOTAL, // log2 of voxel count in one dimension + DIM = 1 << TOTAL, // total voxel count in one dimension + NUM_VALUES = 1 << (3 * Log2Dim), // total voxel count represented by this node + LEVEL = 1 + ChildNodeType::LEVEL; // level 0 = leaf + static const Index64 + NUM_VOXELS = uint64_t(1) << (3 * TOTAL); // total voxel count represented by this node + + /// @brief ValueConverter::Type is the type of an InternalNode having the same + /// child hierarchy and dimensions as this node but a different value type, T. + template + struct ValueConverter { + using Type = InternalNode::Type, Log2Dim>; + }; + + /// @brief SameConfiguration::value is @c true if and only if OtherNodeType + /// is the type of an InternalNode with the same dimensions as this node and whose + /// ChildNodeType has the same configuration as this node's ChildNodeType. + template + struct SameConfiguration { + static const bool value = + SameInternalConfig::value; + }; + + + /// @brief Default constructor + /// @warning The resulting InternalNode is uninitialized + InternalNode() {} + + /// @brief Constructor of an InternalNode with dense inactive tiles of the specified value. + /// @param offValue Background value used for inactive values + explicit InternalNode(const ValueType& offValue); + + /// @brief Constructs an InternalNode with dense tiles + /// @param origin The location in index space of the fist tile value + /// @param fillValue Value assigned to all the tiles + /// @param active State assigned to all the tiles + InternalNode(const Coord& origin, const ValueType& fillValue, bool active = false); + + InternalNode(PartialCreate, const Coord&, const ValueType& fillValue, bool active = false); + + /// @brief Deep copy constructor + /// + /// @note This method is multi-threaded! + InternalNode(const InternalNode&); + + /// @brief Value conversion copy constructor + /// + /// @note This method is multi-threaded! + template + explicit InternalNode(const InternalNode& other); + + /// @brief Topology copy constructor + /// + /// @note This method is multi-threaded! + template + InternalNode(const InternalNode& other, + const ValueType& background, TopologyCopy); + + /// @brief Topology copy constructor + /// + /// @note This method is multi-threaded! + template + InternalNode(const InternalNode& other, + const ValueType& offValue, const ValueType& onValue, TopologyCopy); + +#if OPENVDB_ABI_VERSION_NUMBER < 5 + virtual ~InternalNode(); +#else + ~InternalNode(); +#endif + +protected: + using MaskOnIterator = typename NodeMaskType::OnIterator; + using MaskOffIterator = typename NodeMaskType::OffIterator; + using MaskDenseIterator = typename NodeMaskType::DenseIterator; + + // Type tags to disambiguate template instantiations + struct ValueOn {}; struct ValueOff {}; struct ValueAll {}; + struct ChildOn {}; struct ChildOff {}; struct ChildAll {}; + + // The following class templates implement the iterator interfaces specified in Iterator.h + // by providing getItem(), setItem() and/or modifyItem() methods. + + // Sparse iterator that visits child nodes of an InternalNode + template + struct ChildIter: public SparseIteratorBase< + MaskIterT, ChildIter, NodeT, ChildT> + { + ChildIter() {} + ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ChildIter, NodeT, ChildT>(iter, parent) {} + + ChildT& getItem(Index pos) const + { + assert(this->parent().isChildMaskOn(pos)); + return *(this->parent().getChildNode(pos)); + } + + // Note: setItem() can't be called on const iterators. + void setItem(Index pos, const ChildT& c) const { this->parent().resetChildNode(pos, &c); } + + // Note: modifyItem() isn't implemented, since it's not useful for child node pointers. + };// ChildIter + + // Sparse iterator that visits tile values of an InternalNode + template + struct ValueIter: public SparseIteratorBase< + MaskIterT, ValueIter, NodeT, ValueT> + { + ValueIter() {} + ValueIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ValueIter, NodeT, ValueT>(iter, parent) {} + + const ValueT& getItem(Index pos) const { return this->parent().mNodes[pos].getValue(); } + + // Note: setItem() can't be called on const iterators. + void setItem(Index pos, const ValueT& v) const { this->parent().mNodes[pos].setValue(v); } + + // Note: modifyItem() can't be called on const iterators. + template + void modifyItem(Index pos, const ModifyOp& op) const + { + op(this->parent().mNodes[pos].getValue()); + } + };// ValueIter + + // Dense iterator that visits both tiles and child nodes of an InternalNode + template + struct DenseIter: public DenseIteratorBase< + MaskDenseIterator, DenseIter, NodeT, ChildT, ValueT> + { + using BaseT = DenseIteratorBase; + using NonConstValueT = typename BaseT::NonConstValueType; + + DenseIter() {} + DenseIter(const MaskDenseIterator& iter, NodeT* parent): + DenseIteratorBase(iter, parent) {} + + bool getItem(Index pos, ChildT*& child, NonConstValueT& value) const + { + if (this->parent().isChildMaskOn(pos)) { + child = this->parent().getChildNode(pos); + return true; + } + child = nullptr; + value = this->parent().mNodes[pos].getValue(); + return false; + } + + // Note: setItem() can't be called on const iterators. + void setItem(Index pos, ChildT* child) const + { + this->parent().resetChildNode(pos, child); + } + + // Note: unsetItem() can't be called on const iterators. + void unsetItem(Index pos, const ValueT& value) const + { + this->parent().unsetChildNode(pos, value); + } + };// DenseIter + +public: + // Iterators (see Iterator.h for usage) + using ChildOnIter = ChildIter; + using ChildOnCIter = ChildIter; + using ChildOffIter = ValueIter; + using ChildOffCIter = ValueIter; + using ChildAllIter = DenseIter; + using ChildAllCIter = DenseIter; + + using ValueOnIter = ValueIter; + using ValueOnCIter = ValueIter; + using ValueOffIter = ValueIter; + using ValueOffCIter = ValueIter; + using ValueAllIter = ValueIter; + using ValueAllCIter = ValueIter; + + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mChildMask.beginOn(), this); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mChildMask.beginOff(), this); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mChildMask.beginDense(), this); } + ChildOnCIter beginChildOn() const { return cbeginChildOn(); } + ChildOffCIter beginChildOff() const { return cbeginChildOff(); } + ChildAllCIter beginChildAll() const { return cbeginChildAll(); } + ChildOnIter beginChildOn() { return ChildOnIter(mChildMask.beginOn(), this); } + ChildOffIter beginChildOff() { return ChildOffIter(mChildMask.beginOff(), this); } + ChildAllIter beginChildAll() { return ChildAllIter(mChildMask.beginDense(), this); } + + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); } + /// @warning This iterator will also visit child nodes so use isChildMaskOn to skip them! + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mChildMask.beginOff(), this); } + ValueOnCIter beginValueOn() const { return cbeginValueOn(); } + /// @warning This iterator will also visit child nodes so use isChildMaskOn to skip them! + ValueOffCIter beginValueOff() const { return cbeginValueOff(); } + ValueAllCIter beginValueAll() const { return cbeginValueAll(); } + ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); } + /// @warning This iterator will also visit child nodes so use isChildMaskOn to skip them! + ValueOffIter beginValueOff() { return ValueOffIter(mValueMask.beginOff(), this); } + ValueAllIter beginValueAll() { return ValueAllIter(mChildMask.beginOff(), this); } + + + /// @return The dimension of this InternalNode + /// @details The number of voxels in one coordinate direction covered by this node + static Index dim() { return DIM; } + /// @return The level of this node + /// @details Level 0 is by definition the level of the leaf nodes + static Index getLevel() { return LEVEL; } + /// @brief Populated an stil::vector with the dimension of all the + /// nodes in the branch starting with this node. + static void getNodeLog2Dims(std::vector& dims); + /// @return The dimension of the child nodes of this node. + /// @details The number of voxels in one coordinate direction + /// covered by a child node of this node. + static Index getChildDim() { return ChildNodeType::DIM; } + + /// Return the linear table offset of the given global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static void offsetToLocalCoord(Index n, Coord& xyz); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(Index n) const; + + /// Return the grid index coordinates of this node's local origin. + const Coord& origin() const { return mOrigin; } + /// Set the grid index coordinates of this node's local origin. + void setOrigin(const Coord& origin) { mOrigin = origin; } + + Index32 leafCount() const; + void nodeCount(std::vector &vec) const; + Index32 nonLeafCount() const; + Index64 onVoxelCount() const; + Index64 offVoxelCount() const; + Index64 onLeafVoxelCount() const; + Index64 offLeafVoxelCount() const; + Index64 onTileCount() const; + + /// Return the total amount of memory in bytes occupied by this node and its children. + Index64 memUsage() const; + + /// @brief Expand the specified bounding box so that it includes the active tiles + /// of this internal node as well as all the active values in its child nodes. + /// If visitVoxels is false LeafNodes will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; + + /// @brief Return the bounding box of this node, i.e., the full index space + /// spanned by the node regardless of its content. + CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); } + + /// @return True if this node contains no child nodes. + bool isEmpty() const { return mChildMask.isOff(); } + + /// Return @c true if all of this node's table entries have the same active state + /// and the same constant value to within the given tolerance, + /// and return that value in @a firstValue and the active state in @a state. + /// + /// @note This method also returns @c false if this node contains any child nodes. + bool isConstant(ValueType& firstValue, bool& state, + const ValueType& tolerance = zeroVal()) const; + + /// Return @c true if all of this node's tables entries have + /// the same active @a state and the range of its values satisfy + /// (@a maxValue - @a minValue) <= @a tolerance. + /// + /// @param minValue Is updated with the minimum of all values IF method + /// returns @c true. Else the value is undefined! + /// @param maxValue Is updated with the maximum of all values IF method + /// returns @c true. Else the value is undefined! + /// @param state Is updated with the state of all values IF method + /// returns @c true. Else the value is undefined! + /// @param tolerance The tolerance used to determine if values are + /// approximatly constant. + /// + /// @note This method also returns @c false if this node contains any child nodes. + bool isConstant(ValueType& minValue, ValueType& maxValue, + bool& state, const ValueType& tolerance = zeroVal()) const; + + /// Return @c true if this node has no children and only contains inactive values. + bool isInactive() const { return this->isChildMaskOff() && this->isValueMaskOff(); } + + /// Return @c true if the voxel at the given coordinates is active. + bool isValueOn(const Coord& xyz) const; + /// Return @c true if the voxel at the given offset is active. + bool isValueOn(Index offset) const { return mValueMask.isOn(offset); } + + /// Return @c true if this node or any of its child nodes have any active tiles. + bool hasActiveTiles() const; + + const ValueType& getValue(const Coord& xyz) const; + bool probeValue(const Coord& xyz, ValueType& value) const; + + /// @brief Return the level of the tree (0 = leaf) at which the value + /// at the given coordinates resides. + Index getValueLevel(const Coord& xyz) const; + + /// @brief If the first entry in this node's table is a tile, return the tile's value. + /// Otherwise, return the result of calling getFirstValue() on the child. + const ValueType& getFirstValue() const; + /// @brief If the last entry in this node's table is a tile, return the tile's value. + /// Otherwise, return the result of calling getLastValue() on the child. + const ValueType& getLastValue() const; + + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value); + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOn(const Coord& xyz, const ValueType& value); + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value); + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// Return the value of the voxel at the given coordinates and, if necessary, update + /// the accessor with pointers to the nodes along the path from the root node to + /// the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const; + + /// Return @c true if the voxel at the given coordinates is active and, if necessary, + /// update the accessor with pointers to the nodes along the path from the root node + /// to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + bool isValueOnAndCache(const Coord& xyz, AccessorT&) const; + + /// Change the value of the voxel at the given coordinates and mark it as active. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + + /// Set the value of the voxel at the given coordinate but preserves its active state. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); + + /// Apply a functor to the voxel at the given coordinates. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); + + /// Change the value of the voxel at the given coordinates and mark it as inactive. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + + /// Set the active state of the voxel at the given coordinates without changing its value. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&); + + /// Return, in @a value, the value of the voxel at the given coordinates and, + /// if necessary, update the accessor with pointers to the nodes along + /// the path from the root node to the node containing the voxel. + /// @return @c true if the voxel at the given coordinates is active + /// @note Used internally by ValueAccessor. + template + bool probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT&) const; + + /// @brief Return the level of the tree (0 = leaf) at which the value + /// at the given coordinates resides. + /// + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + Index getValueLevelAndCache(const Coord& xyz, AccessorT&) const; + + /// Mark all values (both tiles and voxels) as active. + void setValuesOn(); + + // + // I/O + // + void writeTopology(std::ostream&, bool toHalf = false) const; + void readTopology(std::istream&, bool fromHalf = false); + void writeBuffers(std::ostream&, bool toHalf = false) const; + void readBuffers(std::istream&, bool fromHalf = false); + void readBuffers(std::istream&, const CoordBBox&, bool fromHalf = false); + + + // + // Aux methods + // + + /// Change the sign of all the values represented in this node and its child nodes. + void negate(); + + /// @brief Set all voxels within a given axis-aligned box to a constant value. + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box + /// @param value the value to which to set voxels within the box + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive + /// @note This operation generates a sparse, but not always optimally sparse, + /// representation of the filled box. Follow fill operations with a prune() + /// operation for optimal sparseness. + void fill(const CoordBBox& bbox, const ValueType& value, bool active = true); + + /// @brief Set all voxels within a given axis-aligned box to a constant value + /// and ensure that those voxels are all represented at the leaf level. + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box. + /// @param value the value to which to set voxels within the box. + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive. + /// @sa voxelizeActiveTiles() + void denseFill(const CoordBBox& bbox, const ValueType& value, bool active = true); + + /// @brief Densify active tiles, i.e., replace them with leaf-level active voxels. + /// @param threaded if true, this operation is multi-threaded (over the internal nodes). + /// @sa denseFill() + void voxelizeActiveTiles(bool threaded = true); + + /// @brief Copy into a dense grid the values of the voxels that lie within + /// a given bounding box. + /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid + /// @param dense dense grid with a stride in @e z of one (see tools::Dense + /// in tools/Dense.h for the required API) + /// @note @a bbox is assumed to be identical to or contained in the coordinate domains + /// of both the dense grid and this node, i.e., no bounds checking is performed. + template + void copyToDense(const CoordBBox& bbox, DenseT& dense) const; + + /// @brief Efficiently merge another tree into this tree using one of several schemes. + /// @warning This operation cannibalizes the other tree. + template + void merge(InternalNode& other, const ValueType& background, const ValueType& otherBackground); + + /// @brief Merge, using one of several schemes, this node (and its descendants) + /// with a tile of the same dimensions and the given value and active state. + template void merge(const ValueType& tileValue, bool tileActive); + + /// @brief Union this branch's set of active values with the other branch's + /// active values. The value type of the other branch can be different. + /// @details The resulting state of a value is active if the corresponding value + /// was already active OR if it is active in the other tree. Also, a resulting + /// value maps to a voxel if the corresponding value already mapped to a voxel + /// OR if it is a voxel in the other tree. Thus, a resulting value can only + /// map to a tile if the corresponding value already mapped to a tile + /// AND if it is a tile value in other tree. + /// + /// Specifically, active tiles and voxels in this branch are not changed, and + /// tiles or voxels that were inactive in this branch but active in the other branch + /// are marked as active in this branch but left with their original values. + template + void topologyUnion(const InternalNode& other); + + /// @brief Intersects this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. + /// @details The resulting state of a value is active only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with inactive tiles in the other grid. Likewise active + /// voxels can be turned into unactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template + void topologyIntersection(const InternalNode& other, + const ValueType& background); + + /// @brief Difference this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this node and inactive in the other node. + /// + /// @details The last dummy argument is required to match the signature + /// for InternalNode::topologyDifference. + /// + /// @note This operation modifies only active states, not + /// values. Also note that this operation can result in all voxels + /// being inactive so consider subsequnetly calling prune. + template + void topologyDifference(const InternalNode& other, + const ValueType& background); + + template + void combine(InternalNode& other, CombineOp&); + template + void combine(const ValueType& value, bool valueIsActive, CombineOp&); + + template + void combine2(const InternalNode& other0, const OtherNodeType& other1, CombineOp&); + template + void combine2(const ValueType& value, const OtherNodeType& other, bool valIsActive, CombineOp&); + template + void combine2(const InternalNode& other, const OtherValueType&, bool valIsActive, CombineOp&); + + /// @brief Calls the templated functor BBoxOp with bounding box + /// information for all active tiles and leaf nodes in this node. + /// An additional level argument is provided for each callback. + /// + /// @note The bounding boxes are guarenteed to be non-overlapping. + template void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2Node(OtherNodeType& other, VisitorOp&); + template + void visit2Node(OtherNodeType& other, VisitorOp&) const; + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false); + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const; + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&, const ValueType& background); + + /// @brief Reduce the memory footprint of this tree by replacing with tiles + /// any nodes whose values are all the same (optionally to within a tolerance) + /// and have the same active state. + void prune(const ValueType& tolerance = zeroVal()); + + /// @brief Add the specified leaf to this node, possibly creating a child branch + /// in the process. If the leaf node already exists, replace it. + void addLeaf(LeafNodeType* leaf); + + /// @brief Same as addLeaf() except, if necessary, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + void addLeafAndCache(LeafNodeType* leaf, AccessorT&); + + /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z) + /// and replace it with a tile of the specified value and state. + /// If no such node exists, leave the tree unchanged and return @c nullptr. + /// + /// @note The caller takes ownership of the node and is responsible for deleting it. + /// + /// @warning Since this method potentially removes nodes and branches of the tree, + /// it is important to clear the caches of all ValueAccessors associated with this tree. + template + NodeT* stealNode(const Coord& xyz, const ValueType& value, bool state); + + /// @brief Add the given child node at this level deducing the offset from it's origin. + /// If a child node with this offset already exists, delete the old node and add the + /// new node in its place (i.e. ownership of the new child node is transferred to + /// this InternalNode) + /// @return @c true if inserting the child has been successful, otherwise the caller + /// retains ownership of the node and is responsible for deleting it. + bool addChild(ChildNodeType* child); + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly creating a parent branch or deleting a child branch in the process. + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state); + + /// @brief Delete any existing child branch at the specified offset and add a tile. + void addTile(Index offset, const ValueType& value, bool state); + + /// @brief Same as addTile() except, if necessary, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing (x, y, z). + template + void addTileAndCache(Index level, const Coord& xyz, const ValueType&, bool state, AccessorT&); + + //@{ + /// @brief Return a pointer to the node that contains voxel (x, y, z). + /// If no such node exists, return nullptr. + template NodeType* probeNode(const Coord& xyz); + template const NodeType* probeConstNode(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeNode() except, if necessary, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing (x, y, z). + template + NodeType* probeNodeAndCache(const Coord& xyz, AccessorT&); + template + const NodeType* probeConstNodeAndCache(const Coord& xyz, AccessorT&) const; + //@} + + //@{ + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, return @c nullptr. + LeafNodeType* probeLeaf(const Coord& xyz); + const LeafNodeType* probeConstLeaf(const Coord& xyz) const; + const LeafNodeType* probeLeaf(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeLeaf() except, if necessary, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing (x, y, z). + template + LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc); + template + const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const; + template + const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const; + //@} + + /// @brief Return the leaf node that contains voxel (x, y, z). + /// If no such node exists, create one, but preserve the values and + /// active states of all voxels. + /// + /// @details Use this method to preallocate a static tree topology + /// over which to safely perform multithreaded processing. + LeafNodeType* touchLeaf(const Coord& xyz); + + /// @brief Same as touchLeaf() except, if necessary, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + LeafNodeType* touchLeafAndCache(const Coord& xyz, AccessorT&); + + //@{ + /// @brief Adds all nodes of a certain type to a container with the following API: + /// @code + /// struct ArrayT { + /// using value_type = ...;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// using value_type = LeafType*; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.getNodes(array); + /// @endcode + template + void getNodes(ArrayT& array); + template + void getNodes(ArrayT& array) const; + //@} + + /// @brief Steals all nodes of a certain type from the tree and + /// adds them to a container with the following API: + /// @code + /// struct ArrayT { + /// using value_type = ...;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// using value_type = LeafType*; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.stealNodes(array); + /// @endcode + template + void stealNodes(ArrayT& array, const ValueType& value, bool state); + + /// @brief Change inactive tiles or voxels with value oldBackground to newBackground + /// or -oldBackground to -newBackground. Active values are unchanged. + void resetBackground(const ValueType& oldBackground, const ValueType& newBackground); + + /// @brief Return @c true if the given tree branch has the same node and active value + /// topology as this tree branch (but possibly a different @c ValueType). + template + bool hasSameTopology(const InternalNode* other) const; + +protected: + //@{ + /// Allow iterators to call mask accessor methods (setValueMask(), setChildMask(), etc.). + /// @todo Make mask accessors public? + friend class IteratorBase; + friend class IteratorBase; + friend class IteratorBase; + //@} + + /// @brief During topology-only construction, access is needed + /// to protected/private members of other template instances. + template friend class InternalNode; + + // Mask accessors +public: + bool isValueMaskOn(Index n) const { return mValueMask.isOn(n); } + bool isValueMaskOn() const { return mValueMask.isOn(); } + bool isValueMaskOff(Index n) const { return mValueMask.isOff(n); } + bool isValueMaskOff() const { return mValueMask.isOff(); } + bool isChildMaskOn(Index n) const { return mChildMask.isOn(n); } + bool isChildMaskOff(Index n) const { return mChildMask.isOff(n); } + bool isChildMaskOff() const { return mChildMask.isOff(); } + const NodeMaskType& getValueMask() const { return mValueMask; } + const NodeMaskType& getChildMask() const { return mChildMask; } + NodeMaskType getValueOffMask() const + { + NodeMaskType mask = mValueMask; + mask |= mChildMask; + mask.toggle(); + return mask; + } + const UnionType* getTable() const { return mNodes; } +protected: + //@{ + /// Use a mask accessor to ensure consistency between the child and value masks; + /// i.e., the value mask should always be off wherever the child mask is on. + void setValueMask(Index n, bool on) { mValueMask.set(n, mChildMask.isOn(n) ? false : on); } + //@} + + void makeChildNodeEmpty(Index n, const ValueType& value); + void setChildNode( Index i, ChildNodeType* child);//assumes a tile + void resetChildNode(Index i, ChildNodeType* child);//checks for an existing child + ChildNodeType* unsetChildNode(Index i, const ValueType& value); + + template + static inline void doVisit(NodeT&, VisitorOp&); + + template + static inline void doVisit2Node(NodeT&, OtherNodeT&, VisitorOp&); + + template + static inline void doVisit2(NodeT&, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); + + ///@{ + /// @brief Returns a pointer to the child node at the linear offset n. + /// @warning This protected method assumes that a child node exists at + /// the specified linear offset! + ChildNodeType* getChildNode(Index n); + const ChildNodeType* getChildNode(Index n) const; + ///@} + + ///@{ + /// @brief Protected member classes for recursive multi-threading + struct VoxelizeActiveTiles; + template struct DeepCopy; + template struct TopologyCopy1; + template struct TopologyCopy2; + template struct TopologyUnion; + template struct TopologyDifference; + template struct TopologyIntersection; + ///@} + + UnionType mNodes[NUM_VALUES]; + NodeMaskType mChildMask, mValueMask; + /// Global grid index coordinates (x,y,z) of the local origin of this node + Coord mOrigin; +}; // class InternalNode + + +//////////////////////////////////////// + + +//@{ +/// Helper metafunction used to implement InternalNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameInternalConfig { + static const bool value = false; +}; + +template +struct SameInternalConfig > { + static const bool value = ChildT1::template SameConfiguration::value; +}; +//@} + + +//////////////////////////////////////// + + +template +inline +InternalNode::InternalNode(const ValueType& background) +{ + for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(background); +} + + +template +inline +InternalNode::InternalNode(const Coord& origin, const ValueType& val, bool active): + mOrigin(origin[0] & ~(DIM - 1), // zero out the low-order bits + origin[1] & ~(DIM - 1), + origin[2] & ~(DIM - 1)) +{ + if (active) mValueMask.setOn(); + for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(val); +} + + +// For InternalNodes, the PartialCreate constructor is identical to its +// non-PartialCreate counterpart. +template +inline +InternalNode::InternalNode(PartialCreate, + const Coord& origin, const ValueType& val, bool active) + : mOrigin(origin[0] & ~(DIM-1), origin[1] & ~(DIM-1), origin[2] & ~(DIM-1)) +{ + if (active) mValueMask.setOn(); + for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(val); +} + +template +template +struct InternalNode::DeepCopy +{ + DeepCopy(const OtherInternalNode* source, InternalNode* target) : s(source), t(target) { + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + //(*this)(tbb::blocked_range(0, NUM_VALUES));//serial + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (s->mChildMask.isOff(i)) { + t->mNodes[i].setValue(ValueType(s->mNodes[i].getValue())); + } else { + t->mNodes[i].setChild(new ChildNodeType(*(s->mNodes[i].getChild()))); + } + } + } + const OtherInternalNode* s; + InternalNode* t; +};// DeepCopy + +template +inline +InternalNode::InternalNode(const InternalNode& other): + mChildMask(other.mChildMask), + mValueMask(other.mValueMask), + mOrigin(other.mOrigin) +{ + DeepCopy > tmp(&other, this); +} + + +// Copy-construct from a node with the same configuration but a different ValueType. +template +template +inline +InternalNode::InternalNode(const InternalNode& other) + : mChildMask(other.mChildMask) + , mValueMask(other.mValueMask) + , mOrigin(other.mOrigin) +{ + DeepCopy > tmp(&other, this); +} + +template +template +struct InternalNode::TopologyCopy1 +{ + TopologyCopy1(const OtherInternalNode* source, InternalNode* target, + const ValueType& background) : s(source), t(target), b(background) { + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + //(*this)(tbb::blocked_range(0, NUM_VALUES));//serial + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (s->isChildMaskOn(i)) { + t->mNodes[i].setChild(new ChildNodeType(*(s->mNodes[i].getChild()), + b, TopologyCopy())); + } else { + t->mNodes[i].setValue(b); + } + } + } + const OtherInternalNode* s; + InternalNode* t; + const ValueType &b; +};// TopologyCopy1 + +template +template +inline +InternalNode::InternalNode(const InternalNode& other, + const ValueType& background, TopologyCopy): + mChildMask(other.mChildMask), + mValueMask(other.mValueMask), + mOrigin(other.mOrigin) +{ + TopologyCopy1 > tmp(&other, this, background); +} + +template +template +struct InternalNode::TopologyCopy2 +{ + TopologyCopy2(const OtherInternalNode* source, InternalNode* target, + const ValueType& offValue, const ValueType& onValue) + : s(source), t(target), offV(offValue), onV(onValue) { + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (s->isChildMaskOn(i)) { + t->mNodes[i].setChild(new ChildNodeType(*(s->mNodes[i].getChild()), + offV, onV, TopologyCopy())); + } else { + t->mNodes[i].setValue(s->isValueMaskOn(i) ? onV : offV); + } + } + } + const OtherInternalNode* s; + InternalNode* t; + const ValueType &offV, &onV; + };// TopologyCopy2 + +template +template +inline +InternalNode::InternalNode(const InternalNode& other, + const ValueType& offValue, + const ValueType& onValue, TopologyCopy): + mChildMask(other.mChildMask), + mValueMask(other.mValueMask), + mOrigin(other.mOrigin) +{ + TopologyCopy2 > tmp(&other, this, offValue, onValue); +} + + +template +inline +InternalNode::~InternalNode() +{ + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + delete mNodes[iter.pos()].getChild(); + } +} + + +//////////////////////////////////////// + + +template +inline Index32 +InternalNode::leafCount() const +{ + if (ChildNodeType::getLevel() == 0) return mChildMask.countOn(); + Index32 sum = 0; + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->leafCount(); + } + return sum; +} + +template +inline void +InternalNode::nodeCount(std::vector &vec) const +{ + assert(vec.size() > ChildNodeType::LEVEL); + const auto count = mChildMask.countOn(); + if (ChildNodeType::LEVEL > 0 && count > 0) { + for (auto iter = this->cbeginChildOn(); iter; ++iter) iter->nodeCount(vec); + } + vec[ChildNodeType::LEVEL] += count; +} + + +template +inline Index32 +InternalNode::nonLeafCount() const +{ + Index32 sum = 1; + if (ChildNodeType::getLevel() == 0) return sum; + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->nonLeafCount(); + } + return sum; +} + + +template +inline Index64 +InternalNode::onVoxelCount() const +{ + Index64 sum = ChildT::NUM_VOXELS * mValueMask.countOn(); + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->onVoxelCount(); + } + return sum; +} + + +template +inline Index64 +InternalNode::offVoxelCount() const +{ + Index64 sum = ChildT::NUM_VOXELS * (NUM_VALUES-mValueMask.countOn()-mChildMask.countOn()); + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->offVoxelCount(); + } + return sum; +} + + +template +inline Index64 +InternalNode::onLeafVoxelCount() const +{ + Index64 sum = 0; + for (ChildOnCIter iter = this->beginChildOn(); iter; ++iter) { + sum += mNodes[iter.pos()].getChild()->onLeafVoxelCount(); + } + return sum; +} + + +template +inline Index64 +InternalNode::offLeafVoxelCount() const +{ + Index64 sum = 0; + for (ChildOnCIter iter = this->beginChildOn(); iter; ++iter) { + sum += mNodes[iter.pos()].getChild()->offLeafVoxelCount(); + } + return sum; +} + +template +inline Index64 +InternalNode::onTileCount() const +{ + Index64 sum = mValueMask.countOn(); + for (ChildOnCIter iter = this->cbeginChildOn(); LEVEL>1 && iter; ++iter) { + sum += iter->onTileCount(); + } + return sum; +} + +template +inline Index64 +InternalNode::memUsage() const +{ + Index64 sum = NUM_VALUES * sizeof(UnionType) + mChildMask.memUsage() + + mValueMask.memUsage() + sizeof(mOrigin); + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->memUsage(); + } + return sum; +} + + +template +inline void +InternalNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + if (bbox.isInside(this->getNodeBoundingBox())) return; + + for (ValueOnCIter i = this->cbeginValueOn(); i; ++i) { + bbox.expand(i.getCoord(), ChildT::DIM); + } + for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) { + i->evalActiveBoundingBox(bbox, visitVoxels); + } +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::prune(const ValueType& tolerance) +{ + bool state = false; + ValueType value = zeroVal(); + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + const Index i = iter.pos(); + ChildT* child = mNodes[i].getChild(); + child->prune(tolerance); + if (child->isConstant(value, state, tolerance)) { + delete child; + mChildMask.setOff(i); + mValueMask.set(i, state); + mNodes[i].setValue(value); + } + } +} + + +//////////////////////////////////////// + + +template +template +inline NodeT* +InternalNode::stealNode(const Coord& xyz, const ValueType& value, bool state) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return nullptr; + ChildT* child = mNodes[n].getChild(); + if (std::is_same::value) { + mChildMask.setOff(n); + mValueMask.set(n, state); + mNodes[n].setValue(value); + } + return (std::is_same::value) + ? reinterpret_cast(child) + : child->template stealNode(xyz, value, state); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +template +inline NodeT* +InternalNode::probeNode(const Coord& xyz) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return nullptr; + ChildT* child = mNodes[n].getChild(); + return (std::is_same::value) + ? reinterpret_cast(child) + : child->template probeNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline NodeT* +InternalNode::probeNodeAndCache(const Coord& xyz, AccessorT& acc) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return nullptr; + ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + return (std::is_same::value) + ? reinterpret_cast(child) + : child->template probeNodeAndCache(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline const NodeT* +InternalNode::probeConstNode(const Coord& xyz) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return nullptr; + const ChildT* child = mNodes[n].getChild(); + return (std::is_same::value) + ? reinterpret_cast(child) + : child->template probeConstNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline const NodeT* +InternalNode::probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return nullptr; + const ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + return (std::is_same::value) + ? reinterpret_cast(child) + : child->template probeConstNodeAndCache(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +inline typename ChildT::LeafNodeType* +InternalNode::probeLeaf(const Coord& xyz) +{ + return this->template probeNode(xyz); +} + + +template +template +inline typename ChildT::LeafNodeType* +InternalNode::probeLeafAndCache(const Coord& xyz, AccessorT& acc) +{ + return this->template probeNodeAndCache(xyz, acc); +} + + +template +template +inline const typename ChildT::LeafNodeType* +InternalNode::probeLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->probeConstLeafAndCache(xyz, acc); +} + + +template +inline const typename ChildT::LeafNodeType* +InternalNode::probeConstLeaf(const Coord& xyz) const +{ + return this->template probeConstNode(xyz); +} + + +template +template +inline const typename ChildT::LeafNodeType* +InternalNode::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->template probeConstNodeAndCache(xyz, acc); +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::addLeaf(LeafNodeType* leaf) +{ + assert(leaf != nullptr); + const Coord& xyz = leaf->origin(); + const Index n = this->coordToOffset(xyz); + ChildT* child = nullptr; + if (mChildMask.isOff(n)) { + if (ChildT::LEVEL>0) { + child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); + } else { + child = reinterpret_cast(leaf); + } + this->setChildNode(n, child); + } else { + if (ChildT::LEVEL>0) { + child = mNodes[n].getChild(); + } else { + delete mNodes[n].getChild(); + child = reinterpret_cast(leaf); + mNodes[n].setChild(child); + } + } + child->addLeaf(leaf); +} + + +template +template +inline void +InternalNode::addLeafAndCache(LeafNodeType* leaf, AccessorT& acc) +{ + assert(leaf != nullptr); + const Coord& xyz = leaf->origin(); + const Index n = this->coordToOffset(xyz); + ChildT* child = nullptr; + if (mChildMask.isOff(n)) { + if (ChildT::LEVEL>0) { + child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); + acc.insert(xyz, child);//we only cache internal nodes + } else { + child = reinterpret_cast(leaf); + } + this->setChildNode(n, child); + } else { + if (ChildT::LEVEL>0) { + child = mNodes[n].getChild(); + acc.insert(xyz, child);//we only cache internal nodes + } else { + delete mNodes[n].getChild(); + child = reinterpret_cast(leaf); + mNodes[n].setChild(child); + } + } + child->addLeafAndCache(leaf, acc); +} + + +//////////////////////////////////////// + + +template +inline bool +InternalNode::addChild(ChildT* child) +{ + assert(child); + const Coord& xyz = child->origin(); + // verify that the child belongs in this internal node + if (Coord((xyz & ~(DIM-1))) != this->origin()) return false; + // compute the offset and insert the child node + const Index n = this->coordToOffset(xyz); + // this also deletes an existing child node + this->resetChildNode(n, child); + return true; +} + + +template +inline void +InternalNode::addTile(Index n, const ValueType& value, bool state) +{ + assert(n < NUM_VALUES); + this->makeChildNodeEmpty(n, value); + mValueMask.set(n, state); +} + + +template +inline void +InternalNode::addTile(Index level, const Coord& xyz, + const ValueType& value, bool state) +{ + if (LEVEL >= level) { + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) {// tile case + if (LEVEL > level) { + ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); + this->setChildNode(n, child); + child->addTile(level, xyz, value, state); + } else { + mValueMask.set(n, state); + mNodes[n].setValue(value); + } + } else {// child branch case + ChildT* child = mNodes[n].getChild(); + if (LEVEL > level) { + child->addTile(level, xyz, value, state); + } else { + delete child; + mChildMask.setOff(n); + mValueMask.set(n, state); + mNodes[n].setValue(value); + } + } + } +} + + +template +template +inline void +InternalNode::addTileAndCache(Index level, const Coord& xyz, + const ValueType& value, bool state, AccessorT& acc) +{ + if (LEVEL >= level) { + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) {// tile case + if (LEVEL > level) { + ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); + this->setChildNode(n, child); + acc.insert(xyz, child); + child->addTileAndCache(level, xyz, value, state, acc); + } else { + mValueMask.set(n, state); + mNodes[n].setValue(value); + } + } else {// child branch case + ChildT* child = mNodes[n].getChild(); + if (LEVEL > level) { + acc.insert(xyz, child); + child->addTileAndCache(level, xyz, value, state, acc); + } else { + delete child; + mChildMask.setOff(n); + mValueMask.set(n, state); + mNodes[n].setValue(value); + } + } + } +} + + +//////////////////////////////////////// + + +template +inline typename ChildT::LeafNodeType* +InternalNode::touchLeaf(const Coord& xyz) +{ + const Index n = this->coordToOffset(xyz); + ChildT* child = nullptr; + if (mChildMask.isOff(n)) { + child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); + this->setChildNode(n, child); + } else { + child = mNodes[n].getChild(); + } + return child->touchLeaf(xyz); +} + + +template +template +inline typename ChildT::LeafNodeType* +InternalNode::touchLeafAndCache(const Coord& xyz, AccessorT& acc) +{ + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) { + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), mValueMask.isOn(n))); + } + acc.insert(xyz, mNodes[n].getChild()); + return mNodes[n].getChild()->touchLeafAndCache(xyz, acc); +} + + +//////////////////////////////////////// + + +template +inline bool +InternalNode::isConstant(ValueType& firstValue, bool& state, + const ValueType& tolerance) const +{ + if (!mChildMask.isOff() || !mValueMask.isConstant(state)) return false;// early termination + + firstValue = mNodes[0].getValue(); + for (Index i = 1; i < NUM_VALUES; ++i) { + if (!math::isApproxEqual(mNodes[i].getValue(), firstValue, tolerance)) { + return false; // early termination + } + } + return true; +} + + +//////////////////////////////////////// + + +template +inline bool +InternalNode::isConstant(ValueType& minValue, + ValueType& maxValue, + bool& state, + const ValueType& tolerance) const +{ + + if (!mChildMask.isOff() || !mValueMask.isConstant(state)) return false;// early termination + minValue = maxValue = mNodes[0].getValue(); + for (Index i = 1; i < NUM_VALUES; ++i) { + const ValueType& v = mNodes[i].getValue(); + if (v < minValue) { + if ((maxValue - v) > tolerance) return false;// early termination + minValue = v; + } else if (v > maxValue) { + if ((v - minValue) > tolerance) return false;// early termination + maxValue = v; + } + } + return true; +} + + +//////////////////////////////////////// + + +template +inline bool +InternalNode::hasActiveTiles() const +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const bool anyActiveTiles = !mValueMask.isOff(); + if (LEVEL==1 || anyActiveTiles) return anyActiveTiles; + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + if (iter->hasActiveTiles()) return true; + } + return false; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +inline bool +InternalNode::isValueOn(const Coord& xyz) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOff(n)) return this->isValueMaskOn(n); + return mNodes[n].getChild()->isValueOn(xyz); +} + +template +template +inline bool +InternalNode::isValueOnAndCache(const Coord& xyz, AccessorT& acc) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOff(n)) return this->isValueMaskOn(n); + acc.insert(xyz, mNodes[n].getChild()); + return mNodes[n].getChild()->isValueOnAndCache(xyz, acc); +} + + +template +inline const typename ChildT::ValueType& +InternalNode::getValue(const Coord& xyz) const +{ + const Index n = this->coordToOffset(xyz); + return this->isChildMaskOff(n) ? mNodes[n].getValue() + : mNodes[n].getChild()->getValue(xyz); +} + +template +template +inline const typename ChildT::ValueType& +InternalNode::getValueAndCache(const Coord& xyz, AccessorT& acc) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOn(n)) { + acc.insert(xyz, mNodes[n].getChild()); + return mNodes[n].getChild()->getValueAndCache(xyz, acc); + } + return mNodes[n].getValue(); +} + + +template +inline Index +InternalNode::getValueLevel(const Coord& xyz) const +{ + const Index n = this->coordToOffset(xyz); + return this->isChildMaskOff(n) ? LEVEL : mNodes[n].getChild()->getValueLevel(xyz); +} + +template +template +inline Index +InternalNode::getValueLevelAndCache(const Coord& xyz, AccessorT& acc) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOn(n)) { + acc.insert(xyz, mNodes[n].getChild()); + return mNodes[n].getChild()->getValueLevelAndCache(xyz, acc); + } + return LEVEL; +} + + +template +inline bool +InternalNode::probeValue(const Coord& xyz, ValueType& value) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOff(n)) { + value = mNodes[n].getValue(); + return this->isValueMaskOn(n); + } + return mNodes[n].getChild()->probeValue(xyz, value); +} + +template +template +inline bool +InternalNode::probeValueAndCache(const Coord& xyz, + ValueType& value, AccessorT& acc) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOn(n)) { + acc.insert(xyz, mNodes[n].getChild()); + return mNodes[n].getChild()->probeValueAndCache(xyz, value, acc); + } + value = mNodes[n].getValue(); + return this->isValueMaskOn(n); +} + + +template +inline void +InternalNode::setValueOff(const Coord& xyz) +{ + const Index n = this->coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild && this->isValueMaskOn(n)) { + // If the voxel belongs to a constant tile that is active, + // a child subtree must be constructed. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/true)); + } + if (hasChild) mNodes[n].getChild()->setValueOff(xyz); +} + + +template +inline void +InternalNode::setValueOn(const Coord& xyz) +{ + const Index n = this->coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild && !this->isValueMaskOn(n)) { + // If the voxel belongs to a constant tile that is inactive, + // a child subtree must be constructed. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/false)); + } + if (hasChild) mNodes[n].getChild()->setValueOn(xyz); +} + + +template +inline void +InternalNode::setValueOff(const Coord& xyz, const ValueType& value) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + const bool active = this->isValueMaskOn(n); + if (active || !math::isExactlyEqual(mNodes[n].getValue(), value)) { + // If the voxel belongs to a tile that is either active or that + // has a constant value that is different from the one provided, + // a child subtree must be constructed. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) mNodes[n].getChild()->setValueOff(xyz, value); +} + +template +template +inline void +InternalNode::setValueOffAndCache(const Coord& xyz, + const ValueType& value, AccessorT& acc) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + const bool active = this->isValueMaskOn(n); + if (active || !math::isExactlyEqual(mNodes[n].getValue(), value)) { + // If the voxel belongs to a tile that is either active or that + // has a constant value that is different from the one provided, + // a child subtree must be constructed. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) { + ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->setValueOffAndCache(xyz, value, acc); + } +} + + +template +inline void +InternalNode::setValueOn(const Coord& xyz, const ValueType& value) +{ + const Index n = this->coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + const bool active = this->isValueMaskOn(n); // tile's active state + if (!active || !math::isExactlyEqual(mNodes[n].getValue(), value)) { + // If the voxel belongs to a tile that is either inactive or that + // has a constant value that is different from the one provided, + // a child subtree must be constructed. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) mNodes[n].getChild()->setValueOn(xyz, value); +} + +template +template +inline void +InternalNode::setValueAndCache(const Coord& xyz, + const ValueType& value, AccessorT& acc) +{ + const Index n = this->coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + const bool active = this->isValueMaskOn(n); + if (!active || !math::isExactlyEqual(mNodes[n].getValue(), value)) { + // If the voxel belongs to a tile that is either inactive or that + // has a constant value that is different from the one provided, + // a child subtree must be constructed. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) { + acc.insert(xyz, mNodes[n].getChild()); + mNodes[n].getChild()->setValueAndCache(xyz, value, acc); + } +} + + +template +inline void +InternalNode::setValueOnly(const Coord& xyz, const ValueType& value) +{ + const Index n = this->coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild && !math::isExactlyEqual(mNodes[n].getValue(), value)) { + // If the voxel has a tile value that is different from the one provided, + // a child subtree must be constructed. + const bool active = this->isValueMaskOn(n); + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + if (hasChild) mNodes[n].getChild()->setValueOnly(xyz, value); +} + +template +template +inline void +InternalNode::setValueOnlyAndCache(const Coord& xyz, + const ValueType& value, AccessorT& acc) +{ + const Index n = this->coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild && !math::isExactlyEqual(mNodes[n].getValue(), value)) { + // If the voxel has a tile value that is different from the one provided, + // a child subtree must be constructed. + const bool active = this->isValueMaskOn(n); + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + if (hasChild) { + acc.insert(xyz, mNodes[n].getChild()); + mNodes[n].getChild()->setValueOnlyAndCache(xyz, value, acc); + } +} + + +template +inline void +InternalNode::setActiveState(const Coord& xyz, bool on) +{ + const Index n = this->coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + if (on != this->isValueMaskOn(n)) { + // If the voxel belongs to a tile with the wrong active state, + // then a child subtree must be constructed. + // 'on' is the voxel's new state, therefore '!on' is the tile's current state + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), !on)); + } + } + if (hasChild) mNodes[n].getChild()->setActiveState(xyz, on); +} + +template +template +inline void +InternalNode::setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& acc) +{ + const Index n = this->coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + if (on != this->isValueMaskOn(n)) { + // If the voxel belongs to a tile with the wrong active state, + // then a child subtree must be constructed. + // 'on' is the voxel's new state, therefore '!on' is the tile's current state + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), !on)); + } + } + if (hasChild) { + ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->setActiveStateAndCache(xyz, on, acc); + } +} + + +template +inline void +InternalNode::setValuesOn() +{ + mValueMask = !mChildMask; + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + mNodes[iter.pos()].getChild()->setValuesOn(); + } +} + + +template +template +inline void +InternalNode::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + const bool active = this->isValueMaskOn(n); + bool createChild = !active; + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = mNodes[n].getValue(); + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) mNodes[n].getChild()->modifyValue(xyz, op); +} + +template +template +inline void +InternalNode::modifyValueAndCache(const Coord& xyz, const ModifyOp& op, + AccessorT& acc) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + const bool active = this->isValueMaskOn(n); + bool createChild = !active; + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = mNodes[n].getValue(); + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) { + ChildNodeType* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->modifyValueAndCache(xyz, op, acc); + } +} + + +template +template +inline void +InternalNode::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + const bool tileState = this->isValueMaskOn(n); + const ValueType& tileVal = mNodes[n].getValue(); + bool modifiedState = !tileState; + ValueType modifiedVal = tileVal; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tileState || !math::isExactlyEqual(modifiedVal, tileVal)) { + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, tileVal, tileState)); + } + } + if (hasChild) mNodes[n].getChild()->modifyValueAndActiveState(xyz, op); +} + +template +template +inline void +InternalNode::modifyValueAndActiveStateAndCache( + const Coord& xyz, const ModifyOp& op, AccessorT& acc) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + const bool tileState = this->isValueMaskOn(n); + const ValueType& tileVal = mNodes[n].getValue(); + bool modifiedState = !tileState; + ValueType modifiedVal = tileVal; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tileState || !math::isExactlyEqual(modifiedVal, tileVal)) { + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, tileVal, tileState)); + } + } + if (hasChild) { + ChildNodeType* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->modifyValueAndActiveStateAndCache(xyz, op, acc); + } +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::clip(const CoordBBox& clipBBox, const ValueType& background) +{ + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. Fill it with background tiles. + this->fill(nodeBBox, background, /*active=*/false); + } else if (clipBBox.isInside(nodeBBox)) { + // This node lies completely inside the clipping region. Leave it intact. + return; + } + + // This node isn't completely contained inside the clipping region. + // Clip tiles and children, and replace any that lie outside the region + // with background tiles. + + for (Index pos = 0; pos < NUM_VALUES; ++pos) { + const Coord xyz = this->offsetToGlobalCoord(pos); // tile or child origin + CoordBBox tileBBox(xyz, xyz.offsetBy(ChildT::DIM - 1)); // tile or child bounds + if (!clipBBox.hasOverlap(tileBBox)) { + // This table entry lies completely outside the clipping region. + // Replace it with a background tile. + this->makeChildNodeEmpty(pos, background); + mValueMask.setOff(pos); + } else if (!clipBBox.isInside(tileBBox)) { + // This table entry does not lie completely inside the clipping region + // and must be clipped. + if (this->isChildMaskOn(pos)) { + mNodes[pos].getChild()->clip(clipBBox, background); + } else { + // Replace this tile with a background tile, then fill the clip region + // with the tile's original value. (This might create a child branch.) + tileBBox.intersect(clipBBox); + const ValueType val = mNodes[pos].getValue(); + const bool on = this->isValueMaskOn(pos); + mNodes[pos].setValue(background); + mValueMask.setOff(pos); + this->fill(tileBBox, val, on); + } + } else { + // This table entry lies completely inside the clipping region. Leave it intact. + } + } +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::fill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + auto clippedBBox = this->getNodeBoundingBox(); + clippedBBox.intersect(bbox); + if (!clippedBBox) return; + + // Iterate over the fill region in axis-aligned, tile-sized chunks. + // (The first and last chunks along each axis might be smaller than a tile.) + Coord xyz, tileMin, tileMax; + for (int x = clippedBBox.min().x(); x <= clippedBBox.max().x(); x = tileMax.x() + 1) { + xyz.setX(x); + for (int y = clippedBBox.min().y(); y <= clippedBBox.max().y(); y = tileMax.y() + 1) { + xyz.setY(y); + for (int z = clippedBBox.min().z(); z <= clippedBBox.max().z(); z = tileMax.z() + 1) { + xyz.setZ(z); + + // Get the bounds of the tile that contains voxel (x, y, z). + const Index n = this->coordToOffset(xyz); + tileMin = this->offsetToGlobalCoord(n); + tileMax = tileMin.offsetBy(ChildT::DIM - 1); + + if (xyz != tileMin || Coord::lessThan(clippedBBox.max(), tileMax)) { + // If the box defined by (xyz, clippedBBox.max()) doesn't completely enclose + // the tile to which xyz belongs, create a child node (or retrieve + // the existing one). + ChildT* child = nullptr; + if (this->isChildMaskOff(n)) { + // Replace the tile with a newly-created child that is initialized + // with the tile's value and active state. + child = new ChildT{xyz, mNodes[n].getValue(), this->isValueMaskOn(n)}; + this->setChildNode(n, child); + } else { + child = mNodes[n].getChild(); + } + + // Forward the fill request to the child. + if (child) { + const Coord tmp = Coord::minComponent(clippedBBox.max(), tileMax); + child->fill(CoordBBox(xyz, tmp), value, active); + } + + } else { + // If the box given by (xyz, clippedBBox.max()) completely encloses + // the tile to which xyz belongs, create the tile (if it + // doesn't already exist) and give it the fill value. + this->makeChildNodeEmpty(n, value); + mValueMask.set(n, active); + } + } + } + } +} + + +template +inline void +InternalNode::denseFill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + auto clippedBBox = this->getNodeBoundingBox(); + clippedBBox.intersect(bbox); + if (!clippedBBox) return; + + // Iterate over the fill region in axis-aligned, tile-sized chunks. + // (The first and last chunks along each axis might be smaller than a tile.) + Coord xyz, tileMin, tileMax; + for (int x = clippedBBox.min().x(); x <= clippedBBox.max().x(); x = tileMax.x() + 1) { + xyz.setX(x); + for (int y = clippedBBox.min().y(); y <= clippedBBox.max().y(); y = tileMax.y() + 1) { + xyz.setY(y); + for (int z = clippedBBox.min().z(); z <= clippedBBox.max().z(); z = tileMax.z() + 1) { + xyz.setZ(z); + + // Get the table index of the tile that contains voxel (x, y, z). + const auto n = this->coordToOffset(xyz); + + // Retrieve the child node at index n, or replace the tile at index n with a child. + ChildT* child = nullptr; + if (this->isChildMaskOn(n)) { + child = mNodes[n].getChild(); + } else { + // Replace the tile with a newly-created child that is filled + // with the tile's value and active state. + child = new ChildT{xyz, mNodes[n].getValue(), this->isValueMaskOn(n)}; + this->setChildNode(n, child); + } + + // Get the bounds of the tile that contains voxel (x, y, z). + tileMin = this->offsetToGlobalCoord(n); + tileMax = tileMin.offsetBy(ChildT::DIM - 1); + + // Forward the fill request to the child. + child->denseFill(CoordBBox{xyz, clippedBBox.max()}, value, active); + } + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ + using DenseValueType = typename DenseT::ValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + for (Coord xyz = bbox.min(), max; xyz[0] <= bbox.max()[0]; xyz[0] = max[0] + 1) { + for (xyz[1] = bbox.min()[1]; xyz[1] <= bbox.max()[1]; xyz[1] = max[1] + 1) { + for (xyz[2] = bbox.min()[2]; xyz[2] <= bbox.max()[2]; xyz[2] = max[2] + 1) { + const Index n = this->coordToOffset(xyz); + // Get max coordinates of the child node that contains voxel xyz. + max = this->offsetToGlobalCoord(n).offsetBy(ChildT::DIM-1); + + // Get the bbox of the interection of bbox and the child node + CoordBBox sub(xyz, Coord::minComponent(bbox.max(), max)); + + if (this->isChildMaskOn(n)) {//is a child + mNodes[n].getChild()->copyToDense(sub, dense); + } else {//a tile value + const ValueType value = mNodes[n].getValue(); + sub.translate(-min); + DenseValueType* a0 = dense.data() + zStride*sub.min()[2]; + for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x +inline void +InternalNode::writeTopology(std::ostream& os, bool toHalf) const +{ + mChildMask.save(os); + mValueMask.save(os); + + { + // Copy all of this node's values into an array. + std::unique_ptr valuePtr(new ValueType[NUM_VALUES]); + ValueType* values = valuePtr.get(); + const ValueType zero = zeroVal(); + for (Index i = 0; i < NUM_VALUES; ++i) { + values[i] = (mChildMask.isOff(i) ? mNodes[i].getValue() : zero); + } + // Compress (optionally) and write out the contents of the array. + io::writeCompressedValues(os, values, NUM_VALUES, mValueMask, mChildMask, toHalf); + } + // Write out the child nodes in order. + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + iter->writeTopology(os, toHalf); + } +} + + +template +inline void +InternalNode::readTopology(std::istream& is, bool fromHalf) +{ + const ValueType background = (!io::getGridBackgroundValuePtr(is) ? zeroVal() + : *static_cast(io::getGridBackgroundValuePtr(is))); + + mChildMask.load(is); + mValueMask.load(is); + + if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_INTERNALNODE_COMPRESSION) { + for (Index i = 0; i < NUM_VALUES; ++i) { + if (this->isChildMaskOn(i)) { + ChildNodeType* child = + new ChildNodeType(PartialCreate(), offsetToGlobalCoord(i), background); + mNodes[i].setChild(child); + child->readTopology(is); + } else { + ValueType value; + is.read(reinterpret_cast(&value), sizeof(ValueType)); + mNodes[i].setValue(value); + } + } + } else { + const bool oldVersion = + (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION); + const Index numValues = (oldVersion ? mChildMask.countOff() : NUM_VALUES); + { + // Read in (and uncompress, if necessary) all of this node's values + // into a contiguous array. + std::unique_ptr valuePtr(new ValueType[numValues]); + ValueType* values = valuePtr.get(); + io::readCompressedValues(is, values, numValues, mValueMask, fromHalf); + + // Copy values from the array into this node's table. + if (oldVersion) { + Index n = 0; + for (ValueAllIter iter = this->beginValueAll(); iter; ++iter) { + mNodes[iter.pos()].setValue(values[n++]); + } + assert(n == numValues); + } else { + for (ValueAllIter iter = this->beginValueAll(); iter; ++iter) { + mNodes[iter.pos()].setValue(values[iter.pos()]); + } + } + } + // Read in all child nodes and insert them into the table at their proper locations. + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + ChildNodeType* child = new ChildNodeType(PartialCreate(), iter.getCoord(), background); + mNodes[iter.pos()].setChild(child); + child->readTopology(is, fromHalf); + } + } +} + + +//////////////////////////////////////// + + +template +inline const typename ChildT::ValueType& +InternalNode::getFirstValue() const +{ + return (this->isChildMaskOn(0) ? mNodes[0].getChild()->getFirstValue() : mNodes[0].getValue()); +} + + +template +inline const typename ChildT::ValueType& +InternalNode::getLastValue() const +{ + const Index n = NUM_VALUES - 1; + return (this->isChildMaskOn(n) ? mNodes[n].getChild()->getLastValue() : mNodes[n].getValue()); +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::negate() +{ + for (Index i = 0; i < NUM_VALUES; ++i) { + if (this->isChildMaskOn(i)) { + mNodes[i].getChild()->negate(); + } else { + mNodes[i].setValue(math::negative(mNodes[i].getValue())); + } + } + +} + + +//////////////////////////////////////// + + +template +struct InternalNode::VoxelizeActiveTiles +{ + VoxelizeActiveTiles(InternalNode &node) : mNode(&node) { + //(*this)(tbb::blocked_range(0, NUM_VALUES));//single thread for debugging + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + + node.mChildMask |= node.mValueMask; + node.mValueMask.setOff(); + } + void operator()(const tbb::blocked_range &r) const + { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (mNode->mChildMask.isOn(i)) {// Loop over node's child nodes + mNode->mNodes[i].getChild()->voxelizeActiveTiles(true); + } else if (mNode->mValueMask.isOn(i)) {// Loop over node's active tiles + const Coord &ijk = mNode->offsetToGlobalCoord(i); + ChildNodeType *child = new ChildNodeType(ijk, mNode->mNodes[i].getValue(), true); + child->voxelizeActiveTiles(true); + mNode->mNodes[i].setChild(child); + } + } + } + InternalNode* mNode; +};// VoxelizeActiveTiles + +template +inline void +InternalNode::voxelizeActiveTiles(bool threaded) +{ + if (threaded) { + VoxelizeActiveTiles tmp(*this); + } else { + for (ValueOnIter iter = this->beginValueOn(); iter; ++iter) { + this->setChildNode(iter.pos(), + new ChildNodeType(iter.getCoord(), iter.getValue(), true)); + } + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) + iter->voxelizeActiveTiles(false); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::merge(InternalNode& other, + const ValueType& background, const ValueType& otherBackground) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + switch (Policy) { + + case MERGE_ACTIVE_STATES: + default: + { + for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge this node's child with the other node's child. + mNodes[n].getChild()->template merge(*iter, + background, otherBackground); + } else if (mValueMask.isOff(n)) { + // Replace this node's inactive tile with the other node's child + // and replace the other node's child with a tile of undefined value + // (which is okay since the other tree is assumed to be cannibalized + // in the process of merging). + ChildNodeType* child = other.mNodes[n].getChild(); + other.mChildMask.setOff(n); + child->resetBackground(otherBackground, background); + this->setChildNode(n, child); + } + } + + // Copy active tile values. + for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mValueMask.isOff(n)) { + // Replace this node's child or inactive tile with the other node's active tile. + this->makeChildNodeEmpty(n, iter.getValue()); + mValueMask.setOn(n); + } + } + break; + } + + case MERGE_NODES: + { + for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge this node's child with the other node's child. + mNodes[n].getChild()->template merge(*iter, background, otherBackground); + } else { + // Replace this node's tile (regardless of its active state) with + // the other node's child and replace the other node's child with + // a tile of undefined value (which is okay since the other tree + // is assumed to be cannibalized in the process of merging). + ChildNodeType* child = other.mNodes[n].getChild(); + other.mChildMask.setOff(n); + child->resetBackground(otherBackground, background); + this->setChildNode(n, child); + } + } + break; + } + + case MERGE_ACTIVE_STATES_AND_NODES: + { + // Transfer children from the other tree to this tree. + for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge this node's child with the other node's child. + mNodes[n].getChild()->template merge(*iter, background, otherBackground); + } else { + // Replace this node's tile with the other node's child, leaving the other + // node with an inactive tile of undefined value (which is okay since + // the other tree is assumed to be cannibalized in the process of merging). + ChildNodeType* child = other.mNodes[n].getChild(); + other.mChildMask.setOff(n); + child->resetBackground(otherBackground, background); + if (mValueMask.isOn(n)) { + // Merge the child with this node's active tile. + child->template merge(mNodes[n].getValue(), /*on=*/true); + mValueMask.setOff(n); + } + mChildMask.setOn(n); + mNodes[n].setChild(child); + } + } + + // Merge active tiles into this tree. + for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge the other node's active tile into this node's child. + mNodes[n].getChild()->template merge(iter.getValue(), /*on=*/true); + } else if (mValueMask.isOff(n)) { + // Replace this node's inactive tile with the other node's active tile. + mNodes[n].setValue(iter.getValue()); + mValueMask.setOn(n); + } + } + break; + } + + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline void +InternalNode::merge(const ValueType& tileValue, bool tileActive) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + + // For MERGE_ACTIVE_STATES_AND_NODES, inactive tiles in the other tree are ignored. + if (!tileActive) return; + + // Iterate over this node's children and inactive tiles. + for (ValueOffIter iter = this->beginValueOff(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge the other node's active tile into this node's child. + mNodes[n].getChild()->template merge(tileValue, /*on=*/true); + } else { + // Replace this node's inactive tile with the other node's active tile. + iter.setValue(tileValue); + mValueMask.setOn(n); + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +template +struct InternalNode::TopologyUnion +{ + using W = typename NodeMaskType::Word; + struct A { inline void operator()(W &tV, const W& sV, const W& tC) const + { tV = (tV | sV) & ~tC; } + }; + TopologyUnion(const OtherInternalNode* source, InternalNode* target) : s(source), t(target) { + //(*this)(tbb::blocked_range(0, NUM_VALUES));//single thread for debugging + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + + // Bit processing is done in a single thread! + t->mChildMask |= s->mChildMask;//serial but very fast bitwise post-process + A op; + t->mValueMask.foreach(s->mValueMask, t->mChildMask, op); + assert((t->mValueMask & t->mChildMask).isOff());//no overlapping active tiles or child nodes + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (s->mChildMask.isOn(i)) {// Loop over other node's child nodes + const typename OtherInternalNode::ChildNodeType& other = *(s->mNodes[i].getChild()); + if (t->mChildMask.isOn(i)) {//this has a child node + t->mNodes[i].getChild()->topologyUnion(other); + } else {// this is a tile so replace it with a child branch with identical topology + ChildT* child = new ChildT(other, t->mNodes[i].getValue(), TopologyCopy()); + if (t->mValueMask.isOn(i)) child->setValuesOn();//activate all values + t->mNodes[i].setChild(child); + } + } else if (s->mValueMask.isOn(i) && t->mChildMask.isOn(i)) { + t->mNodes[i].getChild()->setValuesOn(); + } + } + } + const OtherInternalNode* s; + InternalNode* t; +};// TopologyUnion + +template +template +inline void +InternalNode::topologyUnion(const InternalNode& other) +{ + TopologyUnion > tmp(&other, this); +} + +template +template +struct InternalNode::TopologyIntersection +{ + using W = typename NodeMaskType::Word; + struct A { inline void operator()(W &tC, const W& sC, const W& sV, const W& tV) const + { tC = (tC & (sC | sV)) | (tV & sC); } + }; + TopologyIntersection(const OtherInternalNode* source, InternalNode* target, + const ValueType& background) : s(source), t(target), b(background) { + //(*this)(tbb::blocked_range(0, NUM_VALUES));//single thread for debugging + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + + // Bit processing is done in a single thread! + A op; + t->mChildMask.foreach(s->mChildMask, s->mValueMask, t->mValueMask, op); + + t->mValueMask &= s->mValueMask; + assert((t->mValueMask & t->mChildMask).isOff());//no overlapping active tiles or child nodes + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (t->mChildMask.isOn(i)) {// Loop over this node's child nodes + ChildT* child = t->mNodes[i].getChild(); + if (s->mChildMask.isOn(i)) {//other also has a child node + child->topologyIntersection(*(s->mNodes[i].getChild()), b); + } else if (s->mValueMask.isOff(i)) {//other is an inactive tile + delete child;//convert child to an inactive tile + t->mNodes[i].setValue(b); + } + } else if (t->mValueMask.isOn(i) && s->mChildMask.isOn(i)) {//active tile -> a branch + t->mNodes[i].setChild(new ChildT(*(s->mNodes[i].getChild()), + t->mNodes[i].getValue(), TopologyCopy())); + } + } + } + const OtherInternalNode* s; + InternalNode* t; + const ValueType& b; +};// TopologyIntersection + +template +template +inline void +InternalNode::topologyIntersection( + const InternalNode& other, const ValueType& background) +{ + TopologyIntersection > tmp(&other, this, background); +} + +template +template +struct InternalNode::TopologyDifference +{ + using W = typename NodeMaskType::Word; + struct A {inline void operator()(W &tC, const W& sC, const W& sV, const W& tV) const + { tC = (tC & (sC | ~sV)) | (tV & sC); } + }; + struct B {inline void operator()(W &tV, const W& sC, const W& sV, const W& tC) const + { tV &= ~((tC & sV) | (sC | sV)); } + }; + TopologyDifference(const OtherInternalNode* source, InternalNode* target, + const ValueType& background) : s(source), t(target), b(background) { + //(*this)(tbb::blocked_range(0, NUM_VALUES));//single thread for debugging + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + + // Bit processing is done in a single thread! + const NodeMaskType oldChildMask(t->mChildMask);//important to avoid cross pollution + A op1; + t->mChildMask.foreach(s->mChildMask, s->mValueMask, t->mValueMask, op1); + + B op2; + t->mValueMask.foreach(t->mChildMask, s->mValueMask, oldChildMask, op2); + assert((t->mValueMask & t->mChildMask).isOff());//no overlapping active tiles or child nodes + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (t->mChildMask.isOn(i)) {// Loop over this node's child nodes + ChildT* child = t->mNodes[i].getChild(); + if (s->mChildMask.isOn(i)) { + child->topologyDifference(*(s->mNodes[i].getChild()), b); + } else if (s->mValueMask.isOn(i)) { + delete child;//convert child to an inactive tile + t->mNodes[i].setValue(b); + } + } else if (t->mValueMask.isOn(i)) {//this is an active tile + if (s->mChildMask.isOn(i)) { + const typename OtherInternalNode::ChildNodeType& other = + *(s->mNodes[i].getChild()); + ChildT* child = new ChildT(other.origin(), t->mNodes[i].getValue(), true); + child->topologyDifference(other, b); + t->mNodes[i].setChild(child);//replace the active tile with a child branch + } + } + } + } + const OtherInternalNode* s; + InternalNode* t; + const ValueType& b; +};// TopologyDifference + +template +template +inline void +InternalNode::topologyDifference(const InternalNode& other, + const ValueType& background) +{ + TopologyDifference > tmp(&other, this, background); +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::combine(InternalNode& other, CombineOp& op) +{ + const ValueType zero = zeroVal(); + + CombineArgs args; + + for (Index i = 0; i < NUM_VALUES; ++i) { + if (this->isChildMaskOff(i) && other.isChildMaskOff(i)) { + // Both this node and the other node have constant values (tiles). + // Combine the two values and store the result as this node's new tile value. + op(args.setARef(mNodes[i].getValue()) + .setAIsActive(isValueMaskOn(i)) + .setBRef(other.mNodes[i].getValue()) + .setBIsActive(other.isValueMaskOn(i))); + mNodes[i].setValue(args.result()); + mValueMask.set(i, args.resultIsActive()); + } else if (this->isChildMaskOn(i) && other.isChildMaskOff(i)) { + // Combine this node's child with the other node's constant value. + ChildNodeType* child = mNodes[i].getChild(); + assert(child); + if (child) { + child->combine(other.mNodes[i].getValue(), other.isValueMaskOn(i), op); + } + } else if (this->isChildMaskOff(i) && other.isChildMaskOn(i)) { + // Combine this node's constant value with the other node's child. + ChildNodeType* child = other.mNodes[i].getChild(); + assert(child); + if (child) { + // Combine this node's constant value with the other node's child, + // but use a new functor in which the A and B values are swapped, + // since the constant value is the A value, not the B value. + SwappedCombineOp swappedOp(op); + child->combine(mNodes[i].getValue(), isValueMaskOn(i), swappedOp); + + // Steal the other node's child. + other.mChildMask.setOff(i); + other.mNodes[i].setValue(zero); + this->setChildNode(i, child); + } + + } else /*if (isChildMaskOn(i) && other.isChildMaskOn(i))*/ { + // Combine this node's child with the other node's child. + ChildNodeType + *child = mNodes[i].getChild(), + *otherChild = other.mNodes[i].getChild(); + assert(child); + assert(otherChild); + if (child && otherChild) { + child->combine(*otherChild, op); + } + } + } +} + + +template +template +inline void +InternalNode::combine(const ValueType& value, bool valueIsActive, CombineOp& op) +{ + CombineArgs args; + + for (Index i = 0; i < NUM_VALUES; ++i) { + if (this->isChildMaskOff(i)) { + // Combine this node's constant value with the given constant value. + op(args.setARef(mNodes[i].getValue()) + .setAIsActive(isValueMaskOn(i)) + .setBRef(value) + .setBIsActive(valueIsActive)); + mNodes[i].setValue(args.result()); + mValueMask.set(i, args.resultIsActive()); + } else /*if (isChildMaskOn(i))*/ { + // Combine this node's child with the given constant value. + ChildNodeType* child = mNodes[i].getChild(); + assert(child); + if (child) child->combine(value, valueIsActive, op); + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::combine2(const InternalNode& other0, const OtherNodeType& other1, + CombineOp& op) +{ + CombineArgs args; + + for (Index i = 0; i < NUM_VALUES; ++i) { + if (other0.isChildMaskOff(i) && other1.isChildMaskOff(i)) { + op(args.setARef(other0.mNodes[i].getValue()) + .setAIsActive(other0.isValueMaskOn(i)) + .setBRef(other1.mNodes[i].getValue()) + .setBIsActive(other1.isValueMaskOn(i))); + // Replace child i with a constant value. + this->makeChildNodeEmpty(i, args.result()); + mValueMask.set(i, args.resultIsActive()); + } else { + if (this->isChildMaskOff(i)) { + // Add a new child with the same coordinates, etc. as the other node's child. + const Coord& childOrigin = other0.isChildMaskOn(i) + ? other0.mNodes[i].getChild()->origin() + : other1.mNodes[i].getChild()->origin(); + this->setChildNode(i, new ChildNodeType(childOrigin, mNodes[i].getValue())); + } + + if (other0.isChildMaskOff(i)) { + // Combine node1's child with node0's constant value + // and write the result into child i. + mNodes[i].getChild()->combine2(other0.mNodes[i].getValue(), + *other1.mNodes[i].getChild(), other0.isValueMaskOn(i), op); + } else if (other1.isChildMaskOff(i)) { + // Combine node0's child with node1's constant value + // and write the result into child i. + mNodes[i].getChild()->combine2(*other0.mNodes[i].getChild(), + other1.mNodes[i].getValue(), other1.isValueMaskOn(i), op); + } else { + // Combine node0's child with node1's child + // and write the result into child i. + mNodes[i].getChild()->combine2(*other0.mNodes[i].getChild(), + *other1.mNodes[i].getChild(), op); + } + } + } +} + + +template +template +inline void +InternalNode::combine2(const ValueType& value, const OtherNodeType& other, + bool valueIsActive, CombineOp& op) +{ + CombineArgs args; + + for (Index i = 0; i < NUM_VALUES; ++i) { + if (other.isChildMaskOff(i)) { + op(args.setARef(value) + .setAIsActive(valueIsActive) + .setBRef(other.mNodes[i].getValue()) + .setBIsActive(other.isValueMaskOn(i))); + // Replace child i with a constant value. + this->makeChildNodeEmpty(i, args.result()); + mValueMask.set(i, args.resultIsActive()); + } else { + typename OtherNodeType::ChildNodeType* otherChild = other.mNodes[i].getChild(); + assert(otherChild); + if (this->isChildMaskOff(i)) { + // Add a new child with the same coordinates, etc. + // as the other node's child. + this->setChildNode(i, new ChildNodeType(*otherChild)); + } + // Combine the other node's child with a constant value + // and write the result into child i. + mNodes[i].getChild()->combine2(value, *otherChild, valueIsActive, op); + } + } +} + + +template +template +inline void +InternalNode::combine2(const InternalNode& other, const OtherValueType& value, + bool valueIsActive, CombineOp& op) +{ + CombineArgs args; + + for (Index i = 0; i < NUM_VALUES; ++i) { + if (other.isChildMaskOff(i)) { + op(args.setARef(other.mNodes[i].getValue()) + .setAIsActive(other.isValueMaskOn(i)) + .setBRef(value) + .setBIsActive(valueIsActive)); + // Replace child i with a constant value. + this->makeChildNodeEmpty(i, args.result()); + mValueMask.set(i, args.resultIsActive()); + } else { + ChildNodeType* otherChild = other.mNodes[i].getChild(); + assert(otherChild); + if (this->isChildMaskOff(i)) { + // Add a new child with the same coordinates, etc. as the other node's child. + this->setChildNode(i, + new ChildNodeType(otherChild->origin(), mNodes[i].getValue())); + } + // Combine the other node's child with a constant value + // and write the result into child i. + mNodes[i].getChild()->combine2(*otherChild, value, valueIsActive, op); + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::visitActiveBBox(BBoxOp& op) const +{ + for (ValueOnCIter i = this->cbeginValueOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(CoordBBox::createCube(i.getCoord(), ChildNodeType::DIM)); +#else + op.template operator()(CoordBBox::createCube(i.getCoord(), ChildNodeType::DIM)); +#endif + } + if (op.template descent()) { + for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) i->visitActiveBBox(op); + } else { + for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(i->getNodeBoundingBox()); +#else + op.template operator()(i->getNodeBoundingBox()); +#endif + } + } +} + + +template +template +inline void +InternalNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +InternalNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +InternalNode::doVisit(NodeT& self, VisitorOp& op) +{ + typename NodeT::ValueType val; + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + if (op(iter)) continue; + if (typename ChildAllIterT::ChildNodeType* child = iter.probeChild(val)) { + child->visit(op); + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::visit2Node(OtherNodeType& other, VisitorOp& op) +{ + doVisit2Node(*this, other, op); +} + + +template +template +inline void +InternalNode::visit2Node(OtherNodeType& other, VisitorOp& op) const +{ + doVisit2Node(*this, other, op); +} + + +template +template< + typename NodeT, + typename OtherNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +InternalNode::doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp& op) +{ + // Allow the two nodes to have different ValueTypes, but not different dimensions. + static_assert(OtherNodeT::NUM_VALUES == NodeT::NUM_VALUES, + "visit2() requires nodes to have the same dimensions"); + static_assert(OtherNodeT::LEVEL == NodeT::LEVEL, + "visit2() requires nodes to be at the same tree level"); + + typename NodeT::ValueType val; + typename OtherNodeT::ValueType otherVal; + + ChildAllIterT iter = self.beginChildAll(); + OtherChildAllIterT otherIter = other.beginChildAll(); + + for ( ; iter && otherIter; ++iter, ++otherIter) + { + const size_t skipBranch = static_cast(op(iter, otherIter)); + + typename ChildAllIterT::ChildNodeType* child = + (skipBranch & 1U) ? nullptr : iter.probeChild(val); + typename OtherChildAllIterT::ChildNodeType* otherChild = + (skipBranch & 2U) ? nullptr : otherIter.probeChild(otherVal); + + if (child != nullptr && otherChild != nullptr) { + child->visit2Node(*otherChild, op); + } else if (child != nullptr) { + child->visit2(otherIter, op); + } else if (otherChild != nullptr) { + otherChild->visit2(iter, op, /*otherIsLHS=*/true); + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::visit2(OtherChildAllIterType& otherIter, + VisitorOp& op, bool otherIsLHS) +{ + doVisit2( + *this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +InternalNode::visit2(OtherChildAllIterType& otherIter, + VisitorOp& op, bool otherIsLHS) const +{ + doVisit2( + *this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +InternalNode::doVisit2(NodeT& self, OtherChildAllIterT& otherIter, + VisitorOp& op, bool otherIsLHS) +{ + if (!otherIter) return; + + const size_t skipBitMask = (otherIsLHS ? 2U : 1U); + + typename NodeT::ValueType val; + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + const size_t skipBranch = static_cast( + otherIsLHS ? op(otherIter, iter) : op(iter, otherIter)); + + typename ChildAllIterT::ChildNodeType* child = + (skipBranch & skipBitMask) ? nullptr : iter.probeChild(val); + + if (child != nullptr) child->visit2(otherIter, op, otherIsLHS); + } +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::writeBuffers(std::ostream& os, bool toHalf) const +{ + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + iter->writeBuffers(os, toHalf); + } +} + + +template +inline void +InternalNode::readBuffers(std::istream& is, bool fromHalf) +{ + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + iter->readBuffers(is, fromHalf); + } +} + + +template +inline void +InternalNode::readBuffers(std::istream& is, + const CoordBBox& clipBBox, bool fromHalf) +{ + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + // Stream in the branch rooted at this child. + // (We can't skip over children that lie outside the clipping region, + // because buffers are serialized in depth-first order and need to be + // unserialized in the same order.) + iter->readBuffers(is, clipBBox, fromHalf); + } + + // Get this tree's background value. + ValueType background = zeroVal(); + if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + this->clip(clipBBox, background); +} + + +//////////////////////////////////////// + + +template +void +InternalNode::getNodeLog2Dims(std::vector& dims) +{ + dims.push_back(Log2Dim); + ChildNodeType::getNodeLog2Dims(dims); +} + + +template +inline void +InternalNode::offsetToLocalCoord(Index n, Coord &xyz) +{ + assert(n<(1<<3*Log2Dim)); + xyz.setX(n >> 2*Log2Dim); + n &= ((1<<2*Log2Dim)-1); + xyz.setY(n >> Log2Dim); + xyz.setZ(n & ((1< +inline Index +InternalNode::coordToOffset(const Coord& xyz) +{ + return (((xyz[0] & (DIM-1u)) >> ChildNodeType::TOTAL) << 2*Log2Dim) + + (((xyz[1] & (DIM-1u)) >> ChildNodeType::TOTAL) << Log2Dim) + + ((xyz[2] & (DIM-1u)) >> ChildNodeType::TOTAL); +} + + +template +inline Coord +InternalNode::offsetToGlobalCoord(Index n) const +{ + Coord local; + this->offsetToLocalCoord(n, local); + local <<= ChildT::TOTAL; + return local + this->origin(); +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::getNodes(ArrayT& array) +{ + using T = typename ArrayT::value_type; + static_assert(std::is_pointer::value, "argument to getNodes() must be a pointer array"); + using ArrayChildT = typename std::conditional< + std::is_const::type>::value, const ChildT, ChildT>::type; + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (std::is_same::value) { + array.push_back(reinterpret_cast(mNodes[iter.pos()].getChild())); + } else { + iter->getNodes(array);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } +} + +template +template +inline void +InternalNode::getNodes(ArrayT& array) const +{ + using T = typename ArrayT::value_type; + static_assert(std::is_pointer::value, "argument to getNodes() must be a pointer array"); + static_assert(std::is_const::type>::value, + "argument to getNodes() must be an array of const node pointers"); + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (std::is_same::value) { + array.push_back(reinterpret_cast(mNodes[iter.pos()].getChild())); + } else { + iter->getNodes(array);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::stealNodes(ArrayT& array, const ValueType& value, bool state) +{ + using T = typename ArrayT::value_type; + static_assert(std::is_pointer::value, "argument to stealNodes() must be a pointer array"); + using ArrayChildT = typename std::conditional< + std::is_const::type>::value, const ChildT, ChildT>::type; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (std::is_same::value) { + array.push_back(reinterpret_cast(mNodes[n].getChild())); + mValueMask.set(n, state); + mNodes[n].setValue(value); + } else { + iter->stealNodes(array, value, state);//descent + } + } + if (std::is_same::value) mChildMask.setOff(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::resetBackground(const ValueType& oldBackground, + const ValueType& newBackground) +{ + if (math::isExactlyEqual(oldBackground, newBackground)) return; + for (Index i = 0; i < NUM_VALUES; ++i) { + if (this->isChildMaskOn(i)) { + mNodes[i].getChild()->resetBackground(oldBackground, newBackground); + } else if (this->isValueMaskOff(i)) { + if (math::isApproxEqual(mNodes[i].getValue(), oldBackground)) { + mNodes[i].setValue(newBackground); + } else if (math::isApproxEqual(mNodes[i].getValue(), math::negative(oldBackground))) { + mNodes[i].setValue(math::negative(newBackground)); + } + } + } +} + +template +template +inline bool +InternalNode::hasSameTopology( + const InternalNode* other) const +{ + if (Log2Dim != OtherLog2Dim || mChildMask != other->mChildMask || + mValueMask != other->mValueMask) return false; + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + if (!iter->hasSameTopology(other->mNodes[iter.pos()].getChild())) return false; + } + return true; +} + + +template +inline void +InternalNode::resetChildNode(Index i, ChildNodeType* child) +{ + assert(child); + if (this->isChildMaskOn(i)) { + delete mNodes[i].getChild(); + } else { + mChildMask.setOn(i); + mValueMask.setOff(i); + } + mNodes[i].setChild(child); +} + +template +inline void +InternalNode::setChildNode(Index i, ChildNodeType* child) +{ + assert(child); + assert(mChildMask.isOff(i)); + mChildMask.setOn(i); + mValueMask.setOff(i); + mNodes[i].setChild(child); +} + + +template +inline ChildT* +InternalNode::unsetChildNode(Index i, const ValueType& value) +{ + if (this->isChildMaskOff(i)) { + mNodes[i].setValue(value); + return nullptr; + } + ChildNodeType* child = mNodes[i].getChild(); + mChildMask.setOff(i); + mNodes[i].setValue(value); + return child; +} + + +template +inline void +InternalNode::makeChildNodeEmpty(Index n, const ValueType& value) +{ + delete this->unsetChildNode(n, value); +} + +template +inline ChildT* +InternalNode::getChildNode(Index n) +{ + assert(this->isChildMaskOn(n)); + return mNodes[n].getChild(); +} + + +template +inline const ChildT* +InternalNode::getChildNode(Index n) const +{ + assert(this->isChildMaskOn(n)); + return mNodes[n].getChild(); +} + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/Iterator.h b/openvdb/tree/Iterator.h new file mode 100644 index 00000000..d3512aa0 --- /dev/null +++ b/openvdb/tree/Iterator.h @@ -0,0 +1,253 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file tree/Iterator.h +/// +/// @author Peter Cucka and Ken Museth + +#ifndef OPENVDB_TREE_ITERATOR_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_ITERATOR_HAS_BEEN_INCLUDED + +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +/// @brief Base class for iterators over internal and leaf nodes +/// +/// This class is typically not instantiated directly, since it doesn't provide methods +/// to dereference the iterator. Those methods (@vdblink::tree::SparseIteratorBase::operator*() +/// operator*()@endlink, @vdblink::tree::SparseIteratorBase::setValue() setValue()@endlink, etc.) +/// are implemented in the @vdblink::tree::SparseIteratorBase sparse@endlink and +/// @vdblink::tree::DenseIteratorBase dense@endlink iterator subclasses. +template +class IteratorBase +{ +public: + IteratorBase(): mParentNode(nullptr) {} + IteratorBase(const MaskIterT& iter, NodeT* parent): mParentNode(parent), mMaskIter(iter) {} + IteratorBase(const IteratorBase&) = default; + IteratorBase& operator=(const IteratorBase&) = default; + + bool operator==(const IteratorBase& other) const + { + return (mParentNode == other.mParentNode) && (mMaskIter == other.mMaskIter); + } + bool operator!=(const IteratorBase& other) const + { + return !(*this == other); + } + + /// Return a pointer to the node (if any) over which this iterator is iterating. + NodeT* getParentNode() const { return mParentNode; } + /// @brief Return a reference to the node over which this iterator is iterating. + /// @throw ValueError if there is no parent node. + NodeT& parent() const + { + if (!mParentNode) OPENVDB_THROW(ValueError, "iterator references a null node"); + return *mParentNode; + } + + /// Return this iterator's position as an index into the parent node's table. + Index offset() const { return mMaskIter.offset(); } + + /// Identical to offset + Index pos() const { return mMaskIter.offset(); } + + /// Return @c true if this iterator is not yet exhausted. + bool test() const { return mMaskIter.test(); } + /// Return @c true if this iterator is not yet exhausted. + operator bool() const { return this->test(); } + + /// Advance to the next item in the parent node's table. + bool next() { return mMaskIter.next(); } + /// Advance to the next item in the parent node's table. + void increment() { mMaskIter.increment(); } + /// Advance to the next item in the parent node's table. + IteratorBase& operator++() { this->increment(); return *this; } + /// Advance @a n items in the parent node's table. + void increment(Index n) { mMaskIter.increment(n); } + + /// @brief Return @c true if this iterator is pointing to an active value. + /// Return @c false if it is pointing to either an inactive value or a child node. + bool isValueOn() const { return parent().isValueMaskOn(this->pos()); } + /// @brief If this iterator is pointing to a value, set the value's active state. + /// Otherwise, do nothing. + void setValueOn(bool on = true) const { parent().setValueMask(this->pos(), on); } + /// @brief If this iterator is pointing to a value, mark the value as inactive. + /// @details If this iterator is pointing to a child node, then the current item + /// in the parent node's table is required to be inactive. In that case, + /// this method has no effect. + void setValueOff() const { parent().mValueMask.setOff(this->pos()); } + + /// Return the coordinates of the item to which this iterator is pointing. + Coord getCoord() const { return parent().offsetToGlobalCoord(this->pos()); } + /// Return in @a xyz the coordinates of the item to which this iterator is pointing. + void getCoord(Coord& xyz) const { xyz = this->getCoord(); } + +private: + /// @note This parent node pointer is mutable, because setValueOn() and + /// setValueOff(), though const, need to call non-const methods on the parent. + /// There is a distinction between a const iterator (e.g., const ValueOnIter), + /// which is an iterator that can't be incremented, and an iterator over + /// a const node (e.g., ValueOnCIter), which might be const or non-const itself + /// but can't call non-const methods like setValue() on the node. + mutable NodeT* mParentNode; + MaskIterT mMaskIter; +}; // class IteratorBase + + +//////////////////////////////////////// + + +/// @brief Base class for sparse iterators over internal and leaf nodes +template< + typename MaskIterT, // mask iterator type (OnIterator, OffIterator, etc.) + typename IterT, // SparseIteratorBase subclass (the "Curiously Recurring Template Pattern") + typename NodeT, // type of node over which to iterate + typename ItemT> // type of value to which this iterator points +struct SparseIteratorBase: public IteratorBase +{ + using NodeType = NodeT; + using ValueType = ItemT; + using NonConstNodeType = typename std::remove_const::type; + using NonConstValueType = typename std::remove_const::type; + static const bool IsSparseIterator = true, IsDenseIterator = false; + + SparseIteratorBase() {} + SparseIteratorBase(const MaskIterT& iter, NodeT* parent): + IteratorBase(iter, parent) {} + + /// @brief Return the item at the given index in the parent node's table. + /// @note All subclasses must implement this accessor. + ItemT& getItem(Index) const; + /// @brief Set the value of the item at the given index in the parent node's table. + /// @note All non-const iterator subclasses must implement this accessor. + void setItem(Index, const ItemT&) const; + + /// Return a reference to the item to which this iterator is pointing. + ItemT& operator*() const { return this->getValue(); } + /// Return a pointer to the item to which this iterator is pointing. + ItemT* operator->() const { return &(this->operator*()); } + + /// Return the item to which this iterator is pointing. + ItemT& getValue() const + { + return static_cast(this)->getItem(this->pos()); // static polymorphism + } + /// @brief Set the value of the item to which this iterator is pointing. + /// (Not valid for const iterators.) + void setValue(const ItemT& value) const + { + static_assert(!std::is_const::value, "setValue() not allowed for const iterators"); + static_cast(this)->setItem(this->pos(), value); // static polymorphism + } + /// @brief Apply a functor to the item to which this iterator is pointing. + /// (Not valid for const iterators.) + /// @param op a functor of the form void op(ValueType&) const that modifies + /// its argument in place + /// @see Tree::modifyValue() + template + void modifyValue(const ModifyOp& op) const + { + static_assert(!std::is_const::value, + "modifyValue() not allowed for const iterators"); + static_cast(this)->modifyItem(this->pos(), op); // static polymorphism + } +}; // class SparseIteratorBase + + +//////////////////////////////////////// + + +/// @brief Base class for dense iterators over internal and leaf nodes +/// @note Dense iterators have no @c %operator*() or @c %operator->(), +/// because their return type would have to vary depending on whether +/// the iterator is pointing to a value or a child node. +template< + typename MaskIterT, // mask iterator type (typically a DenseIterator) + typename IterT, // DenseIteratorBase subclass (the "Curiously Recurring Template Pattern") + typename NodeT, // type of node over which to iterate + typename SetItemT, // type of set value (ChildNodeType, for non-leaf nodes) + typename UnsetItemT> // type of unset value (ValueType, usually) +struct DenseIteratorBase: public IteratorBase +{ + using NodeType = NodeT; + using ValueType = UnsetItemT; + using ChildNodeType = SetItemT; + using NonConstNodeType = typename std::remove_const::type; + using NonConstValueType = typename std::remove_const::type; + using NonConstChildNodeType = typename std::remove_const::type; + static const bool IsSparseIterator = false, IsDenseIterator = true; + + DenseIteratorBase() {} + DenseIteratorBase(const MaskIterT& iter, NodeT* parent): + IteratorBase(iter, parent) {} + + /// @brief Return @c true if the item at the given index in the parent node's table + /// is a set value and return either the set value in @a child or the unset value + /// in @a value. + /// @note All subclasses must implement this accessor. + bool getItem(Index, SetItemT*& child, NonConstValueType& value) const; + /// @brief Set the value of the item at the given index in the parent node's table. + /// @note All non-const iterator subclasses must implement this accessor. + void setItem(Index, SetItemT*) const; + /// @brief "Unset" the value of the item at the given index in the parent node's table. + /// @note All non-const iterator subclasses must implement this accessor. + void unsetItem(Index, const UnsetItemT&) const; + + /// Return @c true if this iterator is pointing to a child node. + bool isChildNode() const { return this->parent().isChildMaskOn(this->pos()); } + + /// @brief If this iterator is pointing to a child node, return a pointer to the node. + /// Otherwise, return nullptr and, in @a value, the value to which this iterator is pointing. + SetItemT* probeChild(NonConstValueType& value) const + { + SetItemT* child = nullptr; + static_cast(this)->getItem(this->pos(), child, value); // static polymorphism + return child; + } + /// @brief If this iterator is pointing to a child node, return @c true and return + /// a pointer to the child node in @a child. Otherwise, return @c false and return + /// the value to which this iterator is pointing in @a value. + bool probeChild(SetItemT*& child, NonConstValueType& value) const + { + child = probeChild(value); + return (child != nullptr); + } + + /// @brief Return @c true if this iterator is pointing to a value and return + /// the value in @a value. Otherwise, return @c false. + bool probeValue(NonConstValueType& value) const + { + SetItemT* child = nullptr; + const bool isChild = static_cast(this)-> // static polymorphism + getItem(this->pos(), child, value); + return !isChild; + } + + /// @brief Replace with the given child node the item in the parent node's table + /// to which this iterator is pointing. + void setChild(SetItemT* child) const + { + static_cast(this)->setItem(this->pos(), child); // static polymorphism + } + + /// @brief Replace with the given value the item in the parent node's table + /// to which this iterator is pointing. + void setValue(const UnsetItemT& value) const + { + static_cast(this)->unsetItem(this->pos(), value); // static polymorphism + } +}; // struct DenseIteratorBase + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_ITERATOR_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/LeafBuffer.h b/openvdb/tree/LeafBuffer.h new file mode 100644 index 00000000..40ec75ef --- /dev/null +++ b/openvdb/tree/LeafBuffer.h @@ -0,0 +1,492 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_TREE_LEAFBUFFER_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_LEAFBUFFER_HAS_BEEN_INCLUDED + +#include +#include // for io::readCompressedValues(), etc +#include +#include +#include +#include // for std::swap +#include // for offsetof() +#include +#include +#include + + +class TestLeaf; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +namespace internal { + +/// @internal For delayed loading to be threadsafe, LeafBuffer::mOutOfCore must be +/// memory-fenced when it is set in LeafBuffer::doLoad(), otherwise that operation +/// could be reordered ahead of others in doLoad(), with the possible result that +/// other threads could see the buffer as in-core before it has been fully loaded. +/// Making mOutOfCore a TBB atomic solves the problem, since TBB atomics are release-fenced +/// by default (unlike STL atomics, which are not even guaranteed to be lock-free). +/// However, TBB atomics have stricter alignment requirements than their underlying value_types, +/// so a LeafBuffer with an atomic mOutOfCore is potentially ABI-incompatible with +/// its non-atomic counterpart. +/// This helper class conditionally declares mOutOfCore as an atomic only if doing so +/// doesn't break ABI compatibility. +template +struct LeafBufferFlags +{ +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + /// The type of LeafBuffer::mOutOfCore + using type = tbb::atomic; + static constexpr bool IsAtomic = true; +#else // OPENVDB_ABI_VERSION_NUMBER < 5 + // These structs need to have the same data members as LeafBuffer. + struct Atomic { union { T* data; void* ptr; }; tbb::atomic i; tbb::spin_mutex mutex; }; + struct NonAtomic { union { T* data; void* ptr; }; Index32 i; tbb::spin_mutex mutex; }; + +#ifndef __INTEL_COMPILER + /// @c true if LeafBuffer::mOutOfCore is atomic, @c false otherwise + static constexpr bool IsAtomic = ((sizeof(Atomic) == sizeof(NonAtomic)) + && (offsetof(Atomic, i) == offsetof(NonAtomic, i))); +#else + // We can't use offsetof() with ICC, because it requires the arguments + // to be POD types. (C++11 requires only that they be standard layout types, + // which Atomic and NonAtomic are.) + static constexpr bool IsAtomic = (sizeof(Atomic) == sizeof(NonAtomic)); +#endif + /// The size of a LeafBuffer when LeafBuffer::mOutOfCore is atomic + static constexpr size_t size = sizeof(Atomic); + /// The type of LeafBuffer::mOutOfCore + using type = typename std::conditional, Index32>::type; +#endif +}; + +} // namespace internal + + +/// @brief Array of fixed size 23Log2Dim that stores +/// the voxel values of a LeafNode +template +class LeafBuffer +{ +public: + using ValueType = T; + using StorageType = ValueType; + using NodeMaskType = util::NodeMask; + static const Index SIZE = 1 << 3 * Log2Dim; + + struct FileInfo + { + FileInfo(): bufpos(0) , maskpos(0) {} + std::streamoff bufpos; + std::streamoff maskpos; + io::MappedFile::Ptr mapping; + SharedPtr meta; + }; + + /// Default constructor + inline LeafBuffer(): mData(new ValueType[SIZE]) { mOutOfCore = 0; } + /// Construct a buffer populated with the specified value. + explicit inline LeafBuffer(const ValueType&); + /// Copy constructor + inline LeafBuffer(const LeafBuffer&); + /// Construct a buffer but don't allocate memory for the full array of values. + LeafBuffer(PartialCreate, const ValueType&): mData(nullptr) { mOutOfCore = 0; } + /// Destructor + inline ~LeafBuffer(); + + /// Return @c true if this buffer's values have not yet been read from disk. + bool isOutOfCore() const { return bool(mOutOfCore); } + /// Return @c true if memory for this buffer has not yet been allocated. + bool empty() const { return !mData || this->isOutOfCore(); } + /// Allocate memory for this buffer if it has not already been allocated. + bool allocate() { if (mData == nullptr) mData = new ValueType[SIZE]; return true; } + + /// Populate this buffer with a constant value. + inline void fill(const ValueType&); + + /// Return a const reference to the i'th element of this buffer. + const ValueType& getValue(Index i) const { return this->at(i); } + /// Return a const reference to the i'th element of this buffer. + const ValueType& operator[](Index i) const { return this->at(i); } + /// Set the i'th value of this buffer to the specified value. + inline void setValue(Index i, const ValueType&); + + /// Copy the other buffer's values into this buffer. + inline LeafBuffer& operator=(const LeafBuffer&); + + /// @brief Return @c true if the contents of the other buffer + /// exactly equal the contents of this buffer. + inline bool operator==(const LeafBuffer&) const; + /// @brief Return @c true if the contents of the other buffer + /// are not exactly equal to the contents of this buffer. + inline bool operator!=(const LeafBuffer& other) const { return !(other == *this); } + + /// Exchange this buffer's values with the other buffer's values. + inline void swap(LeafBuffer&); + + /// Return the memory footprint of this buffer in bytes. + inline Index memUsage() const; + /// Return the number of values contained in this buffer. + static Index size() { return SIZE; } + + /// @brief Return a const pointer to the array of voxel values. + /// @details This method guarantees that the buffer is allocated and loaded. + /// @warning This method should only be used by experts seeking low-level optimizations. + const ValueType* data() const; + /// @brief Return a pointer to the array of voxel values. + /// @details This method guarantees that the buffer is allocated and loaded. + /// @warning This method should only be used by experts seeking low-level optimizations. + ValueType* data(); + +private: + /// If this buffer is empty, return zero, otherwise return the value at index @ i. + inline const ValueType& at(Index i) const; + + /// @brief Return a non-const reference to the value at index @a i. + /// @details This method is private since it makes assumptions about the + /// buffer's memory layout. LeafBuffers associated with custom leaf node types + /// (e.g., a bool buffer implemented as a bitmask) might not be able to + /// return non-const references to their values. + ValueType& operator[](Index i) { return const_cast(this->at(i)); } + + bool deallocate(); + + inline void setOutOfCore(bool b) { mOutOfCore = b; } + // To facilitate inlining in the common case in which the buffer is in-core, + // the loading logic is split into a separate function, doLoad(). + inline void loadValues() const { if (this->isOutOfCore()) this->doLoad(); } + inline void doLoad() const; + inline bool detachFromFile(); + + using FlagsType = typename internal::LeafBufferFlags::type; + + union { + ValueType* mData; + FileInfo* mFileInfo; + }; + FlagsType mOutOfCore; // interpreted as bool; extra bits reserved for future use + tbb::spin_mutex mMutex; // 1 byte + //int8_t mReserved[3]; // padding for alignment + + static const ValueType sZero; + + friend class ::TestLeaf; + // Allow the parent LeafNode to access this buffer's data pointer. + template friend class LeafNode; +}; // class LeafBuffer + + +//////////////////////////////////////// + + +template +const T LeafBuffer::sZero = zeroVal(); + + +template +inline +LeafBuffer::LeafBuffer(const ValueType& val) + : mData(new ValueType[SIZE]) +{ + mOutOfCore = 0; + this->fill(val); +} + + +template +inline +LeafBuffer::~LeafBuffer() +{ + if (this->isOutOfCore()) { + this->detachFromFile(); + } else { + this->deallocate(); + } +} + + +template +inline +LeafBuffer::LeafBuffer(const LeafBuffer& other) + : mData(nullptr) + , mOutOfCore(other.mOutOfCore) +{ + if (other.isOutOfCore()) { + mFileInfo = new FileInfo(*other.mFileInfo); + } else if (other.mData != nullptr) { + this->allocate(); + ValueType* target = mData; + const ValueType* source = other.mData; + Index n = SIZE; + while (n--) *target++ = *source++; + } +} + + +template +inline void +LeafBuffer::setValue(Index i, const ValueType& val) +{ + assert(i < SIZE); + this->loadValues(); + if (mData) mData[i] = val; +} + + +template +inline LeafBuffer& +LeafBuffer::operator=(const LeafBuffer& other) +{ + if (&other != this) { + if (this->isOutOfCore()) { + this->detachFromFile(); + } else { + if (other.isOutOfCore()) this->deallocate(); + } + if (other.isOutOfCore()) { + mOutOfCore = other.mOutOfCore; + mFileInfo = new FileInfo(*other.mFileInfo); + } else if (other.mData != nullptr) { + this->allocate(); + ValueType* target = mData; + const ValueType* source = other.mData; + Index n = SIZE; + while (n--) *target++ = *source++; + } + } + return *this; +} + + +template +inline void +LeafBuffer::fill(const ValueType& val) +{ + this->detachFromFile(); + if (mData != nullptr) { + ValueType* target = mData; + Index n = SIZE; + while (n--) *target++ = val; + } +} + + +template +inline bool +LeafBuffer::operator==(const LeafBuffer& other) const +{ + this->loadValues(); + other.loadValues(); + const ValueType *target = mData, *source = other.mData; + if (!target && !source) return true; + if (!target || !source) return false; + Index n = SIZE; + while (n && math::isExactlyEqual(*target++, *source++)) --n; + return n == 0; +} + + +template +inline void +LeafBuffer::swap(LeafBuffer& other) +{ + std::swap(mData, other.mData); + std::swap(mOutOfCore, other.mOutOfCore); +} + + +template +inline Index +LeafBuffer::memUsage() const +{ + size_t n = sizeof(*this); + if (this->isOutOfCore()) n += sizeof(FileInfo); + else if (mData) n += SIZE * sizeof(ValueType); + return static_cast(n); +} + + +template +inline const typename LeafBuffer::ValueType* +LeafBuffer::data() const +{ + this->loadValues(); + if (mData == nullptr) { + LeafBuffer* self = const_cast(this); + // This lock will be contended at most once. + tbb::spin_mutex::scoped_lock lock(self->mMutex); + if (mData == nullptr) self->mData = new ValueType[SIZE]; + } + return mData; +} + +template +inline typename LeafBuffer::ValueType* +LeafBuffer::data() +{ + this->loadValues(); + if (mData == nullptr) { + // This lock will be contended at most once. + tbb::spin_mutex::scoped_lock lock(mMutex); + if (mData == nullptr) mData = new ValueType[SIZE]; + } + return mData; +} + + +template +inline const typename LeafBuffer::ValueType& +LeafBuffer::at(Index i) const +{ + assert(i < SIZE); + this->loadValues(); + // We can't use the ternary operator here, otherwise Visual C++ returns + // a reference to a temporary. + if (mData) return mData[i]; else return sZero; +} + + +template +inline bool +LeafBuffer::deallocate() +{ + if (mData != nullptr && !this->isOutOfCore()) { + delete[] mData; + mData = nullptr; + return true; + } + return false; +} + + +template +inline void +LeafBuffer::doLoad() const +{ + if (!this->isOutOfCore()) return; + + LeafBuffer* self = const_cast*>(this); + + // This lock will be contended at most once, after which this buffer + // will no longer be out-of-core. + tbb::spin_mutex::scoped_lock lock(self->mMutex); + if (!this->isOutOfCore()) return; + + std::unique_ptr info(self->mFileInfo); + assert(info.get() != nullptr); + assert(info->mapping.get() != nullptr); + assert(info->meta.get() != nullptr); + + /// @todo For now, we have to clear the mData pointer in order for allocate() to take effect. + self->mData = nullptr; + self->allocate(); + + SharedPtr buf = info->mapping->createBuffer(); + std::istream is(buf.get()); + + io::setStreamMetadataPtr(is, info->meta, /*transfer=*/true); + + NodeMaskType mask; + is.seekg(info->maskpos); + mask.load(is); + + is.seekg(info->bufpos); + io::readCompressedValues(is, self->mData, SIZE, mask, io::getHalfFloat(is)); + + self->setOutOfCore(false); +} + + +template +inline bool +LeafBuffer::detachFromFile() +{ + if (this->isOutOfCore()) { + delete mFileInfo; + mFileInfo = nullptr; + this->setOutOfCore(false); + return true; + } + return false; +} + + +//////////////////////////////////////// + + +// Partial specialization for bool ValueType +template +class LeafBuffer +{ +public: + using NodeMaskType = util::NodeMask; + using WordType = typename NodeMaskType::Word; + using ValueType = bool; + using StorageType = WordType; + + static const Index WORD_COUNT = NodeMaskType::WORD_COUNT; + static const Index SIZE = 1 << 3 * Log2Dim; + + // These static declarations must be on separate lines to avoid VC9 compiler errors. + static const bool sOn; + static const bool sOff; + + LeafBuffer() {} + LeafBuffer(bool on): mData(on) {} + LeafBuffer(const NodeMaskType& other): mData(other) {} + LeafBuffer(const LeafBuffer& other): mData(other.mData) {} + ~LeafBuffer() {} + void fill(bool val) { mData.set(val); } + LeafBuffer& operator=(const LeafBuffer& b) { if (&b != this) { mData=b.mData; } return *this; } + + const bool& getValue(Index i) const + { + assert(i < SIZE); + // We can't use the ternary operator here, otherwise Visual C++ returns + // a reference to a temporary. + if (mData.isOn(i)) return sOn; else return sOff; + } + const bool& operator[](Index i) const { return this->getValue(i); } + + bool operator==(const LeafBuffer& other) const { return mData == other.mData; } + bool operator!=(const LeafBuffer& other) const { return mData != other.mData; } + + void setValue(Index i, bool val) { assert(i < SIZE); mData.set(i, val); } + + void swap(LeafBuffer& other) { if (&other != this) std::swap(mData, other.mData); } + + Index memUsage() const { return sizeof(*this); } + static Index size() { return SIZE; } + + /// @brief Return a pointer to the C-style array of words encoding the bits. + /// @warning This method should only be used by experts seeking low-level optimizations. + WordType* data() { return &(mData.template getWord(0)); } + /// @brief Return a const pointer to the C-style array of words encoding the bits. + /// @warning This method should only be used by experts seeking low-level optimizations. + const WordType* data() const { return const_cast(this)->data(); } + +private: + // Allow the parent LeafNode to access this buffer's data. + template friend class LeafNode; + + NodeMaskType mData; +}; // class LeafBuffer + + +/// @internal For consistency with other nodes and with iterators, methods like +/// LeafNode::getValue() return a reference to a value. Since it's not possible +/// to return a reference to a bit in a node mask, we return a reference to one +/// of the following static values instead. +template const bool LeafBuffer::sOn = true; +template const bool LeafBuffer::sOff = false; + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_LEAFBUFFER_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/LeafManager.h b/openvdb/tree/LeafManager.h new file mode 100644 index 00000000..06b91365 --- /dev/null +++ b/openvdb/tree/LeafManager.h @@ -0,0 +1,822 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file LeafManager.h +/// +/// @brief A LeafManager manages a linear array of pointers to a given tree's +/// leaf nodes, as well as optional auxiliary buffers (one or more per leaf) +/// that can be swapped with the leaf nodes' voxel data buffers. +/// @details The leaf array is useful for multithreaded computations over +/// leaf voxels in a tree with static topology but varying voxel values. +/// The auxiliary buffers are convenient for temporal integration. +/// Efficient methods are provided for multithreaded swapping and synching +/// (i.e., copying the contents) of these buffers. + +#ifndef OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +namespace leafmgr { + +//@{ +/// Useful traits for Tree types +template struct TreeTraits { + static const bool IsConstTree = false; + using LeafIterType = typename TreeT::LeafIter; +}; +template struct TreeTraits { + static const bool IsConstTree = true; + using LeafIterType = typename TreeT::LeafCIter; +}; +//@} + +} // namespace leafmgr + + +/// This helper class implements LeafManager methods that need to be +/// specialized for const vs. non-const trees. +template +struct LeafManagerImpl +{ + using RangeT = typename ManagerT::RangeType; + using LeafT = typename ManagerT::LeafType; + using BufT = typename ManagerT::BufferType; + + static inline void doSwapLeafBuffer(const RangeT& r, size_t auxBufferIdx, + LeafT** leafs, BufT* bufs, size_t bufsPerLeaf) + { + for (size_t n = r.begin(), m = r.end(), N = bufsPerLeaf; n != m; ++n) { + leafs[n]->swap(bufs[n * N + auxBufferIdx]); + } + } +}; + + +//////////////////////////////////////// + + +/// @brief This class manages a linear array of pointers to a given tree's +/// leaf nodes, as well as optional auxiliary buffers (one or more per leaf) +/// that can be swapped with the leaf nodes' voxel data buffers. +/// @details The leaf array is useful for multithreaded computations over +/// leaf voxels in a tree with static topology but varying voxel values. +/// The auxiliary buffers are convenient for temporal integration. +/// Efficient methods are provided for multithreaded swapping and sync'ing +/// (i.e., copying the contents) of these buffers. +/// +/// @note Buffer index 0 denotes a leaf node's internal voxel data buffer. +/// Any auxiliary buffers are indexed starting from one. +template +class LeafManager +{ +public: + using TreeType = TreeT; + using ValueType = typename TreeT::ValueType; + using RootNodeType = typename TreeT::RootNodeType; + using NonConstLeafType = typename TreeType::LeafNodeType; + using LeafType = typename CopyConstness::Type; + using LeafNodeType = LeafType; + using LeafIterType = typename leafmgr::TreeTraits::LeafIterType; + using NonConstBufferType = typename LeafType::Buffer; + using BufferType = typename CopyConstness::Type; + using RangeType = tbb::blocked_range; // leaf index range + static const Index DEPTH = 2; // root + leaf nodes + + static const bool IsConstTree = leafmgr::TreeTraits::IsConstTree; + + class LeafRange + { + public: + class Iterator + { + public: + Iterator(const LeafRange& range, size_t pos): mRange(range), mPos(pos) + { + assert(this->isValid()); + } + Iterator(const Iterator&) = default; + Iterator& operator=(const Iterator&) = default; + /// Advance to the next leaf node. + Iterator& operator++() { ++mPos; return *this; } + /// Return a reference to the leaf node to which this iterator is pointing. + LeafType& operator*() const { return mRange.mLeafManager.leaf(mPos); } + /// Return a pointer to the leaf node to which this iterator is pointing. + LeafType* operator->() const { return &(this->operator*()); } + /// @brief Return the nth buffer for the leaf node to which this iterator is pointing, + /// where n = @a bufferIdx and n = 0 corresponds to the leaf node's own buffer. + BufferType& buffer(size_t bufferIdx) + { + return mRange.mLeafManager.getBuffer(mPos, bufferIdx); + } + /// Return the index into the leaf array of the current leaf node. + size_t pos() const { return mPos; } + /// Return @c true if the position of this iterator is in a valid range. + bool isValid() const { return mPos>=mRange.mBegin && mPos<=mRange.mEnd; } + /// Return @c true if this iterator is not yet exhausted. + bool test() const { return mPos < mRange.mEnd; } + /// Return @c true if this iterator is not yet exhausted. + operator bool() const { return this->test(); } + /// Return @c true if this iterator is exhausted. + bool empty() const { return !this->test(); } + bool operator!=(const Iterator& other) const + { + return (mPos != other.mPos) || (&mRange != &other.mRange); + } + bool operator==(const Iterator& other) const { return !(*this != other); } + const LeafRange& leafRange() const { return mRange; } + + private: + const LeafRange& mRange; + size_t mPos; + };// end Iterator + + LeafRange(size_t begin, size_t end, const LeafManager& leafManager, size_t grainSize=1) + : mEnd(end) + , mBegin(begin) + , mGrainSize(grainSize) + , mLeafManager(leafManager) + { + } + + Iterator begin() const {return Iterator(*this, mBegin);} + + Iterator end() const {return Iterator(*this, mEnd);} + + size_t size() const { return mEnd - mBegin; } + + size_t grainsize() const { return mGrainSize; } + + const LeafManager& leafManager() const { return mLeafManager; } + + bool empty() const {return !(mBegin < mEnd);} + + bool is_divisible() const {return mGrainSize < this->size();} + + LeafRange(LeafRange& r, tbb::split) + : mEnd(r.mEnd) + , mBegin(doSplit(r)) + , mGrainSize(r.mGrainSize) + , mLeafManager(r.mLeafManager) + { + } + + private: + size_t mEnd, mBegin, mGrainSize; + const LeafManager& mLeafManager; + + static size_t doSplit(LeafRange& r) + { + assert(r.is_divisible()); + size_t middle = r.mBegin + (r.mEnd - r.mBegin) / 2u; + r.mEnd = middle; + return middle; + } + };// end of LeafRange + + /// @brief Constructor from a tree reference and an auxiliary buffer count + /// @note The default is no auxiliary buffers + LeafManager(TreeType& tree, size_t auxBuffersPerLeaf=0, bool serial=false) + : mTree(&tree) + , mLeafCount(0) + , mAuxBufferCount(0) + , mAuxBuffersPerLeaf(auxBuffersPerLeaf) + , mLeafs(nullptr) + , mAuxBuffers(nullptr) + , mTask(nullptr) + , mIsMaster(true) + { + this->rebuild(serial); + } + + /// @brief Construct directly from an existing array of leafnodes. + /// @warning The leafnodes are implicitly assumed to exist in the + /// input @a tree. + LeafManager(TreeType& tree, LeafType** begin, LeafType** end, + size_t auxBuffersPerLeaf=0, bool serial=false) + : mTree(&tree) + , mLeafCount(end-begin) + , mAuxBufferCount(0) + , mAuxBuffersPerLeaf(auxBuffersPerLeaf) + , mLeafs(new LeafType*[mLeafCount]) + , mAuxBuffers(nullptr) + , mTask(nullptr) + , mIsMaster(true) + { + size_t n = mLeafCount; + LeafType **target = mLeafs, **source = begin; + while (n--) *target++ = *source++; + if (auxBuffersPerLeaf) this->initAuxBuffers(serial); + } + + /// Shallow copy constructor called by tbb::parallel_for() threads + /// + /// @note This should never get called directly + LeafManager(const LeafManager& other) + : mTree(other.mTree) + , mLeafCount(other.mLeafCount) + , mAuxBufferCount(other.mAuxBufferCount) + , mAuxBuffersPerLeaf(other.mAuxBuffersPerLeaf) + , mLeafs(other.mLeafs) + , mAuxBuffers(other.mAuxBuffers) + , mTask(other.mTask) + , mIsMaster(false) + { + } + + virtual ~LeafManager() + { + if (mIsMaster) { + delete [] mLeafs; + delete [] mAuxBuffers; + } + } + + /// @brief (Re)initialize by resizing (if necessary) and repopulating the leaf array + /// and by deleting existing auxiliary buffers and allocating new ones. + /// @details Call this method if the tree's topology, and therefore the number + /// of leaf nodes, changes. New auxiliary buffers are initialized with copies + /// of corresponding leaf node buffers. + void rebuild(bool serial=false) + { + this->initLeafArray(); + this->initAuxBuffers(serial); + } + //@{ + /// Repopulate the leaf array and delete and reallocate auxiliary buffers. + void rebuild(size_t auxBuffersPerLeaf, bool serial=false) + { + mAuxBuffersPerLeaf = auxBuffersPerLeaf; + this->rebuild(serial); + } + void rebuild(TreeType& tree, bool serial=false) + { + mTree = &tree; + this->rebuild(serial); + } + void rebuild(TreeType& tree, size_t auxBuffersPerLeaf, bool serial=false) + { + mTree = &tree; + mAuxBuffersPerLeaf = auxBuffersPerLeaf; + this->rebuild(serial); + } + //@} + /// @brief Change the number of auxiliary buffers. + /// @details If auxBuffersPerLeaf is 0, all existing auxiliary buffers are deleted. + /// New auxiliary buffers are initialized with copies of corresponding leaf node buffers. + /// This method does not rebuild the leaf array. + void rebuildAuxBuffers(size_t auxBuffersPerLeaf, bool serial=false) + { + mAuxBuffersPerLeaf = auxBuffersPerLeaf; + this->initAuxBuffers(serial); + } + /// @brief Remove the auxiliary buffers, but don't rebuild the leaf array. + void removeAuxBuffers() { this->rebuildAuxBuffers(0); } + + /// @brief Remove the auxiliary buffers and rebuild the leaf array. + void rebuildLeafArray() + { + this->removeAuxBuffers(); + this->initLeafArray(); + } + + /// @brief Return the total number of allocated auxiliary buffers. + size_t auxBufferCount() const { return mAuxBufferCount; } + /// @brief Return the number of auxiliary buffers per leaf node. + size_t auxBuffersPerLeaf() const { return mAuxBuffersPerLeaf; } + + /// @brief Return the number of leaf nodes. + size_t leafCount() const { return mLeafCount; } + + /// @brief Return the number of active voxels in the leaf nodes. + /// @note Multi-threaded for better performance than Tree::activeLeafVoxelCount + Index64 activeLeafVoxelCount() const + { + return tbb::parallel_reduce(this->leafRange(), Index64(0), + [] (const LeafRange& range, Index64 sum) -> Index64 { + for (const auto& leaf: range) { sum += leaf.onVoxelCount(); } + return sum; + }, + [] (Index64 n, Index64 m) -> Index64 { return n + m; }); + } + + /// Return a const reference to tree associated with this manager. + const TreeType& tree() const { return *mTree; } + + /// Return a reference to the tree associated with this manager. + TreeType& tree() { return *mTree; } + + /// Return a const reference to root node associated with this manager. + const RootNodeType& root() const { return mTree->root(); } + + /// Return a reference to the root node associated with this manager. + RootNodeType& root() { return mTree->root(); } + + /// Return @c true if the tree associated with this manager is immutable. + bool isConstTree() const { return this->IsConstTree; } + + /// @brief Return a pointer to the leaf node at index @a leafIdx in the array. + /// @note For performance reasons no range check is performed (other than an assertion)! + LeafType& leaf(size_t leafIdx) const { assert(leafIdx 0), + /// but it is not safe to modify the leaf buffer (@a bufferIdx = 0). + BufferType& getBuffer(size_t leafIdx, size_t bufferIdx) const + { + assert(leafIdx < mLeafCount); + assert(bufferIdx == 0 || bufferIdx - 1 < mAuxBuffersPerLeaf); + return bufferIdx == 0 ? mLeafs[leafIdx]->buffer() + : mAuxBuffers[leafIdx * mAuxBuffersPerLeaf + bufferIdx - 1]; + } + + /// @brief Return a @c tbb::blocked_range of leaf array indices. + /// + /// @note Consider using leafRange() instead, which provides access methods + /// to leaf nodes and buffers. + RangeType getRange(size_t grainsize = 1) const { return RangeType(0, mLeafCount, grainsize); } + + /// Return a TBB-compatible LeafRange. + LeafRange leafRange(size_t grainsize = 1) const + { + return LeafRange(0, mLeafCount, *this, grainsize); + } + + /// @brief Swap each leaf node's buffer with the nth corresponding auxiliary buffer, + /// where n = @a bufferIdx. + /// @return @c true if the swap was successful + /// @param bufferIdx index of the buffer that will be swapped with + /// the corresponding leaf node buffer + /// @param serial if false, swap buffers in parallel using multiple threads. + /// @note Recall that the indexing of auxiliary buffers is 1-based, since + /// buffer index 0 denotes the leaf node buffer. So buffer index 1 denotes + /// the first auxiliary buffer. + bool swapLeafBuffer(size_t bufferIdx, bool serial = false) + { + namespace ph = std::placeholders; + if (bufferIdx == 0 || bufferIdx > mAuxBuffersPerLeaf || this->isConstTree()) return false; + mTask = std::bind(&LeafManager::doSwapLeafBuffer, ph::_1, ph::_2, bufferIdx - 1); + this->cook(serial ? 0 : 512); + return true;//success + } + /// @brief Swap any two buffers for each leaf node. + /// @note Recall that the indexing of auxiliary buffers is 1-based, since + /// buffer index 0 denotes the leaf node buffer. So buffer index 1 denotes + /// the first auxiliary buffer. + bool swapBuffer(size_t bufferIdx1, size_t bufferIdx2, bool serial = false) + { + namespace ph = std::placeholders; + const size_t b1 = std::min(bufferIdx1, bufferIdx2); + const size_t b2 = std::max(bufferIdx1, bufferIdx2); + if (b1 == b2 || b2 > mAuxBuffersPerLeaf) return false; + if (b1 == 0) { + if (this->isConstTree()) return false; + mTask = std::bind(&LeafManager::doSwapLeafBuffer, ph::_1, ph::_2, b2-1); + } else { + mTask = std::bind(&LeafManager::doSwapAuxBuffer, ph::_1, ph::_2, b1-1, b2-1); + } + this->cook(serial ? 0 : 512); + return true;//success + } + + /// @brief Sync up the specified auxiliary buffer with the corresponding leaf node buffer. + /// @return @c true if the sync was successful + /// @param bufferIdx index of the buffer that will contain a + /// copy of the corresponding leaf node buffer + /// @param serial if false, sync buffers in parallel using multiple threads. + /// @note Recall that the indexing of auxiliary buffers is 1-based, since + /// buffer index 0 denotes the leaf node buffer. So buffer index 1 denotes + /// the first auxiliary buffer. + bool syncAuxBuffer(size_t bufferIdx, bool serial = false) + { + namespace ph = std::placeholders; + if (bufferIdx == 0 || bufferIdx > mAuxBuffersPerLeaf) return false; + mTask = std::bind(&LeafManager::doSyncAuxBuffer, ph::_1, ph::_2, bufferIdx - 1); + this->cook(serial ? 0 : 64); + return true;//success + } + + /// @brief Sync up all auxiliary buffers with their corresponding leaf node buffers. + /// @return true if the sync was successful + /// @param serial if false, sync buffers in parallel using multiple threads. + bool syncAllBuffers(bool serial = false) + { + namespace ph = std::placeholders; + switch (mAuxBuffersPerLeaf) { + case 0: return false;//nothing to do + case 1: mTask = std::bind(&LeafManager::doSyncAllBuffers1, ph::_1, ph::_2); break; + case 2: mTask = std::bind(&LeafManager::doSyncAllBuffers2, ph::_1, ph::_2); break; + default: mTask = std::bind(&LeafManager::doSyncAllBuffersN, ph::_1, ph::_2); break; + } + this->cook(serial ? 0 : 64); + return true;//success + } + + /// @brief Threaded method that applies a user-supplied functor + /// to each leaf node in the LeafManager. + /// + /// @details The user-supplied functor needs to define the methods + /// required for tbb::parallel_for. + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @warning The functor object is deep-copied to create TBB tasks. + /// This allows the function to use non-thread-safe members + /// like a ValueAccessor. + /// + /// @par Example: + /// @code + /// // Functor to offset a tree's voxel values with values from another tree. + /// template + /// struct OffsetOp + /// { + /// using Accessor = tree::ValueAccessor; + /// + /// OffsetOp(const TreeType& tree): mRhsTreeAcc(tree) {} + /// + /// template + /// void operator()(LeafNodeType &lhsLeaf, size_t) const + /// { + /// const LeafNodeType *rhsLeaf = mRhsTreeAcc.probeConstLeaf(lhsLeaf.origin()); + /// if (rhsLeaf) { + /// typename LeafNodeType::ValueOnIter iter = lhsLeaf.beginValueOn(); + /// for (; iter; ++iter) { + /// iter.setValue(iter.getValue() + rhsLeaf->getValue(iter.pos())); + /// } + /// } + /// } + /// Accessor mRhsTreeAcc; + /// }; + /// + /// // usage: + /// tree::LeafManager leafNodes(lhsTree); + /// leafNodes.foreach(OffsetOp(rhsTree)); + /// + /// // A functor that performs a min operation between different auxiliary buffers. + /// template + /// struct MinOp + /// { + /// using BufferType = typename LeafManagerType::BufferType; + /// + /// MinOp(LeafManagerType& leafNodes): mLeafs(leafNodes) {} + /// + /// template + /// void operator()(LeafNodeType &leaf, size_t leafIndex) const + /// { + /// // get the first buffer + /// BufferType& buffer = mLeafs.getBuffer(leafIndex, 1); + /// + /// // min ... + /// } + /// LeafManagerType& mLeafs; + /// }; + /// @endcode + template + void foreach(const LeafOp& op, bool threaded = true, size_t grainSize=1) + { + LeafTransformer transform(op); + transform.run(this->leafRange(grainSize), threaded); + } + + /// @brief Threaded method that applies a user-supplied functor + /// to each leaf node in the LeafManager. Unlike foreach + /// (defined above) this method performs a reduction on + /// all the leaf nodes. + /// + /// @details The user-supplied functor needs to define the methods + /// required for tbb::parallel_reduce. + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @warning The functor object is deep-copied to create TBB tasks. + /// This allows the function to use non-thread-safe members + /// like a ValueAccessor. + /// + /// @par Example: + /// @code + /// // Functor to count the number of negative (active) leaf values + /// struct CountOp + /// { + /// CountOp() : mCounter(0) {} + /// CountOp(const CountOp &other) : mCounter(other.mCounter) {} + /// CountOp(const CountOp &other, tbb::split) : mCounter(0) {} + /// template + /// void operator()(LeafNodeType &leaf, size_t) + /// { + /// typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn(); + /// for (; iter; ++iter) if (*iter < 0.0f) ++mCounter; + /// } + /// void join(const CountOp &other) {mCounter += other.mCounter;} + /// size_t mCounter; + /// }; + /// + /// // usage: + /// tree::LeafManager leafNodes(tree); + /// MinValueOp min; + /// leafNodes.reduce(min); + /// std::cerr << "Number of negative active voxels = " << min.mCounter << std::endl; + /// + /// @endcode + template + void reduce(LeafOp& op, bool threaded = true, size_t grainSize=1) + { + LeafReducer transform(op); + transform.run(this->leafRange(grainSize), threaded); + } + + + /// @brief Insert pointers to nodes of the specified type into the array. + /// @details The type of node pointer is defined by the type + /// ArrayT::value_type. If the node type is a LeafNode the nodes + /// are inserted from this LeafManager, else of the corresponding tree. + template + void getNodes(ArrayT& array) + { + using T = typename ArrayT::value_type; + static_assert(std::is_pointer::value, "argument to getNodes() must be a pointer array"); + using LeafT = typename std::conditional::type>::value, const LeafType, LeafType>::type; + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (std::is_same::value) { + array.resize(mLeafCount); + for (size_t i=0; i(mLeafs[i]); + } else { + mTree->getNodes(array); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + /// @brief Insert node pointers of the specified type into the array. + /// @details The type of node pointer is defined by the type + /// ArrayT::value_type. If the node type is a LeafNode the nodes + /// are inserted from this LeafManager, else of the corresponding tree. + template + void getNodes(ArrayT& array) const + { + using T = typename ArrayT::value_type; + static_assert(std::is_pointer::value, "argument to getNodes() must be a pointer array"); + static_assert(std::is_const::type>::value, + "argument to getNodes() must be an array of const node pointers"); + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (std::is_same::value) { + array.resize(mLeafCount); + for (size_t i=0; i(mLeafs[i]); + } else { + mTree->getNodes(array); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + /// @brief Generate a linear array of prefix sums of offsets into the + /// active voxels in the leafs. So @a offsets[n]+m is the offset to the + /// mth active voxel in the nth leaf node (useful for + /// user-managed value buffers, e.g. in tools/LevelSetAdvect.h). + /// @return The total number of active values in the leaf nodes + /// @param offsets array of prefix sums of offsets to active voxels + /// @param size on input, the size of @a offsets; on output, its new size + /// @param grainSize optional grain size for threading + /// @details If @a offsets is @c nullptr or @a size is smaller than the + /// total number of active voxels (the return value) then @a offsets + /// is reallocated and @a size equals the total number of active voxels. + size_t getPrefixSum(size_t*& offsets, size_t& size, size_t grainSize=1) const + { + if (offsets == nullptr || size < mLeafCount) { + delete [] offsets; + offsets = new size_t[mLeafCount]; + size = mLeafCount; + } + size_t prefix = 0; + if ( grainSize > 0 ) { + PrefixSum tmp(this->leafRange( grainSize ), offsets, prefix); + } else {// serial + for (size_t i=0; ionVoxelCount(); + } + } + return prefix; + } + + //////////////////////////////////////////////////////////////////////////////////// + // All methods below are for internal use only and should never be called directly + + /// Used internally by tbb::parallel_for() - never call it directly! + void operator()(const RangeType& r) const + { + if (mTask) mTask(const_cast(this), r); + else OPENVDB_THROW(ValueError, "task is undefined"); + } + +private: + + // This a simple wrapper for a c-style array so it mimics the api + // of a std container, e.g. std::vector or std::deque, and can be + // passed to Tree::getNodes(). + struct MyArray { + using value_type = LeafType*;//required by Tree::getNodes + value_type* ptr; + MyArray(value_type* array) : ptr(array) {} + void push_back(value_type leaf) { *ptr++ = leaf; }//required by Tree::getNodes + }; + + void initLeafArray() + { + const size_t leafCount = mTree->leafCount(); + if (leafCount != mLeafCount) { + delete [] mLeafs; + mLeafs = (leafCount == 0) ? nullptr : new LeafType*[leafCount]; + mLeafCount = leafCount; + } + MyArray a(mLeafs); + mTree->getNodes(a); + } + + void initAuxBuffers(bool serial) + { + const size_t auxBufferCount = mLeafCount * mAuxBuffersPerLeaf; + if (auxBufferCount != mAuxBufferCount) { + delete [] mAuxBuffers; + mAuxBuffers = (auxBufferCount == 0) ? nullptr : new NonConstBufferType[auxBufferCount]; + mAuxBufferCount = auxBufferCount; + } + this->syncAllBuffers(serial); + } + + void cook(size_t grainsize) + { + if (grainsize>0) { + tbb::parallel_for(this->getRange(grainsize), *this); + } else { + (*this)(this->getRange()); + } + } + + void doSwapLeafBuffer(const RangeType& r, size_t auxBufferIdx) + { + LeafManagerImpl::doSwapLeafBuffer( + r, auxBufferIdx, mLeafs, mAuxBuffers, mAuxBuffersPerLeaf); + } + + void doSwapAuxBuffer(const RangeType& r, size_t auxBufferIdx1, size_t auxBufferIdx2) + { + for (size_t N = mAuxBuffersPerLeaf, n = N*r.begin(), m = N*r.end(); n != m; n+=N) { + mAuxBuffers[n + auxBufferIdx1].swap(mAuxBuffers[n + auxBufferIdx2]); + } + } + + void doSyncAuxBuffer(const RangeType& r, size_t auxBufferIdx) + { + for (size_t n = r.begin(), m = r.end(), N = mAuxBuffersPerLeaf; n != m; ++n) { + mAuxBuffers[n*N + auxBufferIdx] = mLeafs[n]->buffer(); + } + } + + void doSyncAllBuffers1(const RangeType& r) + { + for (size_t n = r.begin(), m = r.end(); n != m; ++n) { + mAuxBuffers[n] = mLeafs[n]->buffer(); + } + } + + void doSyncAllBuffers2(const RangeType& r) + { + for (size_t n = r.begin(), m = r.end(); n != m; ++n) { + const BufferType& leafBuffer = mLeafs[n]->buffer(); + mAuxBuffers[2*n ] = leafBuffer; + mAuxBuffers[2*n+1] = leafBuffer; + } + } + + void doSyncAllBuffersN(const RangeType& r) + { + for (size_t n = r.begin(), m = r.end(), N = mAuxBuffersPerLeaf; n != m; ++n) { + const BufferType& leafBuffer = mLeafs[n]->buffer(); + for (size_t i=n*N, j=i+N; i!=j; ++i) mAuxBuffers[i] = leafBuffer; + } + } + + /// @brief Private member class that applies a user-defined + /// functor to perform parallel_for on all the leaf nodes. + template + struct LeafTransformer + { + LeafTransformer(const LeafOp &leafOp) : mLeafOp(leafOp) + { + } + void run(const LeafRange &range, bool threaded) const + { + threaded ? tbb::parallel_for(range, *this) : (*this)(range); + } + void operator()(const LeafRange &range) const + { + for (typename LeafRange::Iterator it = range.begin(); it; ++it) mLeafOp(*it, it.pos()); + } + const LeafOp mLeafOp; + };// LeafTransformer + + /// @brief Private member class that applies a user-defined + /// functor to perform parallel_reduce on all the leaf nodes. + template + struct LeafReducer + { + LeafReducer(LeafOp &leafOp) : mLeafOp(&leafOp), mOwnsOp(false) + { + } + LeafReducer(const LeafReducer &other, tbb::split) + : mLeafOp(new LeafOp(*(other.mLeafOp), tbb::split())), mOwnsOp(true) + { + } + ~LeafReducer() { if (mOwnsOp) delete mLeafOp; } + void run(const LeafRange& range, bool threaded) + { + threaded ? tbb::parallel_reduce(range, *this) : (*this)(range); + } + void operator()(const LeafRange& range) + { + LeafOp &op = *mLeafOp;//local registry + for (typename LeafRange::Iterator it = range.begin(); it; ++it) op(*it, it.pos()); + } + void join(const LeafReducer& other) { mLeafOp->join(*(other.mLeafOp)); } + LeafOp *mLeafOp; + const bool mOwnsOp; + };// LeafReducer + + // Helper class to compute a prefix sum of offsets to active voxels + struct PrefixSum + { + PrefixSum(const LeafRange& r, size_t* offsets, size_t& prefix) + : mOffsets(offsets) + { + tbb::parallel_for( r, *this); + for (size_t i=0, leafCount = r.size(); ionVoxelCount(); + } + } + size_t* mOffsets; + };// PrefixSum + + using FuncType = typename std::function; + + TreeType* mTree; + size_t mLeafCount, mAuxBufferCount, mAuxBuffersPerLeaf; + LeafType** mLeafs;//array of LeafNode pointers + NonConstBufferType* mAuxBuffers;//array of auxiliary buffers + FuncType mTask; + const bool mIsMaster; +};//end of LeafManager class + + +// Partial specializations of LeafManager methods for const trees +template +struct LeafManagerImpl > +{ + using ManagerT = LeafManager; + using RangeT = typename ManagerT::RangeType; + using LeafT = typename ManagerT::LeafType; + using BufT = typename ManagerT::BufferType; + + static inline void doSwapLeafBuffer(const RangeT&, size_t /*auxBufferIdx*/, + LeafT**, BufT*, size_t /*bufsPerLeaf*/) + { + // Buffers can't be swapped into const trees. + } +}; + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/LeafNode.h b/openvdb/tree/LeafNode.h new file mode 100644 index 00000000..d93cb23c --- /dev/null +++ b/openvdb/tree/LeafNode.h @@ -0,0 +1,1990 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED + +#include +#include +#include // for io::readData(), etc. +#include "Iterator.h" +#include "LeafBuffer.h" +#include // for std::nth_element() +#include +#include +#include +#include +#include +#include + + +class TestLeaf; +template class TestLeafIO; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +template struct SameLeafConfig; // forward declaration + + +/// @brief Templated block class to hold specific data types and a fixed +/// number of values determined by Log2Dim. The actual coordinate +/// dimension of the block is 2^Log2Dim, i.e. Log2Dim=3 corresponds to +/// a LeafNode that spans a 8^3 block. +template +class LeafNode +{ +public: + using BuildType = T; + using ValueType = T; + using Buffer = LeafBuffer; + using LeafNodeType = LeafNode; + using NodeMaskType = util::NodeMask; + using Ptr = SharedPtr; + + static const Index + LOG2DIM = Log2Dim, // needed by parent nodes + TOTAL = Log2Dim, // needed by parent nodes + DIM = 1 << TOTAL, // dimension along one coordinate direction + NUM_VALUES = 1 << 3 * Log2Dim, + NUM_VOXELS = NUM_VALUES, // total number of voxels represented by this node + SIZE = NUM_VALUES, + LEVEL = 0; // level 0 = leaf + + /// @brief ValueConverter::Type is the type of a LeafNode having the same + /// dimensions as this node but a different value type, T. + template + struct ValueConverter { using Type = LeafNode; }; + + /// @brief SameConfiguration::value is @c true if and only if + /// OtherNodeType is the type of a LeafNode with the same dimensions as this node. + template + struct SameConfiguration { + static const bool value = SameLeafConfig::value; + }; + + + /// Default constructor + LeafNode(); + + /// @brief Constructor + /// @param coords the grid index coordinates of a voxel + /// @param value a value with which to fill the buffer + /// @param active the active state to which to initialize all voxels + explicit LeafNode(const Coord& coords, + const ValueType& value = zeroVal(), + bool active = false); + + /// @brief "Partial creation" constructor used during file input + /// @param coords the grid index coordinates of a voxel + /// @param value a value with which to fill the buffer + /// @param active the active state to which to initialize all voxels + /// @details This constructor does not allocate memory for voxel values. + LeafNode(PartialCreate, + const Coord& coords, + const ValueType& value = zeroVal(), + bool active = false); + + /// Deep copy constructor + LeafNode(const LeafNode&); + + /// Deep assignment operator + LeafNode& operator=(const LeafNode&) = default; + + /// Value conversion copy constructor + template + explicit LeafNode(const LeafNode& other); + + /// Topology copy constructor + template + LeafNode(const LeafNode& other, + const ValueType& offValue, const ValueType& onValue, TopologyCopy); + + /// Topology copy constructor + template + LeafNode(const LeafNode& other, + const ValueType& background, TopologyCopy); + + /// Destructor. + ~LeafNode(); + + // + // Statistics + // + /// Return log2 of the dimension of this LeafNode, e.g. 3 if dimensions are 8^3 + static Index log2dim() { return Log2Dim; } + /// Return the number of voxels in each coordinate dimension. + static Index dim() { return DIM; } + /// Return the total number of voxels represented by this LeafNode + static Index size() { return SIZE; } + /// Return the total number of voxels represented by this LeafNode + static Index numValues() { return SIZE; } + /// Return the level of this node, which by definition is zero for LeafNodes + static Index getLevel() { return LEVEL; } + /// Append the Log2Dim of this LeafNode to the specified vector + static void getNodeLog2Dims(std::vector& dims) { dims.push_back(Log2Dim); } + /// Return the dimension of child nodes of this LeafNode, which is one for voxels. + static Index getChildDim() { return 1; } + /// Return the leaf count for this node, which is one. + static Index32 leafCount() { return 1; } + /// no-op + void nodeCount(std::vector &) const {} + /// Return the non-leaf count for this node, which is zero. + static Index32 nonLeafCount() { return 0; } + + /// Return the number of voxels marked On. + Index64 onVoxelCount() const { return mValueMask.countOn(); } + /// Return the number of voxels marked Off. + Index64 offVoxelCount() const { return mValueMask.countOff(); } + Index64 onLeafVoxelCount() const { return onVoxelCount(); } + Index64 offLeafVoxelCount() const { return offVoxelCount(); } + static Index64 onTileCount() { return 0; } + static Index64 offTileCount() { return 0; } + /// Return @c true if this node has no active voxels. + bool isEmpty() const { return mValueMask.isOff(); } + /// Return @c true if this node contains only active voxels. + bool isDense() const { return mValueMask.isOn(); } + /// Return @c true if memory for this node's buffer has been allocated. + bool isAllocated() const { return !mBuffer.isOutOfCore() && !mBuffer.empty(); } + /// Allocate memory for this node's buffer if it has not already been allocated. + bool allocate() { return mBuffer.allocate(); } + + /// Return the memory in bytes occupied by this node. + Index64 memUsage() const; + + /// Expand the given bounding box so that it includes this leaf node's active voxels. + /// If visitVoxels is false this LeafNode will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; + + /// @brief Return the bounding box of this node, i.e., the full index space + /// spanned by this leaf node. + CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); } + + /// Set the grid index coordinates of this node's local origin. + void setOrigin(const Coord& origin) { mOrigin = origin; } + //@{ + /// Return the grid index coordinates of this node's local origin. + const Coord& origin() const { return mOrigin; } + void getOrigin(Coord& origin) const { origin = mOrigin; } + void getOrigin(Int32& x, Int32& y, Int32& z) const { mOrigin.asXYZ(x, y, z); } + //@} + + /// Return the linear table offset of the given global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static Coord offsetToLocalCoord(Index n); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(Index n) const; + + /// Return a string representation of this node. + std::string str() const; + + /// @brief Return @c true if the given node (which may have a different @c ValueType + /// than this node) has the same active value topology as this node. + template + bool hasSameTopology(const LeafNode* other) const; + + /// Check for buffer, state and origin equivalence. + bool operator==(const LeafNode& other) const; + bool operator!=(const LeafNode& other) const { return !(other == *this); } + +protected: + using MaskOnIterator = typename NodeMaskType::OnIterator; + using MaskOffIterator = typename NodeMaskType::OffIterator; + using MaskDenseIterator = typename NodeMaskType::DenseIterator; + + // Type tags to disambiguate template instantiations + struct ValueOn {}; struct ValueOff {}; struct ValueAll {}; + struct ChildOn {}; struct ChildOff {}; struct ChildAll {}; + + template + struct ValueIter: + // Derives from SparseIteratorBase, but can also be used as a dense iterator, + // if MaskIterT is a dense mask iterator type. + public SparseIteratorBase< + MaskIterT, ValueIter, NodeT, ValueT> + { + using BaseT = SparseIteratorBase; + + ValueIter() {} + ValueIter(const MaskIterT& iter, NodeT* parent): BaseT(iter, parent) {} + + ValueT& getItem(Index pos) const { return this->parent().getValue(pos); } + ValueT& getValue() const { return this->parent().getValue(this->pos()); } + + // Note: setItem() can't be called on const iterators. + void setItem(Index pos, const ValueT& value) const + { + this->parent().setValueOnly(pos, value); + } + // Note: setValue() can't be called on const iterators. + void setValue(const ValueT& value) const + { + this->parent().setValueOnly(this->pos(), value); + } + + // Note: modifyItem() can't be called on const iterators. + template + void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); } + // Note: modifyValue() can't be called on const iterators. + template + void modifyValue(const ModifyOp& op) const { this->parent().modifyValue(this->pos(), op); } + }; + + /// Leaf nodes have no children, so their child iterators have no get/set accessors. + template + struct ChildIter: + public SparseIteratorBase, NodeT, ValueType> + { + ChildIter() {} + ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ChildIter, NodeT, ValueType>(iter, parent) {} + }; + + template + struct DenseIter: public DenseIteratorBase< + MaskDenseIterator, DenseIter, NodeT, /*ChildT=*/void, ValueT> + { + using BaseT = DenseIteratorBase; + using NonConstValueT = typename BaseT::NonConstValueType; + + DenseIter() {} + DenseIter(const MaskDenseIterator& iter, NodeT* parent): BaseT(iter, parent) {} + + bool getItem(Index pos, void*& child, NonConstValueT& value) const + { + value = this->parent().getValue(pos); + child = nullptr; + return false; // no child + } + + // Note: setItem() can't be called on const iterators. + //void setItem(Index pos, void* child) const {} + + // Note: unsetItem() can't be called on const iterators. + void unsetItem(Index pos, const ValueT& value) const + { + this->parent().setValueOnly(pos, value); + } + }; + +public: + using ValueOnIter = ValueIter; + using ValueOnCIter = ValueIter; + using ValueOffIter = ValueIter; + using ValueOffCIter = ValueIter; + using ValueAllIter = ValueIter; + using ValueAllCIter = ValueIter; + using ChildOnIter = ChildIter; + using ChildOnCIter = ChildIter; + using ChildOffIter = ChildIter; + using ChildOffCIter = ChildIter; + using ChildAllIter = DenseIter; + using ChildAllCIter = DenseIter; + + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); } + ValueOnCIter beginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); } + ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); } + ValueOffCIter beginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); } + ValueOffIter beginValueOff() { return ValueOffIter(mValueMask.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); } + ValueAllCIter beginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); } + ValueAllIter beginValueAll() { return ValueAllIter(mValueMask.beginDense(), this); } + + ValueOnCIter cendValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); } + ValueOnCIter endValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); } + ValueOnIter endValueOn() { return ValueOnIter(mValueMask.endOn(), this); } + ValueOffCIter cendValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); } + ValueOffCIter endValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); } + ValueOffIter endValueOff() { return ValueOffIter(mValueMask.endOff(), this); } + ValueAllCIter cendValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); } + ValueAllCIter endValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); } + ValueAllIter endValueAll() { return ValueAllIter(mValueMask.endDense(), this); } + + // Note that [c]beginChildOn() and [c]beginChildOff() actually return end iterators, + // because leaf nodes have no children. + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } + ChildOnCIter beginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } + ChildOnIter beginChildOn() { return ChildOnIter(mValueMask.endOn(), this); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } + ChildOffCIter beginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } + ChildOffIter beginChildOff() { return ChildOffIter(mValueMask.endOff(), this); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); } + ChildAllCIter beginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); } + ChildAllIter beginChildAll() { return ChildAllIter(mValueMask.beginDense(), this); } + + ChildOnCIter cendChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } + ChildOnCIter endChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } + ChildOnIter endChildOn() { return ChildOnIter(mValueMask.endOn(), this); } + ChildOffCIter cendChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } + ChildOffCIter endChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } + ChildOffIter endChildOff() { return ChildOffIter(mValueMask.endOff(), this); } + ChildAllCIter cendChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); } + ChildAllCIter endChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); } + ChildAllIter endChildAll() { return ChildAllIter(mValueMask.endDense(), this); } + + // + // Buffer management + // + /// @brief Exchange this node's data buffer with the given data buffer + /// without changing the active states of the values. + void swap(Buffer& other) { mBuffer.swap(other); } + const Buffer& buffer() const { return mBuffer; } + Buffer& buffer() { return mBuffer; } + + // + // I/O methods + // + /// @brief Read in just the topology. + /// @param is the stream from which to read + /// @param fromHalf if true, floating-point input values are assumed to be 16-bit + void readTopology(std::istream& is, bool fromHalf = false); + /// @brief Write out just the topology. + /// @param os the stream to which to write + /// @param toHalf if true, output floating-point values as 16-bit half floats + void writeTopology(std::ostream& os, bool toHalf = false) const; + + /// @brief Read buffers from a stream. + /// @param is the stream from which to read + /// @param fromHalf if true, floating-point input values are assumed to be 16-bit + void readBuffers(std::istream& is, bool fromHalf = false); + /// @brief Read buffers that intersect the given bounding box. + /// @param is the stream from which to read + /// @param bbox an index-space bounding box + /// @param fromHalf if true, floating-point input values are assumed to be 16-bit + void readBuffers(std::istream& is, const CoordBBox& bbox, bool fromHalf = false); + /// @brief Write buffers to a stream. + /// @param os the stream to which to write + /// @param toHalf if true, output floating-point values as 16-bit half floats + void writeBuffers(std::ostream& os, bool toHalf = false) const; + + size_t streamingSize(bool toHalf = false) const; + + // + // Accessor methods + // + /// Return the value of the voxel at the given coordinates. + const ValueType& getValue(const Coord& xyz) const; + /// Return the value of the voxel at the given linear offset. + const ValueType& getValue(Index offset) const; + + /// @brief Return @c true if the voxel at the given coordinates is active. + /// @param xyz the coordinates of the voxel to be probed + /// @param[out] val the value of the voxel at the given coordinates + bool probeValue(const Coord& xyz, ValueType& val) const; + /// @brief Return @c true if the voxel at the given offset is active. + /// @param offset the linear offset of the voxel to be probed + /// @param[out] val the value of the voxel at the given coordinates + bool probeValue(Index offset, ValueType& val) const; + + /// Return the level (i.e., 0) at which leaf node values reside. + static Index getValueLevel(const Coord&) { return LEVEL; } + + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveState(Index offset, bool on) { assert(offsetsetValueOn(LeafNode::coordToOffset(xyz), val); + } + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, const ValueType& val) { this->setValueOn(xyz, val); } + /// Set the value of the voxel at the given offset and mark the voxel as active. + void setValueOn(Index offset, const ValueType& val) { + mBuffer.setValue(offset, val); + mValueMask.setOn(offset); + } + + /// @brief Apply a functor to the value of the voxel at the given offset + /// and mark the voxel as active. + template + void modifyValue(Index offset, const ModifyOp& op) + { + mBuffer.loadValues(); + if (!mBuffer.empty()) { + // in-place modify value + ValueType& val = const_cast(mBuffer[offset]); + op(val); + mValueMask.setOn(offset); + } + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + this->modifyValue(this->coordToOffset(xyz), op); + } + + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + mBuffer.loadValues(); + if (!mBuffer.empty()) { + const Index offset = this->coordToOffset(xyz); + bool state = mValueMask.isOn(offset); + // in-place modify value + ValueType& val = const_cast(mBuffer[offset]); + op(val, state); + mValueMask.set(offset, state); + } + } + + /// Mark all voxels as active but don't change their values. + void setValuesOn() { mValueMask.setOn(); } + /// Mark all voxels as inactive but don't change their values. + void setValuesOff() { mValueMask.setOff(); } + + /// Return @c true if the voxel at the given coordinates is active. + bool isValueOn(const Coord& xyz) const {return this->isValueOn(LeafNode::coordToOffset(xyz));} + /// Return @c true if the voxel at the given offset is active. + bool isValueOn(Index offset) const { return mValueMask.isOn(offset); } + + /// Return @c false since leaf nodes never contain tiles. + static bool hasActiveTiles() { return false; } + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&, const ValueType& background); + + /// Set all voxels within an axis-aligned box to the specified value and active state. + void fill(const CoordBBox& bbox, const ValueType&, bool active = true); + /// Set all voxels within an axis-aligned box to the specified value and active state. + void denseFill(const CoordBBox& bbox, const ValueType& value, bool active = true) + { + this->fill(bbox, value, active); + } + + /// Set all voxels to the specified value but don't change their active states. + void fill(const ValueType& value); + /// Set all voxels to the specified value and active state. + void fill(const ValueType& value, bool active); + + /// @brief Copy into a dense grid the values of the voxels that lie within + /// a given bounding box. + /// + /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid + /// @param dense dense grid with a stride in @e z of one (see tools::Dense + /// in tools/Dense.h for the required API) + /// + /// @note @a bbox is assumed to be identical to or contained in the coordinate domains + /// of both the dense grid and this node, i.e., no bounds checking is performed. + /// @note Consider using tools::CopyToDense in tools/Dense.h + /// instead of calling this method directly. + template + void copyToDense(const CoordBBox& bbox, DenseT& dense) const; + + /// @brief Copy from a dense grid into this node the values of the voxels + /// that lie within a given bounding box. + /// @details Only values that are different (by more than the given tolerance) + /// from the background value will be active. Other values are inactive + /// and truncated to the background value. + /// + /// @param bbox inclusive bounding box of the voxels to be copied into this node + /// @param dense dense grid with a stride in @e z of one (see tools::Dense + /// in tools/Dense.h for the required API) + /// @param background background value of the tree that this node belongs to + /// @param tolerance tolerance within which a value equals the background value + /// + /// @note @a bbox is assumed to be identical to or contained in the coordinate domains + /// of both the dense grid and this node, i.e., no bounds checking is performed. + /// @note Consider using tools::CopyFromDense in tools/Dense.h + /// instead of calling this method directly. + template + void copyFromDense(const CoordBBox& bbox, const DenseT& dense, + const ValueType& background, const ValueType& tolerance); + + /// @brief Return the value of the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template + const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const + { + return this->getValue(xyz); + } + + /// @brief Return @c true if the voxel at the given coordinates is active. + /// @note Used internally by ValueAccessor. + template + bool isValueOnAndCache(const Coord& xyz, AccessorT&) const { return this->isValueOn(xyz); } + + /// @brief Change the value of the voxel at the given coordinates and mark it as active. + /// @note Used internally by ValueAccessor. + template + void setValueAndCache(const Coord& xyz, const ValueType& val, AccessorT&) + { + this->setValueOn(xyz, val); + } + + /// @brief Change the value of the voxel at the given coordinates + /// but preserve its state. + /// @note Used internally by ValueAccessor. + template + void setValueOnlyAndCache(const Coord& xyz, const ValueType& val, AccessorT&) + { + this->setValueOnly(xyz, val); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValue(xyz, op); + } + + /// Apply a functor to the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValueAndActiveState(xyz, op); + } + + /// @brief Change the value of the voxel at the given coordinates and mark it as inactive. + /// @note Used internally by ValueAccessor. + template + void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&) + { + this->setValueOff(xyz, value); + } + + /// @brief Set the active state of the voxel at the given coordinates + /// without changing its value. + /// @note Used internally by ValueAccessor. + template + void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&) + { + this->setActiveState(xyz, on); + } + + /// @brief Return @c true if the voxel at the given coordinates is active + /// and return the voxel value in @a val. + /// @note Used internally by ValueAccessor. + template + bool probeValueAndCache(const Coord& xyz, ValueType& val, AccessorT&) const + { + return this->probeValue(xyz, val); + } + + /// @brief Return the value of the voxel at the given coordinates and return + /// its active state and level (i.e., 0) in @a state and @a level. + /// @note Used internally by ValueAccessor. + template + const ValueType& getValue(const Coord& xyz, bool& state, int& level, AccessorT&) const + { + const Index offset = this->coordToOffset(xyz); + state = mValueMask.isOn(offset); + level = LEVEL; + return mBuffer[offset]; + } + + /// @brief Return the LEVEL (=0) at which leaf node values reside. + /// @note Used internally by ValueAccessor (note last argument is a dummy). + template + static Index getValueLevelAndCache(const Coord&, AccessorT&) { return LEVEL; } + + /// @brief Return a const reference to the first value in the buffer. + /// @note Though it is potentially risky you can convert this + /// to a non-const pointer by means of const_case&. + const ValueType& getFirstValue() const { return mBuffer[0]; } + /// Return a const reference to the last value in the buffer. + const ValueType& getLastValue() const { return mBuffer[SIZE - 1]; } + + /// @brief Replace inactive occurrences of @a oldBackground with @a newBackground, + /// and inactive occurrences of @a -oldBackground with @a -newBackground. + void resetBackground(const ValueType& oldBackground, const ValueType& newBackground); + + void negate(); + + /// @brief No-op + /// @details This function exists only to enable template instantiation. + void voxelizeActiveTiles(bool = true) {} + + template void merge(const LeafNode&); + template void merge(const ValueType& tileValue, bool tileActive); + template + void merge(const LeafNode& other, const ValueType& /*bg*/, const ValueType& /*otherBG*/); + + /// @brief Union this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active if either of the original voxels + /// were active. + /// + /// @note This operation modifies only active states, not values. + template + void topologyUnion(const LeafNode& other); + + /// @brief Intersect this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active only if both of the original voxels + /// were active. + /// + /// @details The last dummy argument is required to match the signature + /// for InternalNode::topologyIntersection. + /// + /// @note This operation modifies only active states, not + /// values. Also note that this operation can result in all voxels + /// being inactive so consider subsequnetly calling prune. + template + void topologyIntersection(const LeafNode& other, const ValueType&); + + /// @brief Difference this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this LeafNode and inactive in the other LeafNode. + /// + /// @details The last dummy argument is required to match the signature + /// for InternalNode::topologyDifference. + /// + /// @note This operation modifies only active states, not values. + /// Also, because it can deactivate all of this node's voxels, + /// consider subsequently calling prune. + template + void topologyDifference(const LeafNode& other, const ValueType&); + + template + void combine(const LeafNode& other, CombineOp& op); + template + void combine(const ValueType& value, bool valueIsActive, CombineOp& op); + + template + void combine2(const LeafNode& other, const OtherType&, bool valueIsActive, CombineOp&); + template + void combine2(const ValueType&, const OtherNodeT& other, bool valueIsActive, CombineOp&); + template + void combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp&); + + /// @brief Calls the templated functor BBoxOp with bounding box + /// information. An additional level argument is provided to the + /// callback. + /// + /// @note The bounding boxes are guarenteed to be non-overlapping. + template void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&); + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&) const; + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false); + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const; + + //@{ + /// This function exists only to enable template instantiation. + void prune(const ValueType& /*tolerance*/ = zeroVal()) {} + void addLeaf(LeafNode*) {} + template + void addLeafAndCache(LeafNode*, AccessorT&) {} + template + NodeT* stealNode(const Coord&, const ValueType&, bool) { return nullptr; } + template + NodeT* probeNode(const Coord&) { return nullptr; } + template + const NodeT* probeConstNode(const Coord&) const { return nullptr; } + template void getNodes(ArrayT&) const {} + template void stealNodes(ArrayT&, const ValueType&, bool) {} + //@} + + void addTile(Index level, const Coord&, const ValueType&, bool); + void addTile(Index offset, const ValueType&, bool); + template + void addTileAndCache(Index, const Coord&, const ValueType&, bool, AccessorT&); + + //@{ + /// @brief Return a pointer to this node. + LeafNode* touchLeaf(const Coord&) { return this; } + template + LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + LeafNode* probeLeaf(const Coord&) { return this; } + template + LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + //@} + //@{ + /// @brief Return a @const pointer to this node. + const LeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } + template + const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const LeafNode* probeLeaf(const Coord&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + /// Return @c true if all of this node's values have the same active state + /// and are in the range this->getFirstValue() +/- @a tolerance. + /// + /// + /// @param firstValue Is updated with the first value of this leaf node. + /// @param state Is updated with the state of all values IF method + /// returns @c true. Else the value is undefined! + /// @param tolerance The tolerance used to determine if values are + /// approximatly equal to the for value. + bool isConstant(ValueType& firstValue, bool& state, + const ValueType& tolerance = zeroVal()) const; + + /// Return @c true if all of this node's values have the same active state + /// and the range (@a maxValue - @a minValue) < @a tolerance. + /// + /// @param minValue Is updated with the minimum of all values IF method + /// returns @c true. Else the value is undefined! + /// @param maxValue Is updated with the maximum of all values IF method + /// returns @c true. Else the value is undefined! + /// @param state Is updated with the state of all values IF method + /// returns @c true. Else the value is undefined! + /// @param tolerance The tolerance used to determine if values are + /// approximatly constant. + bool isConstant(ValueType& minValue, ValueType& maxValue, + bool& state, const ValueType& tolerance = zeroVal()) const; + + + /// @brief Computes the median value of all the active AND inactive voxels in this node. + /// @return The median value of all values in this node. + /// + /// @param tmp Optional temporary storage that can hold at least NUM_VALUES values + /// Use of this temporary storage can improve performance + /// when this method is called multiple times. + /// + /// @note If tmp = this->buffer().data() then the median + /// value is computed very efficiently (in place) but + /// the voxel values in this node are re-shuffeled! + /// + /// @warning If tmp != nullptr then it is the responsibility of + /// the client code that it points to enough memory to + /// hold NUM_VALUES elements of type ValueType. + ValueType medianAll(ValueType *tmp = nullptr) const; + + /// @brief Computes the median value of all the active voxels in this node. + /// @return The number of active voxels. + /// + /// @param value If the return value is non zero @a value is updated + /// with the median value. + /// + /// @param tmp Optional temporary storage that can hold at least + /// as many values as there are active voxels in this node. + /// Use of this temporary storage can improve performance + /// when this method is called multiple times. + /// + /// @warning If tmp != nullptr then it is the responsibility of + /// the client code that it points to enough memory to + /// hold the number of active voxels of type ValueType. + Index medianOn(ValueType &value, ValueType *tmp = nullptr) const; + + /// @brief Computes the median value of all the inactive voxels in this node. + /// @return The number of inactive voxels. + /// + /// @param value If the return value is non zero @a value is updated + /// with the median value. + /// + /// @param tmp Optional temporary storage that can hold at least + /// as many values as there are inactive voxels in this node. + /// Use of this temporary storage can improve performance + /// when this method is called multiple times. + /// + /// @warning If tmp != nullptr then it is the responsibility of + /// the client code that it points to enough memory to + /// hold the number of inactive voxels of type ValueType. + Index medianOff(ValueType &value, ValueType *tmp = nullptr) const; + + /// Return @c true if all of this node's values are inactive. + bool isInactive() const { return mValueMask.isOff(); } + +protected: + friend class ::TestLeaf; + template friend class ::TestLeafIO; + + // During topology-only construction, access is needed + // to protected/private members of other template instances. + template friend class LeafNode; + + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + + // Allow iterators to call mask accessor methods (see below). + /// @todo Make mask accessors public? + friend class IteratorBase; + friend class IteratorBase; + friend class IteratorBase; + + // Mask accessors +public: + bool isValueMaskOn(Index n) const { return mValueMask.isOn(n); } + bool isValueMaskOn() const { return mValueMask.isOn(); } + bool isValueMaskOff(Index n) const { return mValueMask.isOff(n); } + bool isValueMaskOff() const { return mValueMask.isOff(); } + const NodeMaskType& getValueMask() const { return mValueMask; } + NodeMaskType& getValueMask() { return mValueMask; } + const NodeMaskType& valueMask() const { return mValueMask; } + void setValueMask(const NodeMaskType& mask) { mValueMask = mask; } + bool isChildMaskOn(Index) const { return false; } // leaf nodes have no children + bool isChildMaskOff(Index) const { return true; } + bool isChildMaskOff() const { return true; } +protected: + void setValueMask(Index n, bool on) { mValueMask.set(n, on); } + void setValueMaskOn(Index n) { mValueMask.setOn(n); } + void setValueMaskOff(Index n) { mValueMask.setOff(n); } + + inline void skipCompressedValues(bool seekable, std::istream&, bool fromHalf); + + /// Compute the origin of the leaf node that contains the voxel with the given coordinates. + static void evalNodeOrigin(Coord& xyz) { xyz &= ~(DIM - 1); } + + template + static inline void doVisit(NodeT&, VisitorOp&); + + template + static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&); + + template + static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); + +private: + /// Buffer containing the actual data values + Buffer mBuffer; + /// Bitmask that determines which voxels are active + NodeMaskType mValueMask; + /// Global grid index coordinates (x,y,z) of the local origin of this node + Coord mOrigin; +}; // end of LeafNode class + + +//////////////////////////////////////// + + +//@{ +/// Helper metafunction used to implement LeafNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameLeafConfig { static const bool value = false; }; + +template +struct SameLeafConfig > { static const bool value = true; }; +//@} + + +//////////////////////////////////////// + + +template +inline +LeafNode::LeafNode(): + mValueMask(),//default is off! + mOrigin(0, 0, 0) +{ +} + + +template +inline +LeafNode::LeafNode(const Coord& xyz, const ValueType& val, bool active): + mBuffer(val), + mValueMask(active), + mOrigin(xyz & (~(DIM - 1))) +{ +} + + +template +inline +LeafNode::LeafNode(PartialCreate, const Coord& xyz, const ValueType& val, bool active): + mBuffer(PartialCreate(), val), + mValueMask(active), + mOrigin(xyz & (~(DIM - 1))) +{ +} + + +template +inline +LeafNode::LeafNode(const LeafNode& other): + mBuffer(other.mBuffer), + mValueMask(other.valueMask()), + mOrigin(other.mOrigin) +{ +} + + +// Copy-construct from a leaf node with the same configuration but a different ValueType. +template +template +inline +LeafNode::LeafNode(const LeafNode& other): + mValueMask(other.valueMask()), + mOrigin(other.mOrigin) +{ + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline ValueType convertValue(const OtherValueType& val) { return ValueType(val); } + }; + + for (Index i = 0; i < SIZE; ++i) { + mBuffer[i] = Local::convertValue(other.mBuffer[i]); + } +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + const ValueType& background, TopologyCopy): + mBuffer(background), + mValueMask(other.valueMask()), + mOrigin(other.mOrigin) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + const ValueType& offValue, const ValueType& onValue, TopologyCopy): + mValueMask(other.valueMask()), + mOrigin(other.mOrigin) +{ + for (Index i = 0; i < SIZE; ++i) { + mBuffer[i] = (mValueMask.isOn(i) ? onValue : offValue); + } +} + + +template +inline +LeafNode::~LeafNode() +{ +} + + +template +inline std::string +LeafNode::str() const +{ + std::ostringstream ostr; + ostr << "LeafNode @" << mOrigin << ": " << mBuffer; + return ostr.str(); +} + + +//////////////////////////////////////// + + +template +inline Index +LeafNode::coordToOffset(const Coord& xyz) +{ + assert ((xyz[0] & (DIM-1u)) < DIM && (xyz[1] & (DIM-1u)) < DIM && (xyz[2] & (DIM-1u)) < DIM); + return ((xyz[0] & (DIM-1u)) << 2*Log2Dim) + + ((xyz[1] & (DIM-1u)) << Log2Dim) + + (xyz[2] & (DIM-1u)); +} + +template +inline Coord +LeafNode::offsetToLocalCoord(Index n) +{ + assert(n<(1<< 3*Log2Dim)); + Coord xyz; + xyz.setX(n >> 2*Log2Dim); + n &= ((1<<2*Log2Dim)-1); + xyz.setY(n >> Log2Dim); + xyz.setZ(n & ((1< +inline Coord +LeafNode::offsetToGlobalCoord(Index n) const +{ + return (this->offsetToLocalCoord(n) + this->origin()); +} + + +//////////////////////////////////////// + + +template +inline const ValueT& +LeafNode::getValue(const Coord& xyz) const +{ + return this->getValue(LeafNode::coordToOffset(xyz)); +} + +template +inline const ValueT& +LeafNode::getValue(Index offset) const +{ + assert(offset < SIZE); + return mBuffer[offset]; +} + + +template +inline bool +LeafNode::probeValue(const Coord& xyz, ValueType& val) const +{ + return this->probeValue(LeafNode::coordToOffset(xyz), val); +} + +template +inline bool +LeafNode::probeValue(Index offset, ValueType& val) const +{ + assert(offset < SIZE); + val = mBuffer[offset]; + return mValueMask.isOn(offset); +} + + +template +inline void +LeafNode::setValueOff(const Coord& xyz, const ValueType& val) +{ + this->setValueOff(LeafNode::coordToOffset(xyz), val); +} + +template +inline void +LeafNode::setValueOff(Index offset, const ValueType& val) +{ + assert(offset < SIZE); + mBuffer.setValue(offset, val); + mValueMask.setOff(offset); +} + + +template +inline void +LeafNode::setActiveState(const Coord& xyz, bool on) +{ + mValueMask.set(this->coordToOffset(xyz), on); +} + + +template +inline void +LeafNode::setValueOnly(const Coord& xyz, const ValueType& val) +{ + this->setValueOnly(LeafNode::coordToOffset(xyz), val); +} + +template +inline void +LeafNode::setValueOnly(Index offset, const ValueType& val) +{ + assert(offset +inline void +LeafNode::clip(const CoordBBox& clipBBox, const T& background) +{ + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. Fill it with the background. + this->fill(background, /*active=*/false); + } else if (clipBBox.isInside(nodeBBox)) { + // This node lies completely inside the clipping region. Leave it intact. + return; + } + + // This node isn't completely contained inside the clipping region. + // Set any voxels that lie outside the region to the background value. + + // Construct a boolean mask that is on inside the clipping region and off outside it. + NodeMaskType mask; + nodeBBox.intersect(clipBBox); + Coord xyz; + int &x = xyz.x(), &y = xyz.y(), &z = xyz.z(); + for (x = nodeBBox.min().x(); x <= nodeBBox.max().x(); ++x) { + for (y = nodeBBox.min().y(); y <= nodeBBox.max().y(); ++y) { + for (z = nodeBBox.min().z(); z <= nodeBBox.max().z(); ++z) { + mask.setOn(static_cast(this->coordToOffset(xyz))); + } + } + } + + // Set voxels that lie in the inactive region of the mask (i.e., outside + // the clipping region) to the background value. + for (MaskOffIterator maskIter = mask.beginOff(); maskIter; ++maskIter) { + this->setValueOff(maskIter.pos(), background); + } +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::fill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + if (!this->allocate()) return; + + auto clippedBBox = this->getNodeBoundingBox(); + clippedBBox.intersect(bbox); + if (!clippedBBox) return; + + for (Int32 x = clippedBBox.min().x(); x <= clippedBBox.max().x(); ++x) { + const Index offsetX = (x & (DIM-1u)) << 2*Log2Dim; + for (Int32 y = clippedBBox.min().y(); y <= clippedBBox.max().y(); ++y) { + const Index offsetXY = offsetX + ((y & (DIM-1u)) << Log2Dim); + for (Int32 z = clippedBBox.min().z(); z <= clippedBBox.max().z(); ++z) { + const Index offset = offsetXY + (z & (DIM-1u)); + mBuffer[offset] = value; + mValueMask.set(offset, active); + } + } + } +} + +template +inline void +LeafNode::fill(const ValueType& value) +{ + mBuffer.fill(value); +} + +template +inline void +LeafNode::fill(const ValueType& value, bool active) +{ + mBuffer.fill(value); + mValueMask.set(active); +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ + mBuffer.loadValues(); + + using DenseValueType = typename DenseT::ValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + DenseValueType* t0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // target array + const T* s0 = &mBuffer[bbox.min()[2] & (DIM-1u)]; // source array + for (Int32 x = bbox.min()[0], ex = bbox.max()[0] + 1; x < ex; ++x) { + DenseValueType* t1 = t0 + xStride * (x - min[0]); + const T* s1 = s0 + ((x & (DIM-1u)) << 2*Log2Dim); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1] + 1; y < ey; ++y) { + DenseValueType* t2 = t1 + yStride * (y - min[1]); + const T* s2 = s1 + ((y & (DIM-1u)) << Log2Dim); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2] + 1; z < ez; ++z, t2 += zStride) { + *t2 = DenseValueType(*s2++); + } + } + } +} + + +template +template +inline void +LeafNode::copyFromDense(const CoordBBox& bbox, const DenseT& dense, + const ValueType& background, const ValueType& tolerance) +{ + if (!this->allocate()) return; + + using DenseValueType = typename DenseT::ValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + + const DenseValueType* s0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // source + const Int32 n0 = bbox.min()[2] & (DIM-1u); + for (Int32 x = bbox.min()[0], ex = bbox.max()[0]+1; x < ex; ++x) { + const DenseValueType* s1 = s0 + xStride * (x - min[0]); + const Int32 n1 = n0 + ((x & (DIM-1u)) << 2*LOG2DIM); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1]+1; y < ey; ++y) { + const DenseValueType* s2 = s1 + yStride * (y - min[1]); + Int32 n2 = n1 + ((y & (DIM-1u)) << LOG2DIM); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2]+1; z < ez; ++z, ++n2, s2 += zStride) { + if (math::isApproxEqual(background, ValueType(*s2), tolerance)) { + mValueMask.setOff(n2); + mBuffer[n2] = background; + } else { + mValueMask.setOn(n2); + mBuffer[n2] = ValueType(*s2); + } + } + } + } +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::readTopology(std::istream& is, bool /*fromHalf*/) +{ + mValueMask.load(is); +} + + +template +inline void +LeafNode::writeTopology(std::ostream& os, bool /*toHalf*/) const +{ + mValueMask.save(os); +} + + +//////////////////////////////////////// + + + +template +inline void +LeafNode::skipCompressedValues(bool seekable, std::istream& is, bool fromHalf) +{ + if (seekable) { + // Seek over voxel values. + io::readCompressedValues( + is, nullptr, SIZE, mValueMask, fromHalf); + } else { + // Read and discard voxel values. + Buffer temp; + io::readCompressedValues(is, temp.mData, SIZE, mValueMask, fromHalf); + } +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, bool fromHalf) +{ + this->readBuffers(is, CoordBBox::inf(), fromHalf); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf) +{ + SharedPtr meta = io::getStreamMetadataPtr(is); + const bool seekable = meta && meta->seekable(); + + std::streamoff maskpos = is.tellg(); + + if (seekable) { + // Seek over the value mask. + mValueMask.seek(is); + } else { + // Read in the value mask. + mValueMask.load(is); + } + + int8_t numBuffers = 1; + if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) { + // Read in the origin. + is.read(reinterpret_cast(&mOrigin), sizeof(Coord::ValueType) * 3); + + // Read in the number of buffers, which should now always be one. + is.read(reinterpret_cast(&numBuffers), sizeof(int8_t)); + } + + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. + skipCompressedValues(seekable, is, fromHalf); + mValueMask.setOff(); + mBuffer.setOutOfCore(false); + } else { + // If this node lies completely inside the clipping region and it is being read + // from a memory-mapped file, delay loading of its buffer until the buffer + // is actually accessed. (If this node requires clipping, its buffer + // must be accessed and therefore must be loaded.) + io::MappedFile::Ptr mappedFile = io::getMappedFilePtr(is); + const bool delayLoad = ((mappedFile.get() != nullptr) && clipBBox.isInside(nodeBBox)); + + if (delayLoad) { + mBuffer.setOutOfCore(true); + mBuffer.mFileInfo = new typename Buffer::FileInfo; + mBuffer.mFileInfo->meta = meta; + mBuffer.mFileInfo->bufpos = is.tellg(); + mBuffer.mFileInfo->mapping = mappedFile; + // Save the offset to the value mask, because the in-memory copy + // might change before the value buffer gets read. + mBuffer.mFileInfo->maskpos = maskpos; + // Skip over voxel values. + skipCompressedValues(seekable, is, fromHalf); + } else { + mBuffer.allocate(); + io::readCompressedValues(is, mBuffer.mData, SIZE, mValueMask, fromHalf); + mBuffer.setOutOfCore(false); + + // Get this tree's background value. + T background = zeroVal(); + if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + this->clip(clipBBox, background); + } + } + + if (numBuffers > 1) { + // Read in and discard auxiliary buffers that were created with earlier + // versions of the library. (Auxiliary buffers are not mask compressed.) + const bool zipped = io::getDataCompression(is) & io::COMPRESS_ZIP; + Buffer temp; + for (int i = 1; i < numBuffers; ++i) { + if (fromHalf) { + io::HalfReader::isReal, T>::read(is, temp.mData, SIZE, zipped); + } else { + io::readData(is, temp.mData, SIZE, zipped); + } + } + } + + // increment the leaf number + if (meta) meta->setLeaf(meta->leaf() + 1); +} + + +template +inline void +LeafNode::writeBuffers(std::ostream& os, bool toHalf) const +{ + // Write out the value mask. + mValueMask.save(os); + + mBuffer.loadValues(); + + io::writeCompressedValues(os, mBuffer.mData, SIZE, + mValueMask, /*childMask=*/NodeMaskType(), toHalf); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::operator==(const LeafNode& other) const +{ + return mOrigin == other.mOrigin && + mValueMask == other.valueMask() && + mBuffer == other.mBuffer; +} + + +template +inline Index64 +LeafNode::memUsage() const +{ + // Use sizeof(*this) to capture alignment-related padding + // (but note that sizeof(*this) includes sizeof(mBuffer)). + return sizeof(*this) + mBuffer.memUsage() - sizeof(mBuffer); +} + + +template +inline void +LeafNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + CoordBBox this_bbox = this->getNodeBoundingBox(); + if (bbox.isInside(this_bbox)) return;//this LeafNode is already enclosed in the bbox + if (ValueOnCIter iter = this->cbeginValueOn()) {//any active values? + if (visitVoxels) {//use voxel granularity? + this_bbox.reset(); + for(; iter; ++iter) this_bbox.expand(this->offsetToLocalCoord(iter.pos())); + this_bbox.translate(this->origin()); + } + bbox.expand(this_bbox); + } +} + + +template +template +inline bool +LeafNode::hasSameTopology(const LeafNode* other) const +{ + assert(other); + return (Log2Dim == OtherLog2Dim && mValueMask == other->getValueMask()); +} + +template +inline bool +LeafNode::isConstant(ValueType& firstValue, + bool& state, + const ValueType& tolerance) const +{ + if (!mValueMask.isConstant(state)) return false;// early termination + firstValue = mBuffer[0]; + for (Index i = 1; i < SIZE; ++i) { + if ( !math::isApproxEqual(mBuffer[i], firstValue, tolerance) ) return false;// early termination + } + return true; +} + +template +inline bool +LeafNode::isConstant(ValueType& minValue, + ValueType& maxValue, + bool& state, + const ValueType& tolerance) const +{ + if (!mValueMask.isConstant(state)) return false;// early termination + minValue = maxValue = mBuffer[0]; + for (Index i = 1; i < SIZE; ++i) { + const T& v = mBuffer[i]; + if (v < minValue) { + if ((maxValue - v) > tolerance) return false;// early termination + minValue = v; + } else if (v > maxValue) { + if ((v - minValue) > tolerance) return false;// early termination + maxValue = v; + } + } + return true; +} + +template +inline T +LeafNode::medianAll(T *tmp) const +{ + std::unique_ptr data(nullptr); + if (tmp == nullptr) {//allocate temporary storage + data.reset(new T[NUM_VALUES]); + tmp = data.get(); + } + if (tmp != mBuffer.data()) { + const T* src = mBuffer.data(); + for (T* dst = tmp; dst-tmp < NUM_VALUES;) *dst++ = *src++; + } + static const size_t midpoint = (NUM_VALUES - 1) >> 1; + std::nth_element(tmp, tmp + midpoint, tmp + NUM_VALUES); + return tmp[midpoint]; +} + +template +inline Index +LeafNode::medianOn(T &value, T *tmp) const +{ + const Index count = mValueMask.countOn(); + if (count == NUM_VALUES) {//special case: all voxels are active + value = this->medianAll(tmp); + return NUM_VALUES; + } else if (count == 0) { + return 0; + } + std::unique_ptr data(nullptr); + if (tmp == nullptr) {//allocate temporary storage + data.reset(new T[count]);// 0 < count < NUM_VALUES + tmp = data.get(); + } + for (auto iter=this->cbeginValueOn(); iter; ++iter) *tmp++ = *iter; + T *begin = tmp - count; + const size_t midpoint = (count - 1) >> 1; + std::nth_element(begin, begin + midpoint, tmp); + value = begin[midpoint]; + return count; +} + +template +inline Index +LeafNode::medianOff(T &value, T *tmp) const +{ + const Index count = mValueMask.countOff(); + if (count == NUM_VALUES) {//special case: all voxels are inactive + value = this->medianAll(tmp); + return NUM_VALUES; + } else if (count == 0) { + return 0; + } + std::unique_ptr data(nullptr); + if (tmp == nullptr) {//allocate temporary storage + data.reset(new T[count]);// 0 < count < NUM_VALUES + tmp = data.get(); + } + for (auto iter=this->cbeginValueOff(); iter; ++iter) *tmp++ = *iter; + T *begin = tmp - count; + const size_t midpoint = (count - 1) >> 1; + std::nth_element(begin, begin + midpoint, tmp); + value = begin[midpoint]; + return count; +} + +//////////////////////////////////////// + + +template +inline void +LeafNode::addTile(Index /*level*/, const Coord& xyz, const ValueType& val, bool active) +{ + this->addTile(this->coordToOffset(xyz), val, active); +} + +template +inline void +LeafNode::addTile(Index offset, const ValueType& val, bool active) +{ + assert(offset < SIZE); + setValueOnly(offset, val); + setActiveState(offset, active); +} + +template +template +inline void +LeafNode::addTileAndCache(Index level, const Coord& xyz, + const ValueType& val, bool active, AccessorT&) +{ + this->addTile(level, xyz, val, active); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::resetBackground(const ValueType& oldBackground, + const ValueType& newBackground) +{ + if (!this->allocate()) return; + + typename NodeMaskType::OffIterator iter; + // For all inactive values... + for (iter = this->mValueMask.beginOff(); iter; ++iter) { + ValueType &inactiveValue = mBuffer[iter.pos()]; + if (math::isApproxEqual(inactiveValue, oldBackground)) { + inactiveValue = newBackground; + } else if (math::isApproxEqual(inactiveValue, math::negative(oldBackground))) { + inactiveValue = math::negative(newBackground); + } + } +} + + +template +template +inline void +LeafNode::merge(const LeafNode& other) +{ + if (!this->allocate()) return; + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy == MERGE_NODES) return; + typename NodeMaskType::OnIterator iter = other.valueMask().beginOn(); + for (; iter; ++iter) { + const Index n = iter.pos(); + if (mValueMask.isOff(n)) { + mBuffer[n] = other.mBuffer[n]; + mValueMask.setOn(n); + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +template +template +inline void +LeafNode::merge(const LeafNode& other, + const ValueType& /*bg*/, const ValueType& /*otherBG*/) +{ + this->template merge(other); +} + +template +template +inline void +LeafNode::merge(const ValueType& tileValue, bool tileActive) +{ + if (!this->allocate()) return; + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + if (!tileActive) return; + // Replace all inactive values with the active tile value. + for (typename NodeMaskType::OffIterator iter = mValueMask.beginOff(); iter; ++iter) { + const Index n = iter.pos(); + mBuffer[n] = tileValue; + mValueMask.setOn(n); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline void +LeafNode::topologyUnion(const LeafNode& other) +{ + mValueMask |= other.valueMask(); +} + +template +template +inline void +LeafNode::topologyIntersection(const LeafNode& other, + const ValueType&) +{ + mValueMask &= other.valueMask(); +} + +template +template +inline void +LeafNode::topologyDifference(const LeafNode& other, + const ValueType&) +{ + mValueMask &= !other.valueMask(); +} + +template +inline void +LeafNode::negate() +{ + if (!this->allocate()) return; + + for (Index i = 0; i < SIZE; ++i) { + mBuffer[i] = -mBuffer[i]; + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine(const LeafNode& other, CombineOp& op) +{ + if (!this->allocate()) return; + + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + op(args.setARef(mBuffer[i]) + .setAIsActive(mValueMask.isOn(i)) + .setBRef(other.mBuffer[i]) + .setBIsActive(other.valueMask().isOn(i)) + .setResultRef(mBuffer[i])); + mValueMask.set(i, args.resultIsActive()); + } +} + + +template +template +inline void +LeafNode::combine(const ValueType& value, bool valueIsActive, CombineOp& op) +{ + if (!this->allocate()) return; + + CombineArgs args; + args.setBRef(value).setBIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + op(args.setARef(mBuffer[i]) + .setAIsActive(mValueMask.isOn(i)) + .setResultRef(mBuffer[i])); + mValueMask.set(i, args.resultIsActive()); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine2(const LeafNode& other, const OtherType& value, + bool valueIsActive, CombineOp& op) +{ + if (!this->allocate()) return; + + CombineArgs args; + args.setBRef(value).setBIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + op(args.setARef(other.mBuffer[i]) + .setAIsActive(other.valueMask().isOn(i)) + .setResultRef(mBuffer[i])); + mValueMask.set(i, args.resultIsActive()); + } +} + + +template +template +inline void +LeafNode::combine2(const ValueType& value, const OtherNodeT& other, + bool valueIsActive, CombineOp& op) +{ + if (!this->allocate()) return; + + CombineArgs args; + args.setARef(value).setAIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + op(args.setBRef(other.mBuffer[i]) + .setBIsActive(other.valueMask().isOn(i)) + .setResultRef(mBuffer[i])); + mValueMask.set(i, args.resultIsActive()); + } +} + + +template +template +inline void +LeafNode::combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp& op) +{ + if (!this->allocate()) return; + + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + mValueMask.set(i, b0.valueMask().isOn(i) || b1.valueMask().isOn(i)); + op(args.setARef(b0.mBuffer[i]) + .setAIsActive(b0.valueMask().isOn(i)) + .setBRef(b1.mBuffer[i]) + .setBIsActive(b1.valueMask().isOn(i)) + .setResultRef(mBuffer[i])); + mValueMask.set(i, args.resultIsActive()); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visitActiveBBox(BBoxOp& op) const +{ + if (op.template descent()) { + for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(CoordBBox::createCube(i.getCoord(), 1)); +#else + op.template operator()(CoordBBox::createCube(i.getCoord(), 1)); +#endif + } + } else { +#ifdef _MSC_VER + op.operator()(this->getNodeBoundingBox()); +#else + op.template operator()(this->getNodeBoundingBox()); +#endif + } +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::doVisit(NodeT& self, VisitorOp& op) +{ + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(iter); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) +{ + doVisit2Node(*this, other, op); +} + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const +{ + doVisit2Node(*this, other, op); +} + + +template +template< + typename NodeT, + typename OtherNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp& op) +{ + // Allow the two nodes to have different ValueTypes, but not different dimensions. + static_assert(OtherNodeT::SIZE == NodeT::SIZE, + "can't visit nodes of different sizes simultaneously"); + static_assert(OtherNodeT::LEVEL == NodeT::LEVEL, + "can't visit nodes at different tree levels simultaneously"); + + ChildAllIterT iter = self.beginChildAll(); + OtherChildAllIterT otherIter = other.beginChildAll(); + + for ( ; iter && otherIter; ++iter, ++otherIter) { + op(iter, otherIter); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) +{ + doVisit2( + *this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const +{ + doVisit2( + *this, otherIter, op, otherIsLHS); +} + + +template +template< + typename NodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::doVisit2(NodeT& self, OtherChildAllIterT& otherIter, + VisitorOp& op, bool otherIsLHS) +{ + if (!otherIter) return; + + if (otherIsLHS) { + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(otherIter, iter); + } + } else { + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(iter, otherIter); + } + } +} + + +//////////////////////////////////////// + + +template +inline std::ostream& +operator<<(std::ostream& os, const typename LeafNode::Buffer& buf) +{ + for (Index32 i = 0, N = buf.size(); i < N; ++i) os << buf.mData[i] << ", "; + return os; +} + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +//////////////////////////////////////// + + +// Specialization for LeafNodes of type bool +#include "LeafNodeBool.h" + +// Specialization for LeafNodes with mask information only +#include "LeafNodeMask.h" + +#endif // OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/LeafNodeBool.h b/openvdb/tree/LeafNodeBool.h new file mode 100644 index 00000000..d155c4c9 --- /dev/null +++ b/openvdb/tree/LeafNodeBool.h @@ -0,0 +1,1733 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_TREE_LEAF_NODE_BOOL_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_LEAF_NODE_BOOL_HAS_BEEN_INCLUDED + +#include +#include // for io::readData(), etc. +#include // for math::isZero() +#include +#include "LeafNode.h" +#include "Iterator.h" +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +/// @brief LeafNode specialization for values of type bool that stores both +/// the active states and the values of (2^Log2Dim)^3 voxels as bit masks +template +class LeafNode +{ +public: + using LeafNodeType = LeafNode; + using BuildType = bool; + using ValueType = bool; + using Buffer = LeafBuffer; + using NodeMaskType = util::NodeMask; + using Ptr = SharedPtr; + + // These static declarations must be on separate lines to avoid VC9 compiler errors. + static const Index LOG2DIM = Log2Dim; // needed by parent nodes + static const Index TOTAL = Log2Dim; // needed by parent nodes + static const Index DIM = 1 << TOTAL; // dimension along one coordinate direction + static const Index NUM_VALUES = 1 << 3 * Log2Dim; + static const Index NUM_VOXELS = NUM_VALUES; // total number of voxels represented by this node + static const Index SIZE = NUM_VALUES; + static const Index LEVEL = 0; // level 0 = leaf + + /// @brief ValueConverter::Type is the type of a LeafNode having the same + /// dimensions as this node but a different value type, T. + template + struct ValueConverter { using Type = LeafNode; }; + + /// @brief SameConfiguration::value is @c true if and only if + /// OtherNodeType is the type of a LeafNode with the same dimensions as this node. + template + struct SameConfiguration { + static const bool value = SameLeafConfig::value; + }; + + + /// Default constructor + LeafNode(); + + /// Constructor + /// @param xyz the coordinates of a voxel that lies within the node + /// @param value the initial value for all of this node's voxels + /// @param active the active state to which to initialize all voxels + explicit LeafNode(const Coord& xyz, bool value = false, bool active = false); + + /// "Partial creation" constructor used during file input + LeafNode(PartialCreate, const Coord& xyz, bool value = false, bool active = false); + + /// Deep copy constructor + LeafNode(const LeafNode&); + + /// Deep assignment operator + LeafNode& operator=(const LeafNode&) = default; + + /// Value conversion copy constructor + template + explicit LeafNode(const LeafNode& other); + + /// Topology copy constructor + template + LeafNode(const LeafNode& other, TopologyCopy); + + //@{ + /// @brief Topology copy constructor + /// @note This variant exists mainly to enable template instantiation. + template + LeafNode(const LeafNode& other, bool offValue, bool onValue, TopologyCopy); + template + LeafNode(const LeafNode& other, bool background, TopologyCopy); + //@} + + /// Destructor + ~LeafNode(); + + // + // Statistics + // + /// Return log2 of the size of the buffer storage. + static Index log2dim() { return Log2Dim; } + /// Return the number of voxels in each dimension. + static Index dim() { return DIM; } + static Index size() { return SIZE; } + static Index numValues() { return SIZE; } + static Index getLevel() { return LEVEL; } + static void getNodeLog2Dims(std::vector& dims) { dims.push_back(Log2Dim); } + static Index getChildDim() { return 1; } + + static Index32 leafCount() { return 1; } + /// no-op + void nodeCount(std::vector &) const {} + static Index32 nonLeafCount() { return 0; } + + /// Return the number of active voxels. + Index64 onVoxelCount() const { return mValueMask.countOn(); } + /// Return the number of inactive voxels. + Index64 offVoxelCount() const { return mValueMask.countOff(); } + Index64 onLeafVoxelCount() const { return onVoxelCount(); } + Index64 offLeafVoxelCount() const { return offVoxelCount(); } + static Index64 onTileCount() { return 0; } + static Index64 offTileCount() { return 0; } + + /// Return @c true if this node has no active voxels. + bool isEmpty() const { return mValueMask.isOff(); } + /// Return @c true if this node only contains active voxels. + bool isDense() const { return mValueMask.isOn(); } + /// @brief Return @c true if memory for this node's buffer has been allocated. + /// @details Currently, boolean leaf nodes don't support partial creation, + /// so this always returns @c true. + bool isAllocated() const { return true; } + /// @brief Allocate memory for this node's buffer if it has not already been allocated. + /// @details Currently, boolean leaf nodes don't support partial creation, + /// so this has no effect. + bool allocate() { return true; } + + /// Return the memory in bytes occupied by this node. + Index64 memUsage() const; + + /// Expand the given bounding box so that it includes this leaf node's active voxels. + /// If visitVoxels is false this LeafNode will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; + + /// @brief Return the bounding box of this node, i.e., the full index space + /// spanned by this leaf node. + CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); } + + /// Set the grid index coordinates of this node's local origin. + void setOrigin(const Coord& origin) { mOrigin = origin; } + //@{ + /// Return the grid index coordinates of this node's local origin. + const Coord& origin() const { return mOrigin; } + void getOrigin(Coord& origin) const { origin = mOrigin; } + void getOrigin(Int32& x, Int32& y, Int32& z) const { mOrigin.asXYZ(x, y, z); } + //@} + + /// Return the linear table offset of the given global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static Coord offsetToLocalCoord(Index n); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(Index n) const; + + /// Return a string representation of this node. + std::string str() const; + + /// @brief Return @c true if the given node (which may have a different @c ValueType + /// than this node) has the same active value topology as this node. + template + bool hasSameTopology(const LeafNode* other) const; + + /// Check for buffer equivalence by value. + bool operator==(const LeafNode&) const; + bool operator!=(const LeafNode&) const; + + // + // Buffer management + // + /// @brief Exchange this node's data buffer with the given data buffer + /// without changing the active states of the values. + void swap(Buffer& other) { mBuffer.swap(other); } + const Buffer& buffer() const { return mBuffer; } + Buffer& buffer() { return mBuffer; } + + // + // I/O methods + // + /// Read in just the topology. + void readTopology(std::istream&, bool fromHalf = false); + /// Write out just the topology. + void writeTopology(std::ostream&, bool toHalf = false) const; + + /// Read in the topology and the origin. + void readBuffers(std::istream&, bool fromHalf = false); + void readBuffers(std::istream& is, const CoordBBox&, bool fromHalf = false); + /// Write out the topology and the origin. + void writeBuffers(std::ostream&, bool toHalf = false) const; + + // + // Accessor methods + // + /// Return the value of the voxel at the given coordinates. + const bool& getValue(const Coord& xyz) const; + /// Return the value of the voxel at the given offset. + const bool& getValue(Index offset) const; + + /// @brief Return @c true if the voxel at the given coordinates is active. + /// @param xyz the coordinates of the voxel to be probed + /// @param[out] val the value of the voxel at the given coordinates + bool probeValue(const Coord& xyz, bool& val) const; + + /// Return the level (0) at which leaf node values reside. + static Index getValueLevel(const Coord&) { return LEVEL; } + + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveState(Index offset, bool on) { assert(offsetcoordToOffset(xyz)); } + /// Mark the voxel at the given offset as inactive but don't change its value. + void setValueOff(Index offset) { assert(offset < SIZE); mValueMask.setOff(offset); } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, bool val); + /// Set the value of the voxel at the given offset and mark the voxel as inactive. + void setValueOff(Index offset, bool val); + + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz) { mValueMask.setOn(this->coordToOffset(xyz)); } + /// Mark the voxel at the given offset as active but don't change its value. + void setValueOn(Index offset) { assert(offset < SIZE); mValueMask.setOn(offset); } + + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOn(const Coord& xyz, bool val); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, bool val) { this->setValueOn(xyz, val); } + /// Set the value of the voxel at the given offset and mark the voxel as active. + void setValueOn(Index offset, bool val); + + /// @brief Apply a functor to the value of the voxel at the given offset + /// and mark the voxel as active. + template + void modifyValue(Index offset, const ModifyOp& op); + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// Mark all voxels as active but don't change their values. + void setValuesOn() { mValueMask.setOn(); } + /// Mark all voxels as inactive but don't change their values. + void setValuesOff() { mValueMask.setOff(); } + + /// Return @c true if the voxel at the given coordinates is active. + bool isValueOn(const Coord& xyz) const { return mValueMask.isOn(this->coordToOffset(xyz)); } + /// Return @c true if the voxel at the given offset is active. + bool isValueOn(Index offset) const { assert(offset < SIZE); return mValueMask.isOn(offset); } + + /// Return @c false since leaf nodes never contain tiles. + static bool hasActiveTiles() { return false; } + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&, bool background); + + /// Set all voxels within an axis-aligned box to the specified value and active state. + void fill(const CoordBBox& bbox, bool value, bool active = true); + /// Set all voxels within an axis-aligned box to the specified value and active state. + void denseFill(const CoordBBox& bbox, bool val, bool on = true) { this->fill(bbox, val, on); } + + /// Set all voxels to the specified value but don't change their active states. + void fill(const bool& value); + /// Set all voxels to the specified value and active state. + void fill(const bool& value, bool active); + + /// @brief Copy into a dense grid the values of the voxels that lie within + /// a given bounding box. + /// + /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid + /// @param dense dense grid with a stride in @e z of one (see tools::Dense + /// in tools/Dense.h for the required API) + /// + /// @note @a bbox is assumed to be identical to or contained in the coordinate domains + /// of both the dense grid and this node, i.e., no bounds checking is performed. + /// @note Consider using tools::CopyToDense in tools/Dense.h + /// instead of calling this method directly. + template + void copyToDense(const CoordBBox& bbox, DenseT& dense) const; + + /// @brief Copy from a dense grid into this node the values of the voxels + /// that lie within a given bounding box. + /// @details Only values that are different (by more than the given tolerance) + /// from the background value will be active. Other values are inactive + /// and truncated to the background value. + /// + /// @param bbox inclusive bounding box of the voxels to be copied into this node + /// @param dense dense grid with a stride in @e z of one (see tools::Dense + /// in tools/Dense.h for the required API) + /// @param background background value of the tree that this node belongs to + /// @param tolerance tolerance within which a value equals the background value + /// + /// @note @a bbox is assumed to be identical to or contained in the coordinate domains + /// of both the dense grid and this node, i.e., no bounds checking is performed. + /// @note Consider using tools::CopyFromDense in tools/Dense.h + /// instead of calling this method directly. + template + void copyFromDense(const CoordBBox& bbox, const DenseT& dense, bool background, bool tolerance); + + /// @brief Return the value of the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template + const bool& getValueAndCache(const Coord& xyz, AccessorT&) const {return this->getValue(xyz);} + + /// @brief Return @c true if the voxel at the given coordinates is active. + /// @note Used internally by ValueAccessor. + template + bool isValueOnAndCache(const Coord& xyz, AccessorT&) const { return this->isValueOn(xyz); } + + /// @brief Change the value of the voxel at the given coordinates and mark it as active. + /// @note Used internally by ValueAccessor. + template + void setValueAndCache(const Coord& xyz, bool val, AccessorT&) { this->setValueOn(xyz, val); } + + /// @brief Change the value of the voxel at the given coordinates + /// but preserve its state. + /// @note Used internally by ValueAccessor. + template + void setValueOnlyAndCache(const Coord& xyz, bool val, AccessorT&) {this->setValueOnly(xyz,val);} + + /// @brief Change the value of the voxel at the given coordinates and mark it as inactive. + /// @note Used internally by ValueAccessor. + template + void setValueOffAndCache(const Coord& xyz, bool value, AccessorT&) + { + this->setValueOff(xyz, value); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValue(xyz, op); + } + + /// Apply a functor to the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValueAndActiveState(xyz, op); + } + + /// @brief Set the active state of the voxel at the given coordinates + /// without changing its value. + /// @note Used internally by ValueAccessor. + template + void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&) + { + this->setActiveState(xyz, on); + } + + /// @brief Return @c true if the voxel at the given coordinates is active + /// and return the voxel value in @a val. + /// @note Used internally by ValueAccessor. + template + bool probeValueAndCache(const Coord& xyz, bool& val, AccessorT&) const + { + return this->probeValue(xyz, val); + } + + /// @brief Return the LEVEL (=0) at which leaf node values reside. + /// @note Used internally by ValueAccessor. + template + static Index getValueLevelAndCache(const Coord&, AccessorT&) { return LEVEL; } + + /// @brief Return a const reference to the first entry in the buffer. + /// @note Since it's actually a reference to a static data member + /// it should not be converted to a non-const pointer! + const bool& getFirstValue() const { if (mValueMask.isOn(0)) return Buffer::sOn; else return Buffer::sOff; } + /// @brief Return a const reference to the last entry in the buffer. + /// @note Since it's actually a reference to a static data member + /// it should not be converted to a non-const pointer! + const bool& getLastValue() const { if (mValueMask.isOn(SIZE-1)) return Buffer::sOn; else return Buffer::sOff; } + + /// Return @c true if all of this node's voxels have the same active state + /// and are equal to within the given tolerance, and return the value in + /// @a constValue and the active state in @a state. + bool isConstant(bool& constValue, bool& state, bool tolerance = 0) const; + + /// @brief Computes the median value of all the active and inactive voxels in this node. + /// @return The median value. + /// + /// @details The median for boolean values is defined as the mode + /// of the values, i.e. the value that occurs most often. + bool medianAll() const; + + /// @brief Computes the median value of all the active voxels in this node. + /// @return The number of active voxels. + /// @param value Updated with the median value of all the active voxels. + /// + /// @details The median for boolean values is defined as the mode + /// of the values, i.e. the value that occurs most often. + Index medianOn(ValueType &value) const; + + /// @brief Computes the median value of all the inactive voxels in this node. + /// @return The number of inactive voxels. + /// @param value Updated with the median value of all the inactive voxels. + /// + /// @details The median for boolean values is defined as the mode + /// of the values, i.e. the value that occurs most often. + Index medianOff(ValueType &value) const; + + /// Return @c true if all of this node's values are inactive. + bool isInactive() const { return mValueMask.isOff(); } + + void resetBackground(bool oldBackground, bool newBackground); + + void negate() { mBuffer.mData.toggle(); } + + template + void merge(const LeafNode& other, bool bg = false, bool otherBG = false); + template void merge(bool tileValue, bool tileActive); + + /// @brief No-op + /// @details This function exists only to enable template instantiation. + void voxelizeActiveTiles(bool = true) {} + + /// @brief Union this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active if either of the original voxels + /// were active. + /// + /// @note This operation modifies only active states, not values. + template + void topologyUnion(const LeafNode& other); + + /// @brief Intersect this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active only if both of the original voxels + /// were active. + /// + /// @details The last dummy argument is required to match the signature + /// for InternalNode::topologyIntersection. + /// + /// @note This operation modifies only active states, not + /// values. Also note that this operation can result in all voxels + /// being inactive so consider subsequnetly calling prune. + template + void topologyIntersection(const LeafNode& other, const bool&); + + /// @brief Difference this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this LeafNode and inactive in the other LeafNode. + /// + /// @details The last dummy argument is required to match the signature + /// for InternalNode::topologyDifference. + /// + /// @note This operation modifies only active states, not values. + /// Also, because it can deactivate all of this node's voxels, + /// consider subsequently calling prune. + template + void topologyDifference(const LeafNode& other, const bool&); + + template + void combine(const LeafNode& other, CombineOp& op); + template + void combine(bool, bool valueIsActive, CombineOp& op); + + template + void combine2(const LeafNode& other, const OtherType&, bool valueIsActive, CombineOp&); + template + void combine2(bool, const OtherNodeT& other, bool valueIsActive, CombineOp&); + template + void combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp&); + + /// @brief Calls the templated functor BBoxOp with bounding box information. + /// An additional level argument is provided to the callback. + /// + /// @note The bounding boxes are guarenteed to be non-overlapping. + template void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&); + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&) const; + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false); + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const; + + //@{ + /// This function exists only to enable template instantiation. + void prune(const ValueType& /*tolerance*/ = zeroVal()) {} + void addLeaf(LeafNode*) {} + template + void addLeafAndCache(LeafNode*, AccessorT&) {} + template + NodeT* stealNode(const Coord&, const ValueType&, bool) { return nullptr; } + template + NodeT* probeNode(const Coord&) { return nullptr; } + template + const NodeT* probeConstNode(const Coord&) const { return nullptr; } + template void getNodes(ArrayT&) const {} + template void stealNodes(ArrayT&, const ValueType&, bool) {} + //@} + + void addTile(Index level, const Coord&, bool val, bool active); + void addTile(Index offset, bool val, bool active); + template + void addTileAndCache(Index level, const Coord&, bool val, bool active, AccessorT&); + + //@{ + /// @brief Return a pointer to this node. + LeafNode* touchLeaf(const Coord&) { return this; } + template + LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + LeafNode* probeLeaf(const Coord&) { return this; } + template + LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + //@{ + /// @brief Return a @const pointer to this node. + const LeafNode* probeLeaf(const Coord&) const { return this; } + template + const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const LeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + // + // Iterators + // +protected: + using MaskOnIter = typename NodeMaskType::OnIterator; + using MaskOffIter = typename NodeMaskType::OffIterator; + using MaskDenseIter = typename NodeMaskType::DenseIterator; + + template + struct ValueIter: + // Derives from SparseIteratorBase, but can also be used as a dense iterator, + // if MaskIterT is a dense mask iterator type. + public SparseIteratorBase, NodeT, ValueT> + { + using BaseT = SparseIteratorBase; + + ValueIter() {} + ValueIter(const MaskIterT& iter, NodeT* parent): BaseT(iter, parent) {} + + const bool& getItem(Index pos) const { return this->parent().getValue(pos); } + const bool& getValue() const { return this->getItem(this->pos()); } + + // Note: setItem() can't be called on const iterators. + void setItem(Index pos, bool value) const { this->parent().setValueOnly(pos, value); } + // Note: setValue() can't be called on const iterators. + void setValue(bool value) const { this->setItem(this->pos(), value); } + + // Note: modifyItem() can't be called on const iterators. + template + void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); } + // Note: modifyValue() can't be called on const iterators. + template + void modifyValue(const ModifyOp& op) const { this->modifyItem(this->pos(), op); } + }; + + /// Leaf nodes have no children, so their child iterators have no get/set accessors. + template + struct ChildIter: + public SparseIteratorBase, NodeT, bool> + { + ChildIter() {} + ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ChildIter, NodeT, bool>(iter, parent) {} + }; + + template + struct DenseIter: public DenseIteratorBase< + MaskDenseIter, DenseIter, NodeT, /*ChildT=*/void, ValueT> + { + using BaseT = DenseIteratorBase; + using NonConstValueT = typename BaseT::NonConstValueType; + + DenseIter() {} + DenseIter(const MaskDenseIter& iter, NodeT* parent): BaseT(iter, parent) {} + + bool getItem(Index pos, void*& child, NonConstValueT& value) const + { + value = this->parent().getValue(pos); + child = nullptr; + return false; // no child + } + + // Note: setItem() can't be called on const iterators. + //void setItem(Index pos, void* child) const {} + + // Note: unsetItem() can't be called on const iterators. + void unsetItem(Index pos, const ValueT& val) const {this->parent().setValueOnly(pos, val);} + }; + +public: + using ValueOnIter = ValueIter; + using ValueOnCIter = ValueIter; + using ValueOffIter = ValueIter; + using ValueOffCIter = ValueIter; + using ValueAllIter = ValueIter; + using ValueAllCIter = ValueIter; + using ChildOnIter = ChildIter; + using ChildOnCIter = ChildIter; + using ChildOffIter = ChildIter; + using ChildOffCIter = ChildIter; + using ChildAllIter = DenseIter; + using ChildAllCIter = DenseIter; + + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); } + ValueOnCIter beginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); } + ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); } + ValueOffCIter beginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); } + ValueOffIter beginValueOff() { return ValueOffIter(mValueMask.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); } + ValueAllCIter beginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); } + ValueAllIter beginValueAll() { return ValueAllIter(mValueMask.beginDense(), this); } + + ValueOnCIter cendValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); } + ValueOnCIter endValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); } + ValueOnIter endValueOn() { return ValueOnIter(mValueMask.endOn(), this); } + ValueOffCIter cendValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); } + ValueOffCIter endValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); } + ValueOffIter endValueOff() { return ValueOffIter(mValueMask.endOff(), this); } + ValueAllCIter cendValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); } + ValueAllCIter endValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); } + ValueAllIter endValueAll() { return ValueAllIter(mValueMask.endDense(), this); } + + // Note that [c]beginChildOn() and [c]beginChildOff() actually return end iterators, + // because leaf nodes have no children. + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } + ChildOnCIter beginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } + ChildOnIter beginChildOn() { return ChildOnIter(mValueMask.endOn(), this); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } + ChildOffCIter beginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } + ChildOffIter beginChildOff() { return ChildOffIter(mValueMask.endOff(), this); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); } + ChildAllCIter beginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); } + ChildAllIter beginChildAll() { return ChildAllIter(mValueMask.beginDense(), this); } + + ChildOnCIter cendChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } + ChildOnCIter endChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); } + ChildOnIter endChildOn() { return ChildOnIter(mValueMask.endOn(), this); } + ChildOffCIter cendChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } + ChildOffCIter endChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); } + ChildOffIter endChildOff() { return ChildOffIter(mValueMask.endOff(), this); } + ChildAllCIter cendChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); } + ChildAllCIter endChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); } + ChildAllIter endChildAll() { return ChildAllIter(mValueMask.endDense(), this); } + + // + // Mask accessors + // + bool isValueMaskOn(Index n) const { return mValueMask.isOn(n); } + bool isValueMaskOn() const { return mValueMask.isOn(); } + bool isValueMaskOff(Index n) const { return mValueMask.isOff(n); } + bool isValueMaskOff() const { return mValueMask.isOff(); } + const NodeMaskType& getValueMask() const { return mValueMask; } + const NodeMaskType& valueMask() const { return mValueMask; } + NodeMaskType& getValueMask() { return mValueMask; } + void setValueMask(const NodeMaskType& mask) { mValueMask = mask; } + bool isChildMaskOn(Index) const { return false; } // leaf nodes have no children + bool isChildMaskOff(Index) const { return true; } + bool isChildMaskOff() const { return true; } +protected: + void setValueMask(Index n, bool on) { mValueMask.set(n, on); } + void setValueMaskOn(Index n) { mValueMask.setOn(n); } + void setValueMaskOff(Index n) { mValueMask.setOff(n); } + + /// Compute the origin of the leaf node that contains the voxel with the given coordinates. + static void evalNodeOrigin(Coord& xyz) { xyz &= ~(DIM - 1); } + + template + static inline void doVisit(NodeT&, VisitorOp&); + + template + static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&); + + template + static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); + + + /// Bitmask that determines which voxels are active + NodeMaskType mValueMask; + /// Bitmask representing the values of voxels + Buffer mBuffer; + /// Global grid index coordinates (x,y,z) of the local origin of this node + Coord mOrigin; + +private: + /// @brief During topology-only construction, access is needed + /// to protected/private members of other template instances. + template friend class LeafNode; + + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + + //@{ + /// Allow iterators to call mask accessor methods (see below). + /// @todo Make mask accessors public? + friend class IteratorBase; + friend class IteratorBase; + friend class IteratorBase; + //@} + +}; // class LeafNode + + +//////////////////////////////////////// + + +template +inline +LeafNode::LeafNode() + : mOrigin(0, 0, 0) +{ +} + + +template +inline +LeafNode::LeafNode(const Coord& xyz, bool value, bool active) + : mValueMask(active) + , mBuffer(value) + , mOrigin(xyz & (~(DIM - 1))) +{ +} + + +template +inline +LeafNode::LeafNode(PartialCreate, const Coord& xyz, bool value, bool active) + : mValueMask(active) + , mBuffer(value) + , mOrigin(xyz & (~(DIM - 1))) +{ + /// @todo For now, this is identical to the non-PartialCreate constructor. + /// Consider modifying the Buffer class to allow it to be constructed + /// without allocating a bitmask. +} + + +template +inline +LeafNode::LeafNode(const LeafNode &other) + : mValueMask(other.valueMask()) + , mBuffer(other.mBuffer) + , mOrigin(other.mOrigin) +{ +} + + +// Copy-construct from a leaf node with the same configuration but a different ValueType. +template +template +inline +LeafNode::LeafNode(const LeafNode& other) + : mValueMask(other.valueMask()) + , mOrigin(other.origin()) +{ + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline bool convertValue(const ValueT& val) { return bool(val); } + }; + + for (Index i = 0; i < SIZE; ++i) { + mBuffer.setValue(i, Local::convertValue(other.mBuffer[i])); + } +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + bool background, TopologyCopy) + : mValueMask(other.valueMask()) + , mBuffer(background) + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, TopologyCopy) + : mValueMask(other.valueMask()) + , mBuffer(other.valueMask())// value = active state + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + bool offValue, bool onValue, TopologyCopy) + : mValueMask(other.valueMask()) + , mBuffer(other.valueMask()) + , mOrigin(other.origin()) +{ + if (offValue) { if (!onValue) mBuffer.mData.toggle(); else mBuffer.mData.setOn(); } +} + + +template +inline +LeafNode::~LeafNode() +{ +} + + +//////////////////////////////////////// + + +template +inline Index64 +LeafNode::memUsage() const +{ + // Use sizeof(*this) to capture alignment-related padding + return sizeof(*this); +} + + +template +inline void +LeafNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + CoordBBox this_bbox = this->getNodeBoundingBox(); + if (bbox.isInside(this_bbox)) return;//this LeafNode is already enclosed in the bbox + if (ValueOnCIter iter = this->cbeginValueOn()) {//any active values? + if (visitVoxels) {//use voxel granularity? + this_bbox.reset(); + for(; iter; ++iter) this_bbox.expand(this->offsetToLocalCoord(iter.pos())); + this_bbox.translate(this->origin()); + } + bbox.expand(this_bbox); + } +} + + +template +template +inline bool +LeafNode::hasSameTopology(const LeafNode* other) const +{ + assert(other); + return (Log2Dim == OtherLog2Dim && mValueMask == other->getValueMask()); +} + + +template +inline std::string +LeafNode::str() const +{ + std::ostringstream ostr; + ostr << "LeafNode @" << mOrigin << ": "; + for (Index32 n = 0; n < SIZE; ++n) ostr << (mValueMask.isOn(n) ? '#' : '.'); + return ostr.str(); +} + + +//////////////////////////////////////// + + +template +inline Index +LeafNode::coordToOffset(const Coord& xyz) +{ + assert ((xyz[0] & (DIM-1u)) < DIM && (xyz[1] & (DIM-1u)) < DIM && (xyz[2] & (DIM-1u)) < DIM); + return ((xyz[0] & (DIM-1u)) << 2*Log2Dim) + + ((xyz[1] & (DIM-1u)) << Log2Dim) + + (xyz[2] & (DIM-1u)); +} + + +template +inline Coord +LeafNode::offsetToLocalCoord(Index n) +{ + assert(n < (1 << 3*Log2Dim)); + Coord xyz; + xyz.setX(n >> 2*Log2Dim); + n &= ((1 << 2*Log2Dim) - 1); + xyz.setY(n >> Log2Dim); + xyz.setZ(n & ((1 << Log2Dim) - 1)); + return xyz; +} + + +template +inline Coord +LeafNode::offsetToGlobalCoord(Index n) const +{ + return (this->offsetToLocalCoord(n) + this->origin()); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::readTopology(std::istream& is, bool /*fromHalf*/) +{ + mValueMask.load(is); +} + + +template +inline void +LeafNode::writeTopology(std::ostream& os, bool /*toHalf*/) const +{ + mValueMask.save(os); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf) +{ + // Boolean LeafNodes don't currently implement lazy loading. + // Instead, load the full buffer, then clip it. + + this->readBuffers(is, fromHalf); + + // Get this tree's background value. + bool background = false; + if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + this->clip(clipBBox, background); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, bool /*fromHalf*/) +{ + // Read in the value mask. + mValueMask.load(is); + // Read in the origin. + is.read(reinterpret_cast(&mOrigin), sizeof(Coord::ValueType) * 3); + + if (io::getFormatVersion(is) >= OPENVDB_FILE_VERSION_BOOL_LEAF_OPTIMIZATION) { + // Read in the mask for the voxel values. + mBuffer.mData.load(is); + } else { + // Older files stored one or more bool arrays. + + // Read in the number of buffers, which should now always be one. + int8_t numBuffers = 0; + is.read(reinterpret_cast(&numBuffers), sizeof(int8_t)); + + // Read in the buffer. + // (Note: prior to the bool leaf optimization, buffers were always compressed.) + std::unique_ptr buf{new bool[SIZE]}; + io::readData(is, buf.get(), SIZE, /*isCompressed=*/true); + + // Transfer values to mBuffer. + mBuffer.mData.setOff(); + for (Index i = 0; i < SIZE; ++i) { + if (buf[i]) mBuffer.mData.setOn(i); + } + + if (numBuffers > 1) { + // Read in and discard auxiliary buffers that were created with + // earlier versions of the library. + for (int i = 1; i < numBuffers; ++i) { + io::readData(is, buf.get(), SIZE, /*isCompressed=*/true); + } + } + } +} + + +template +inline void +LeafNode::writeBuffers(std::ostream& os, bool /*toHalf*/) const +{ + // Write out the value mask. + mValueMask.save(os); + // Write out the origin. + os.write(reinterpret_cast(&mOrigin), sizeof(Coord::ValueType) * 3); + // Write out the voxel values. + mBuffer.mData.save(os); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::operator==(const LeafNode& other) const +{ + return mOrigin == other.mOrigin && + mValueMask == other.valueMask() && + mBuffer == other.mBuffer; +} + + +template +inline bool +LeafNode::operator!=(const LeafNode& other) const +{ + return !(this->operator==(other)); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::isConstant(bool& constValue, bool& state, bool tolerance) const +{ + if (!mValueMask.isConstant(state)) return false; + + // Note: if tolerance is true (i.e., 1), then all boolean values compare equal. + if (!tolerance && !(mBuffer.mData.isOn() || mBuffer.mData.isOff())) return false; + + constValue = mBuffer.mData.isOn(); + return true; +} + +//////////////////////////////////////// + +template +inline bool +LeafNode::medianAll() const +{ + const Index countTrue = mBuffer.mData.countOn(); + return countTrue > (NUM_VALUES >> 1); +} + +template +inline Index +LeafNode::medianOn(bool& state) const +{ + const NodeMaskType tmp = mBuffer.mData & mValueMask;//both true and active + const Index countTrueOn = tmp.countOn(), countOn = mValueMask.countOn(); + state = countTrueOn > (NUM_VALUES >> 1); + return countOn; +} + +template +inline Index +LeafNode::medianOff(bool& state) const +{ + const NodeMaskType tmp = mBuffer.mData & (!mValueMask);//both true and inactive + const Index countTrueOff = tmp.countOn(), countOff = mValueMask.countOff(); + state = countTrueOff > (NUM_VALUES >> 1); + return countOff; +} + +//////////////////////////////////////// + + +template +inline void +LeafNode::addTile(Index /*level*/, const Coord& xyz, bool val, bool active) +{ + this->addTile(this->coordToOffset(xyz), val, active); +} + +template +inline void +LeafNode::addTile(Index offset, bool val, bool active) +{ + assert(offset < SIZE); + this->setValueOnly(offset, val); + this->setActiveState(offset, active); +} + +template +template +inline void +LeafNode::addTileAndCache(Index level, const Coord& xyz, + bool val, bool active, AccessorT&) +{ + this->addTile(level, xyz, val, active); +} + + +//////////////////////////////////////// + + +template +inline const bool& +LeafNode::getValue(const Coord& xyz) const +{ + // This *CANNOT* use operator ? because Visual C++ + if (mBuffer.mData.isOn(this->coordToOffset(xyz))) return Buffer::sOn; else return Buffer::sOff; +} + + +template +inline const bool& +LeafNode::getValue(Index offset) const +{ + assert(offset < SIZE); + // This *CANNOT* use operator ? for Windows + if (mBuffer.mData.isOn(offset)) return Buffer::sOn; else return Buffer::sOff; +} + + +template +inline bool +LeafNode::probeValue(const Coord& xyz, bool& val) const +{ + const Index offset = this->coordToOffset(xyz); + val = mBuffer.mData.isOn(offset); + return mValueMask.isOn(offset); +} + + +template +inline void +LeafNode::setValueOn(const Coord& xyz, bool val) +{ + this->setValueOn(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setValueOn(Index offset, bool val) +{ + assert(offset < SIZE); + mValueMask.setOn(offset); + mBuffer.mData.set(offset, val); +} + + +template +inline void +LeafNode::setValueOnly(const Coord& xyz, bool val) +{ + this->setValueOnly(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setActiveState(const Coord& xyz, bool on) +{ + mValueMask.set(this->coordToOffset(xyz), on); +} + + +template +inline void +LeafNode::setValueOff(const Coord& xyz, bool val) +{ + this->setValueOff(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setValueOff(Index offset, bool val) +{ + assert(offset < SIZE); + mValueMask.setOff(offset); + mBuffer.mData.set(offset, val); +} + + +template +template +inline void +LeafNode::modifyValue(Index offset, const ModifyOp& op) +{ + bool val = mBuffer.mData.isOn(offset); + op(val); + mBuffer.mData.set(offset, val); + mValueMask.setOn(offset); +} + + +template +template +inline void +LeafNode::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + this->modifyValue(this->coordToOffset(xyz), op); +} + + +template +template +inline void +LeafNode::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + const Index offset = this->coordToOffset(xyz); + bool val = mBuffer.mData.isOn(offset), state = mValueMask.isOn(offset); + op(val, state); + mBuffer.mData.set(offset, val); + mValueMask.set(offset, state); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::resetBackground(bool oldBackground, bool newBackground) +{ + if (newBackground != oldBackground) { + // Flip mBuffer's background bits and zero its foreground bits. + NodeMaskType bgMask = !(mBuffer.mData | mValueMask); + // Overwrite mBuffer's background bits, leaving its foreground bits intact. + mBuffer.mData = (mBuffer.mData & mValueMask) | bgMask; + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::merge(const LeafNode& other, bool /*bg*/, bool /*otherBG*/) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy == MERGE_NODES) return; + for (typename NodeMaskType::OnIterator iter = other.valueMask().beginOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mValueMask.isOff(n)) { + mBuffer.mData.set(n, other.mBuffer.mData.isOn(n)); + mValueMask.setOn(n); + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +template +template +inline void +LeafNode::merge(bool tileValue, bool tileActive) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + if (!tileActive) return; + // Replace all inactive values with the active tile value. + if (tileValue) mBuffer.mData |= !mValueMask; // -0=>1, +0=>0, -1=>1, +1=>1 (-,+ = off,on) + else mBuffer.mData &= mValueMask; // -0=>0, +0=>0, -1=>0, +1=>1 + mValueMask.setOn(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::topologyUnion(const LeafNode& other) +{ + mValueMask |= other.valueMask(); +} + + +template +template +inline void +LeafNode::topologyIntersection(const LeafNode& other, + const bool&) +{ + mValueMask &= other.valueMask(); +} + + +template +template +inline void +LeafNode::topologyDifference(const LeafNode& other, + const bool&) +{ + mValueMask &= !other.valueMask(); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::clip(const CoordBBox& clipBBox, bool background) +{ + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. Fill it with background tiles. + this->fill(nodeBBox, background, /*active=*/false); + } else if (clipBBox.isInside(nodeBBox)) { + // This node lies completely inside the clipping region. Leave it intact. + return; + } + + // This node isn't completely contained inside the clipping region. + // Set any voxels that lie outside the region to the background value. + + // Construct a boolean mask that is on inside the clipping region and off outside it. + NodeMaskType mask; + nodeBBox.intersect(clipBBox); + Coord xyz; + int &x = xyz.x(), &y = xyz.y(), &z = xyz.z(); + for (x = nodeBBox.min().x(); x <= nodeBBox.max().x(); ++x) { + for (y = nodeBBox.min().y(); y <= nodeBBox.max().y(); ++y) { + for (z = nodeBBox.min().z(); z <= nodeBBox.max().z(); ++z) { + mask.setOn(static_cast(this->coordToOffset(xyz))); + } + } + } + + // Set voxels that lie in the inactive region of the mask (i.e., outside + // the clipping region) to the background value. + for (MaskOffIter maskIter = mask.beginOff(); maskIter; ++maskIter) { + this->setValueOff(maskIter.pos(), background); + } +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::fill(const CoordBBox& bbox, bool value, bool active) +{ + auto clippedBBox = this->getNodeBoundingBox(); + clippedBBox.intersect(bbox); + if (!clippedBBox) return; + + for (Int32 x = clippedBBox.min().x(); x <= clippedBBox.max().x(); ++x) { + const Index offsetX = (x & (DIM-1u))<<2*Log2Dim; + for (Int32 y = clippedBBox.min().y(); y <= clippedBBox.max().y(); ++y) { + const Index offsetXY = offsetX + ((y & (DIM-1u))<< Log2Dim); + for (Int32 z = clippedBBox.min().z(); z <= clippedBBox.max().z(); ++z) { + const Index offset = offsetXY + (z & (DIM-1u)); + mValueMask.set(offset, active); + mBuffer.mData.set(offset, value); + } + } + } +} + +template +inline void +LeafNode::fill(const bool& value) +{ + mBuffer.fill(value); +} + +template +inline void +LeafNode::fill(const bool& value, bool active) +{ + mBuffer.fill(value); + mValueMask.set(active); +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ + using DenseValueType = typename DenseT::ValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + DenseValueType* t0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // target array + const Int32 n0 = bbox.min()[2] & (DIM-1u); + for (Int32 x = bbox.min()[0], ex = bbox.max()[0] + 1; x < ex; ++x) { + DenseValueType* t1 = t0 + xStride * (x - min[0]); + const Int32 n1 = n0 + ((x & (DIM-1u)) << 2*LOG2DIM); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1] + 1; y < ey; ++y) { + DenseValueType* t2 = t1 + yStride * (y - min[1]); + Int32 n2 = n1 + ((y & (DIM-1u)) << LOG2DIM); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2] + 1; z < ez; ++z, t2 += zStride) { + *t2 = DenseValueType(mBuffer.mData.isOn(n2++)); + } + } + } +} + + +template +template +inline void +LeafNode::copyFromDense(const CoordBBox& bbox, const DenseT& dense, + bool background, bool tolerance) +{ + using DenseValueType = typename DenseT::ValueType; + struct Local { + inline static bool toBool(const DenseValueType& v) { return !math::isZero(v); } + }; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + const DenseValueType* s0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // source + const Int32 n0 = bbox.min()[2] & (DIM-1u); + for (Int32 x = bbox.min()[0], ex = bbox.max()[0] + 1; x < ex; ++x) { + const DenseValueType* s1 = s0 + xStride * (x - min[0]); + const Int32 n1 = n0 + ((x & (DIM-1u)) << 2*LOG2DIM); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1] + 1; y < ey; ++y) { + const DenseValueType* s2 = s1 + yStride * (y - min[1]); + Int32 n2 = n1 + ((y & (DIM-1u)) << LOG2DIM); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2]+1; z < ez; ++z, ++n2, s2 += zStride) { + // Note: if tolerance is true (i.e., 1), then all boolean values compare equal. + if (tolerance || (background == Local::toBool(*s2))) { + mValueMask.setOff(n2); + mBuffer.mData.set(n2, background); + } else { + mValueMask.setOn(n2); + mBuffer.mData.set(n2, Local::toBool(*s2)); + } + } + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine(const LeafNode& other, CombineOp& op) +{ + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + bool result = false, aVal = mBuffer.mData.isOn(i), bVal = other.mBuffer.mData.isOn(i); + op(args.setARef(aVal) + .setAIsActive(mValueMask.isOn(i)) + .setBRef(bVal) + .setBIsActive(other.valueMask().isOn(i)) + .setResultRef(result)); + mValueMask.set(i, args.resultIsActive()); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine(bool value, bool valueIsActive, CombineOp& op) +{ + CombineArgs args; + args.setBRef(value).setBIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + bool result = false, aVal = mBuffer.mData.isOn(i); + op(args.setARef(aVal) + .setAIsActive(mValueMask.isOn(i)) + .setResultRef(result)); + mValueMask.set(i, args.resultIsActive()); + mBuffer.mData.set(i, result); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine2(const LeafNode& other, const OtherType& value, + bool valueIsActive, CombineOp& op) +{ + CombineArgs args; + args.setBRef(value).setBIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + bool result = false, aVal = other.mBuffer.mData.isOn(i); + op(args.setARef(aVal) + .setAIsActive(other.valueMask().isOn(i)) + .setResultRef(result)); + mValueMask.set(i, args.resultIsActive()); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine2(bool value, const OtherNodeT& other, + bool valueIsActive, CombineOp& op) +{ + CombineArgs args; + args.setARef(value).setAIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + bool result = false, bVal = other.mBuffer.mData.isOn(i); + op(args.setBRef(bVal) + .setBIsActive(other.valueMask().isOn(i)) + .setResultRef(result)); + mValueMask.set(i, args.resultIsActive()); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp& op) +{ + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + // Default behavior: output voxel is active if either input voxel is active. + mValueMask.set(i, b0.valueMask().isOn(i) || b1.valueMask().isOn(i)); + + bool result = false, b0Val = b0.mBuffer.mData.isOn(i), b1Val = b1.mBuffer.mData.isOn(i); + op(args.setARef(b0Val) + .setAIsActive(b0.valueMask().isOn(i)) + .setBRef(b1Val) + .setBIsActive(b1.valueMask().isOn(i)) + .setResultRef(result)); + mValueMask.set(i, args.resultIsActive()); + mBuffer.mData.set(i, result); + } +} + + +//////////////////////////////////////// + +template +template +inline void +LeafNode::visitActiveBBox(BBoxOp& op) const +{ + if (op.template descent()) { + for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(CoordBBox::createCube(i.getCoord(), 1)); +#else + op.template operator()(CoordBBox::createCube(i.getCoord(), 1)); +#endif + } + } else { +#ifdef _MSC_VER + op.operator()(this->getNodeBoundingBox()); +#else + op.template operator()(this->getNodeBoundingBox()); +#endif + } +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::doVisit(NodeT& self, VisitorOp& op) +{ + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(iter); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) +{ + doVisit2Node(*this, other, op); +} + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const +{ + doVisit2Node(*this, other, op); +} + + +template +template< + typename NodeT, + typename OtherNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp& op) +{ + // Allow the two nodes to have different ValueTypes, but not different dimensions. + static_assert(OtherNodeT::SIZE == NodeT::SIZE, + "can't visit nodes of different sizes simultaneously"); + static_assert(OtherNodeT::LEVEL == NodeT::LEVEL, + "can't visit nodes at different tree levels simultaneously"); + + ChildAllIterT iter = self.beginChildAll(); + OtherChildAllIterT otherIter = other.beginChildAll(); + + for ( ; iter && otherIter; ++iter, ++otherIter) { + op(iter, otherIter); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) +{ + doVisit2(*this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const +{ + doVisit2(*this, otherIter, op, otherIsLHS); +} + + +template +template< + typename NodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::doVisit2(NodeT& self, OtherChildAllIterT& otherIter, + VisitorOp& op, bool otherIsLHS) +{ + if (!otherIter) return; + + if (otherIsLHS) { + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(otherIter, iter); + } + } else { + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(iter, otherIter); + } + } +} + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_LEAF_NODE_BOOL_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/LeafNodeMask.h b/openvdb/tree/LeafNodeMask.h new file mode 100644 index 00000000..ea7b79e1 --- /dev/null +++ b/openvdb/tree/LeafNodeMask.h @@ -0,0 +1,1643 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_TREE_LEAF_NODE_MASK_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_LEAF_NODE_MASK_HAS_BEEN_INCLUDED + +#include +#include +#include // for io::readData(), etc. +#include // for math::isZero() +#include +#include "LeafNode.h" +#include "Iterator.h" +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +/// @brief LeafNode specialization for values of type ValueMask that encodes both +/// the active states and the boolean values of (2^Log2Dim)^3 voxels +/// in a single bit mask, i.e. voxel values and states are indistinguishable! +template +class LeafNode +{ +public: + using LeafNodeType = LeafNode; + using BuildType = ValueMask;// this is a rare case where + using ValueType = bool;// value type != build type + using Buffer = LeafBuffer;// buffer uses the bool specialization + using NodeMaskType = util::NodeMask; + using Ptr = SharedPtr; + + // These static declarations must be on separate lines to avoid VC9 compiler errors. + static const Index LOG2DIM = Log2Dim; // needed by parent nodes + static const Index TOTAL = Log2Dim; // needed by parent nodes + static const Index DIM = 1 << TOTAL; // dimension along one coordinate direction + static const Index NUM_VALUES = 1 << 3 * Log2Dim; + static const Index NUM_VOXELS = NUM_VALUES; // total number of voxels represented by this node + static const Index SIZE = NUM_VALUES; + static const Index LEVEL = 0; // level 0 = leaf + + /// @brief ValueConverter::Type is the type of a LeafNode having the same + /// dimensions as this node but a different value type, T. + template + struct ValueConverter { using Type = LeafNode; }; + + /// @brief SameConfiguration::value is @c true if and only if + /// OtherNodeType is the type of a LeafNode with the same dimensions as this node. + template + struct SameConfiguration { + static const bool value = SameLeafConfig::value; + }; + + /// Default constructor + LeafNode(); + + /// Constructor + /// @param xyz the coordinates of a voxel that lies within the node + /// @param value the initial value = state for all of this node's voxels + /// @param dummy dummy value + explicit LeafNode(const Coord& xyz, bool value = false, bool dummy = false); + + /// "Partial creation" constructor used during file input + LeafNode(PartialCreate, const Coord& xyz, bool value = false, bool dummy = false); + + /// Deep copy constructor + LeafNode(const LeafNode&); + + /// Value conversion copy constructor + template + explicit LeafNode(const LeafNode& other); + + /// Topology copy constructor + template + LeafNode(const LeafNode& other, TopologyCopy); + + //@{ + /// @brief Topology copy constructor + /// @note This variant exists mainly to enable template instantiation. + template + LeafNode(const LeafNode& other, bool offValue, bool onValue, TopologyCopy); + template + LeafNode(const LeafNode& other, bool background, TopologyCopy); + //@} + + /// Destructor + ~LeafNode(); + + // + // Statistics + // + /// Return log2 of the size of the buffer storage. + static Index log2dim() { return Log2Dim; } + /// Return the number of voxels in each dimension. + static Index dim() { return DIM; } + /// Return the total number of voxels represented by this LeafNode + static Index size() { return SIZE; } + /// Return the total number of voxels represented by this LeafNode + static Index numValues() { return SIZE; } + /// Return the level of this node, which by definition is zero for LeafNodes + static Index getLevel() { return LEVEL; } + /// Append the Log2Dim of this LeafNode to the specified vector + static void getNodeLog2Dims(std::vector& dims) { dims.push_back(Log2Dim); } + /// Return the dimension of child nodes of this LeafNode, which is one for voxels. + static Index getChildDim() { return 1; } + /// Return the leaf count for this node, which is one. + static Index32 leafCount() { return 1; } + /// no-op + void nodeCount(std::vector &) const {} + /// Return the non-leaf count for this node, which is zero. + static Index32 nonLeafCount() { return 0; } + + /// Return the number of active voxels. + Index64 onVoxelCount() const { return mBuffer.mData.countOn(); } + /// Return the number of inactive voxels. + Index64 offVoxelCount() const { return mBuffer.mData.countOff(); } + Index64 onLeafVoxelCount() const { return this->onVoxelCount(); } + Index64 offLeafVoxelCount() const { return this->offVoxelCount(); } + static Index64 onTileCount() { return 0; } + static Index64 offTileCount() { return 0; } + + /// Return @c true if this node has no active voxels. + bool isEmpty() const { return mBuffer.mData.isOff(); } + /// Return @c true if this node only contains active voxels. + bool isDense() const { return mBuffer.mData.isOn(); } + /// @brief Return @c true if memory for this node's buffer has been allocated. + /// @details Currently, boolean leaf nodes don't support partial creation, + /// so this always returns @c true. + bool isAllocated() const { return true; } + /// @brief Allocate memory for this node's buffer if it has not already been allocated. + /// @details Currently, boolean leaf nodes don't support partial creation, + /// so this has no effect. + bool allocate() { return true; } + + /// Return the memory in bytes occupied by this node. + Index64 memUsage() const; + + /// Expand the given bounding box so that it includes this leaf node's active voxels. + /// If visitVoxels is false this LeafNode will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; + + /// @brief Return the bounding box of this node, i.e., the full index space + /// spanned by this leaf node. + CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); } + + /// Set the grid index coordinates of this node's local origin. + void setOrigin(const Coord& origin) { mOrigin = origin; } + //@{ + /// Return the grid index coordinates of this node's local origin. + const Coord& origin() const { return mOrigin; } + void getOrigin(Coord& origin) const { origin = mOrigin; } + void getOrigin(Int32& x, Int32& y, Int32& z) const { mOrigin.asXYZ(x, y, z); } + //@} + + /// Return the linear table offset of the given global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static Coord offsetToLocalCoord(Index n); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(Index n) const; + + /// Return a string representation of this node. + std::string str() const; + + /// @brief Return @c true if the given node (which may have a different @c ValueType + /// than this node) has the same active value topology as this node. + template + bool hasSameTopology(const LeafNode* other) const; + + /// Check for buffer equivalence by value. + bool operator==(const LeafNode&) const; + bool operator!=(const LeafNode&) const; + + // + // Buffer management + // + /// @brief Exchange this node's data buffer with the given data buffer + /// without changing the active states of the values. + void swap(Buffer& other) { mBuffer.swap(other); } + const Buffer& buffer() const { return mBuffer; } + Buffer& buffer() { return mBuffer; } + + // + // I/O methods + // + /// Read in just the topology. + void readTopology(std::istream&, bool fromHalf = false); + /// Write out just the topology. + void writeTopology(std::ostream&, bool toHalf = false) const; + + /// Read in the topology and the origin. + void readBuffers(std::istream&, bool fromHalf = false); + void readBuffers(std::istream& is, const CoordBBox&, bool fromHalf = false); + /// Write out the topology and the origin. + void writeBuffers(std::ostream&, bool toHalf = false) const; + + // + // Accessor methods + // + /// Return the value of the voxel at the given coordinates. + const bool& getValue(const Coord& xyz) const; + /// Return the value of the voxel at the given offset. + const bool& getValue(Index offset) const; + + /// @brief Return @c true if the voxel at the given coordinates is active. + /// @param xyz the coordinates of the voxel to be probed + /// @param[out] val the value of the voxel at the given coordinates + bool probeValue(const Coord& xyz, bool& val) const; + + /// Return the level (0) at which leaf node values reside. + static Index getValueLevel(const Coord&) { return LEVEL; } + + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveState(Index offset, bool on) { assert(offsetcoordToOffset(xyz)); } + /// Mark the voxel at the given offset as inactive but don't change its value. + void setValueOff(Index offset) { assert(offset < SIZE); mBuffer.mData.setOff(offset); } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, bool val); + /// Set the value of the voxel at the given offset and mark the voxel as inactive. + void setValueOff(Index offset, bool val); + + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz) { mBuffer.mData.setOn(this->coordToOffset(xyz)); } + /// Mark the voxel at the given offset as active but don't change its value. + void setValueOn(Index offset) { assert(offset < SIZE); mBuffer.mData.setOn(offset); } + + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOn(const Coord& xyz, bool val); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, bool val) { this->setValueOn(xyz, val); } + /// Set the value of the voxel at the given offset and mark the voxel as active. + void setValueOn(Index offset, bool val); + + /// @brief Apply a functor to the value of the voxel at the given offset + /// and mark the voxel as active. + template + void modifyValue(Index offset, const ModifyOp& op); + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// Mark all voxels as active but don't change their values. + void setValuesOn() { mBuffer.mData.setOn(); } + /// Mark all voxels as inactive but don't change their values. + void setValuesOff() { mBuffer.mData.setOff(); } + + /// Return @c true if the voxel at the given coordinates is active. + bool isValueOn(const Coord& xyz) const { return mBuffer.mData.isOn(this->coordToOffset(xyz)); } + /// Return @c true if the voxel at the given offset is active. + bool isValueOn(Index offset) const { assert(offset < SIZE); return mBuffer.mData.isOn(offset); } + + /// Return @c false since leaf nodes never contain tiles. + static bool hasActiveTiles() { return false; } + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&, bool background); + + /// Set all voxels within an axis-aligned box to the specified value. + void fill(const CoordBBox& bbox, bool value, bool = false); + /// Set all voxels within an axis-aligned box to the specified value. + void denseFill(const CoordBBox& bbox, bool value, bool = false) { this->fill(bbox, value); } + + /// Set the state of all voxels to the specified active state. + void fill(const bool& value, bool dummy = false); + + /// @brief Copy into a dense grid the values of the voxels that lie within + /// a given bounding box. + /// + /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid + /// @param dense dense grid with a stride in @e z of one (see tools::Dense + /// in tools/Dense.h for the required API) + /// + /// @note @a bbox is assumed to be identical to or contained in the coordinate domains + /// of both the dense grid and this node, i.e., no bounds checking is performed. + /// @note Consider using tools::CopyToDense in tools/Dense.h + /// instead of calling this method directly. + template + void copyToDense(const CoordBBox& bbox, DenseT& dense) const; + + /// @brief Copy from a dense grid into this node the values of the voxels + /// that lie within a given bounding box. + /// @details Only values that are different (by more than the given tolerance) + /// from the background value will be active. Other values are inactive + /// and truncated to the background value. + /// + /// @param bbox inclusive bounding box of the voxels to be copied into this node + /// @param dense dense grid with a stride in @e z of one (see tools::Dense + /// in tools/Dense.h for the required API) + /// @param background background value of the tree that this node belongs to + /// @param tolerance tolerance within which a value equals the background value + /// + /// @note @a bbox is assumed to be identical to or contained in the coordinate domains + /// of both the dense grid and this node, i.e., no bounds checking is performed. + /// @note Consider using tools::CopyFromDense in tools/Dense.h + /// instead of calling this method directly. + template + void copyFromDense(const CoordBBox& bbox, const DenseT& dense, bool background, bool tolerance); + + /// @brief Return the value of the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template + const bool& getValueAndCache(const Coord& xyz, AccessorT&) const {return this->getValue(xyz);} + + /// @brief Return @c true if the voxel at the given coordinates is active. + /// @note Used internally by ValueAccessor. + template + bool isValueOnAndCache(const Coord& xyz, AccessorT&) const { return this->isValueOn(xyz); } + + /// @brief Change the value of the voxel at the given coordinates and mark it as active. + /// @note Used internally by ValueAccessor. + template + void setValueAndCache(const Coord& xyz, bool val, AccessorT&) { this->setValueOn(xyz, val); } + + /// @brief Change the value of the voxel at the given coordinates + /// but preserve its state. + /// @note Used internally by ValueAccessor. + template + void setValueOnlyAndCache(const Coord& xyz, bool val, AccessorT&) {this->setValueOnly(xyz,val);} + + /// @brief Change the value of the voxel at the given coordinates and mark it as inactive. + /// @note Used internally by ValueAccessor. + template + void setValueOffAndCache(const Coord& xyz, bool value, AccessorT&) + { + this->setValueOff(xyz, value); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValue(xyz, op); + } + + /// Apply a functor to the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValueAndActiveState(xyz, op); + } + + /// @brief Set the active state of the voxel at the given coordinates + /// without changing its value. + /// @note Used internally by ValueAccessor. + template + void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&) + { + this->setActiveState(xyz, on); + } + + /// @brief Return @c true if the voxel at the given coordinates is active + /// and return the voxel value in @a val. + /// @note Used internally by ValueAccessor. + template + bool probeValueAndCache(const Coord& xyz, bool& val, AccessorT&) const + { + return this->probeValue(xyz, val); + } + + /// @brief Return the LEVEL (=0) at which leaf node values reside. + /// @note Used internally by ValueAccessor. + template + static Index getValueLevelAndCache(const Coord&, AccessorT&) { return LEVEL; } + + /// @brief Return a const reference to the first entry in the buffer. + /// @note Since it's actually a reference to a static data member + /// it should not be converted to a non-const pointer! + const bool& getFirstValue() const { if (mBuffer.mData.isOn(0)) return Buffer::sOn; else return Buffer::sOff; } + /// @brief Return a const reference to the last entry in the buffer. + /// @note Since it's actually a reference to a static data member + /// it should not be converted to a non-const pointer! + const bool& getLastValue() const { if (mBuffer.mData.isOn(SIZE-1)) return Buffer::sOn; else return Buffer::sOff; } + + /// Return @c true if all of this node's voxels have the same active state + /// and are equal to within the given tolerance, and return the value in + /// @a constValue and the active state in @a state. + bool isConstant(bool& constValue, bool& state, bool tolerance = 0) const; + + /// @brief Computes the median value of all the active and inactive voxels in this node. + /// @return The median value. + /// + /// @details The median for boolean values is defined as the mode + /// of the values, i.e. the value that occurs most often. + bool medianAll() const; + + /// @brief Computes the median value of all the active voxels in this node. + /// @return The number of active voxels. + /// + /// @param value Updated with the median value of all the active voxels. + /// + /// @note Since the value and state are shared for this + /// specialization of the LeafNode the @a value will always be true! + Index medianOn(ValueType &value) const; + + /// @brief Computes the median value of all the inactive voxels in this node. + /// @return The number of inactive voxels. + /// + /// @param value Updated with the median value of all the inactive + /// voxels. + /// + /// @note Since the value and state are shared for this + /// specialization of the LeafNode the @a value will always be false! + Index medianOff(ValueType &value) const; + + /// Return @c true if all of this node's values are inactive. + bool isInactive() const { return mBuffer.mData.isOff(); } + + /// @brief no-op since for this temaplte specialization voxel + /// values and states are indistinguishable. + void resetBackground(bool, bool) {} + + /// @brief Invert the bits of the voxels, i.e. states and values + void negate() { mBuffer.mData.toggle(); } + + template + void merge(const LeafNode& other, bool bg = false, bool otherBG = false); + template void merge(bool tileValue, bool tileActive=false); + + /// @brief No-op + /// @details This function exists only to enable template instantiation. + void voxelizeActiveTiles(bool = true) {} + + /// @brief Union this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active if either of the original voxels + /// were active. + /// + /// @note This operation modifies only active states, not values. + template + void topologyUnion(const LeafNode& other); + + /// @brief Intersect this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active only if both of the original voxels + /// were active. + /// + /// @details The last dummy argument is required to match the signature + /// for InternalNode::topologyIntersection. + /// + /// @note This operation modifies only active states, not + /// values. Also note that this operation can result in all voxels + /// being inactive so consider subsequnetly calling prune. + template + void topologyIntersection(const LeafNode& other, const bool&); + + /// @brief Difference this node's set of active values with the active values + /// of the other node, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this LeafNode and inactive in the other LeafNode. + /// + /// @details The last dummy argument is required to match the signature + /// for InternalNode::topologyDifference. + /// + /// @note This operation modifies only active states, not values. + /// Also, because it can deactivate all of this node's voxels, + /// consider subsequently calling prune. + template + void topologyDifference(const LeafNode& other, const bool&); + + template + void combine(const LeafNode& other, CombineOp& op); + template + void combine(bool, bool valueIsActive, CombineOp& op); + + template + void combine2(const LeafNode& other, const OtherType&, bool valueIsActive, CombineOp&); + template + void combine2(bool, const OtherNodeT& other, bool valueIsActive, CombineOp&); + template + void combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp&); + + /// @brief Calls the templated functor BBoxOp with bounding box information. + /// An additional level argument is provided to the callback. + /// + /// @note The bounding boxes are guarenteed to be non-overlapping. + template void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&); + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&) const; + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false); + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const; + + //@{ + /// This function exists only to enable template instantiation. + void prune(const ValueType& /*tolerance*/ = zeroVal()) {} + void addLeaf(LeafNode*) {} + template + void addLeafAndCache(LeafNode*, AccessorT&) {} + template + NodeT* stealNode(const Coord&, const ValueType&, bool) { return nullptr; } + template + NodeT* probeNode(const Coord&) { return nullptr; } + template + const NodeT* probeConstNode(const Coord&) const { return nullptr; } + template void getNodes(ArrayT&) const {} + template void stealNodes(ArrayT&, const ValueType&, bool) {} + //@} + + void addTile(Index level, const Coord&, bool val, bool active); + void addTile(Index offset, bool val, bool active); + template + void addTileAndCache(Index level, const Coord&, bool val, bool active, AccessorT&); + + //@{ + /// @brief Return a pointer to this node. + LeafNode* touchLeaf(const Coord&) { return this; } + template + LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + LeafNode* probeLeaf(const Coord&) { return this; } + template + LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + //@{ + /// @brief Return a @const pointer to this node. + const LeafNode* probeLeaf(const Coord&) const { return this; } + template + const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const LeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(std::is_same::value)) return nullptr; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + // + // Iterators + // +protected: + using MaskOnIter = typename NodeMaskType::OnIterator; + using MaskOffIter = typename NodeMaskType::OffIterator; + using MaskDenseIter = typename NodeMaskType::DenseIterator; + + template + struct ValueIter: + // Derives from SparseIteratorBase, but can also be used as a dense iterator, + // if MaskIterT is a dense mask iterator type. + public SparseIteratorBase, NodeT, ValueT> + { + using BaseT = SparseIteratorBase; + + ValueIter() {} + ValueIter(const MaskIterT& iter, NodeT* parent): BaseT(iter, parent) {} + + const bool& getItem(Index pos) const { return this->parent().getValue(pos); } + const bool& getValue() const { return this->getItem(this->pos()); } + + // Note: setItem() can't be called on const iterators. + void setItem(Index pos, bool value) const { this->parent().setValueOnly(pos, value); } + // Note: setValue() can't be called on const iterators. + void setValue(bool value) const { this->setItem(this->pos(), value); } + + // Note: modifyItem() can't be called on const iterators. + template + void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); } + // Note: modifyValue() can't be called on const iterators. + template + void modifyValue(const ModifyOp& op) const { this->modifyItem(this->pos(), op); } + }; + + /// Leaf nodes have no children, so their child iterators have no get/set accessors. + template + struct ChildIter: + public SparseIteratorBase, NodeT, bool> + { + ChildIter() {} + ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ChildIter, NodeT, bool>(iter, parent) {} + }; + + template + struct DenseIter: public DenseIteratorBase< + MaskDenseIter, DenseIter, NodeT, /*ChildT=*/void, ValueT> + { + using BaseT = DenseIteratorBase; + using NonConstValueT = typename BaseT::NonConstValueType; + + DenseIter() {} + DenseIter(const MaskDenseIter& iter, NodeT* parent): BaseT(iter, parent) {} + + bool getItem(Index pos, void*& child, NonConstValueT& value) const + { + value = this->parent().getValue(pos); + child = nullptr; + return false; // no child + } + + // Note: setItem() can't be called on const iterators. + //void setItem(Index pos, void* child) const {} + + // Note: unsetItem() can't be called on const iterators. + void unsetItem(Index pos, const ValueT& val) const {this->parent().setValueOnly(pos, val);} + }; + +public: + using ValueOnIter = ValueIter; + using ValueOnCIter = ValueIter; + using ValueOffIter = ValueIter; + using ValueOffCIter = ValueIter; + using ValueAllIter = ValueIter; + using ValueAllCIter = ValueIter; + using ChildOnIter = ChildIter; + using ChildOnCIter = ChildIter; + using ChildOffIter = ChildIter; + using ChildOffCIter = ChildIter; + using ChildAllIter = DenseIter; + using ChildAllCIter = DenseIter; + + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mBuffer.mData.beginOn(), this); } + ValueOnCIter beginValueOn() const { return ValueOnCIter(mBuffer.mData.beginOn(), this); } + ValueOnIter beginValueOn() { return ValueOnIter(mBuffer.mData.beginOn(), this); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mBuffer.mData.beginOff(), this); } + ValueOffCIter beginValueOff() const { return ValueOffCIter(mBuffer.mData.beginOff(), this); } + ValueOffIter beginValueOff() { return ValueOffIter(mBuffer.mData.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mBuffer.mData.beginDense(), this); } + ValueAllCIter beginValueAll() const { return ValueAllCIter(mBuffer.mData.beginDense(), this); } + ValueAllIter beginValueAll() { return ValueAllIter(mBuffer.mData.beginDense(), this); } + + ValueOnCIter cendValueOn() const { return ValueOnCIter(mBuffer.mData.endOn(), this); } + ValueOnCIter endValueOn() const { return ValueOnCIter(mBuffer.mData.endOn(), this); } + ValueOnIter endValueOn() { return ValueOnIter(mBuffer.mData.endOn(), this); } + ValueOffCIter cendValueOff() const { return ValueOffCIter(mBuffer.mData.endOff(), this); } + ValueOffCIter endValueOff() const { return ValueOffCIter(mBuffer.mData.endOff(), this); } + ValueOffIter endValueOff() { return ValueOffIter(mBuffer.mData.endOff(), this); } + ValueAllCIter cendValueAll() const { return ValueAllCIter(mBuffer.mData.endDense(), this); } + ValueAllCIter endValueAll() const { return ValueAllCIter(mBuffer.mData.endDense(), this); } + ValueAllIter endValueAll() { return ValueAllIter(mBuffer.mData.endDense(), this); } + + // Note that [c]beginChildOn() and [c]beginChildOff() actually return end iterators, + // because leaf nodes have no children. + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mBuffer.mData.endOn(), this); } + ChildOnCIter beginChildOn() const { return ChildOnCIter(mBuffer.mData.endOn(), this); } + ChildOnIter beginChildOn() { return ChildOnIter(mBuffer.mData.endOn(), this); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mBuffer.mData.endOff(), this); } + ChildOffCIter beginChildOff() const { return ChildOffCIter(mBuffer.mData.endOff(), this); } + ChildOffIter beginChildOff() { return ChildOffIter(mBuffer.mData.endOff(), this); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mBuffer.mData.beginDense(), this); } + ChildAllCIter beginChildAll() const { return ChildAllCIter(mBuffer.mData.beginDense(), this); } + ChildAllIter beginChildAll() { return ChildAllIter(mBuffer.mData.beginDense(), this); } + + ChildOnCIter cendChildOn() const { return ChildOnCIter(mBuffer.mData.endOn(), this); } + ChildOnCIter endChildOn() const { return ChildOnCIter(mBuffer.mData.endOn(), this); } + ChildOnIter endChildOn() { return ChildOnIter(mBuffer.mData.endOn(), this); } + ChildOffCIter cendChildOff() const { return ChildOffCIter(mBuffer.mData.endOff(), this); } + ChildOffCIter endChildOff() const { return ChildOffCIter(mBuffer.mData.endOff(), this); } + ChildOffIter endChildOff() { return ChildOffIter(mBuffer.mData.endOff(), this); } + ChildAllCIter cendChildAll() const { return ChildAllCIter(mBuffer.mData.endDense(), this); } + ChildAllCIter endChildAll() const { return ChildAllCIter(mBuffer.mData.endDense(), this); } + ChildAllIter endChildAll() { return ChildAllIter(mBuffer.mData.endDense(), this); } + + // + // Mask accessors + // + bool isValueMaskOn(Index n) const { return mBuffer.mData.isOn(n); } + bool isValueMaskOn() const { return mBuffer.mData.isOn(); } + bool isValueMaskOff(Index n) const { return mBuffer.mData.isOff(n); } + bool isValueMaskOff() const { return mBuffer.mData.isOff(); } + const NodeMaskType& getValueMask() const { return mBuffer.mData; } + const NodeMaskType& valueMask() const { return mBuffer.mData; } + NodeMaskType& getValueMask() { return mBuffer.mData; } + void setValueMask(const NodeMaskType& mask) { mBuffer.mData = mask; } + bool isChildMaskOn(Index) const { return false; } // leaf nodes have no children + bool isChildMaskOff(Index) const { return true; } + bool isChildMaskOff() const { return true; } +protected: + void setValueMask(Index n, bool on) { mBuffer.mData.set(n, on); } + void setValueMaskOn(Index n) { mBuffer.mData.setOn(n); } + void setValueMaskOff(Index n) { mBuffer.mData.setOff(n); } + + /// Compute the origin of the leaf node that contains the voxel with the given coordinates. + static void evalNodeOrigin(Coord& xyz) { xyz &= ~(DIM - 1); } + + template + static inline void doVisit(NodeT&, VisitorOp&); + + template + static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&); + + template + static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); + + /// Bitmask representing the values AND state of voxels + Buffer mBuffer; + + /// Global grid index coordinates (x,y,z) of the local origin of this node + Coord mOrigin; + +private: + /// @brief During topology-only construction, access is needed + /// to protected/private members of other template instances. + template friend class LeafNode; + + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + + //@{ + /// Allow iterators to call mask accessor methods (see below). + /// @todo Make mask accessors public? + friend class IteratorBase; + friend class IteratorBase; + friend class IteratorBase; + //@} + + template friend class LeafBuffer; + +}; // class LeafNode + + +//////////////////////////////////////// + + +template +inline +LeafNode::LeafNode() + : mOrigin(0, 0, 0) +{ +} + +template +inline +LeafNode::LeafNode(const Coord& xyz, bool value, bool active) + : mBuffer(value || active) + , mOrigin(xyz & (~(DIM - 1))) +{ +} + + +template +inline +LeafNode::LeafNode(PartialCreate, const Coord& xyz, bool value, bool active) + : mBuffer(value || active) + , mOrigin(xyz & (~(DIM - 1))) +{ +} + + +template +inline +LeafNode::LeafNode(const LeafNode &other) + : mBuffer(other.mBuffer) + , mOrigin(other.mOrigin) +{ +} + + +// Copy-construct from a leaf node with the same configuration but a different ValueType. +template +template +inline +LeafNode::LeafNode(const LeafNode& other) + : mBuffer(other.valueMask()) + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + bool, TopologyCopy) + : mBuffer(other.valueMask())// value = active state + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, TopologyCopy) + : mBuffer(other.valueMask())// value = active state + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + bool offValue, bool onValue, TopologyCopy) + : mBuffer(other.valueMask()) + , mOrigin(other.origin()) +{ + if (offValue==true) { + if (onValue==false) { + mBuffer.mData.toggle(); + } else { + mBuffer.mData.setOn(); + } + } +} + + +template +inline +LeafNode::~LeafNode() +{ +} + + +//////////////////////////////////////// + + +template +inline Index64 +LeafNode::memUsage() const +{ + // Use sizeof(*this) to capture alignment-related padding + return sizeof(*this); +} + + +template +inline void +LeafNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + CoordBBox this_bbox = this->getNodeBoundingBox(); + if (bbox.isInside(this_bbox)) return;//this LeafNode is already enclosed in the bbox + if (ValueOnCIter iter = this->cbeginValueOn()) {//any active values? + if (visitVoxels) {//use voxel granularity? + this_bbox.reset(); + for(; iter; ++iter) this_bbox.expand(this->offsetToLocalCoord(iter.pos())); + this_bbox.translate(this->origin()); + } + bbox.expand(this_bbox); + } +} + + +template +template +inline bool +LeafNode::hasSameTopology(const LeafNode* other) const +{ + assert(other); + return (Log2Dim == OtherLog2Dim && mBuffer.mData == other->getValueMask()); +} + + +template +inline std::string +LeafNode::str() const +{ + std::ostringstream ostr; + ostr << "LeafNode @" << mOrigin << ": "; + for (Index32 n = 0; n < SIZE; ++n) ostr << (mBuffer.mData.isOn(n) ? '#' : '.'); + return ostr.str(); +} + + +//////////////////////////////////////// + + +template +inline Index +LeafNode::coordToOffset(const Coord& xyz) +{ + assert ((xyz[0] & (DIM-1u)) < DIM && (xyz[1] & (DIM-1u)) < DIM && (xyz[2] & (DIM-1u)) < DIM); + return ((xyz[0] & (DIM-1u)) << 2*Log2Dim) + + ((xyz[1] & (DIM-1u)) << Log2Dim) + + (xyz[2] & (DIM-1u)); +} + + +template +inline Coord +LeafNode::offsetToLocalCoord(Index n) +{ + assert(n < (1 << 3*Log2Dim)); + Coord xyz; + xyz.setX(n >> 2*Log2Dim); + n &= ((1 << 2*Log2Dim) - 1); + xyz.setY(n >> Log2Dim); + xyz.setZ(n & ((1 << Log2Dim) - 1)); + return xyz; +} + + +template +inline Coord +LeafNode::offsetToGlobalCoord(Index n) const +{ + return (this->offsetToLocalCoord(n) + this->origin()); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::readTopology(std::istream& is, bool /*fromHalf*/) +{ + mBuffer.mData.load(is); +} + + +template +inline void +LeafNode::writeTopology(std::ostream& os, bool /*toHalf*/) const +{ + mBuffer.mData.save(os); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf) +{ + // Boolean LeafNodes don't currently implement lazy loading. + // Instead, load the full buffer, then clip it. + + this->readBuffers(is, fromHalf); + + // Get this tree's background value. + bool background = false; + if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + this->clip(clipBBox, background); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, bool /*fromHalf*/) +{ + // Read in the value mask = buffer. + mBuffer.mData.load(is); + // Read in the origin. + is.read(reinterpret_cast(&mOrigin), sizeof(Coord::ValueType) * 3); +} + + +template +inline void +LeafNode::writeBuffers(std::ostream& os, bool /*toHalf*/) const +{ + // Write out the value mask = buffer. + mBuffer.mData.save(os); + // Write out the origin. + os.write(reinterpret_cast(&mOrigin), sizeof(Coord::ValueType) * 3); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::operator==(const LeafNode& other) const +{ + return mOrigin == other.mOrigin && mBuffer == other.mBuffer; +} + + +template +inline bool +LeafNode::operator!=(const LeafNode& other) const +{ + return !(this->operator==(other)); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::isConstant(bool& constValue, bool& state, bool) const +{ + if (!mBuffer.mData.isConstant(state)) return false; + + constValue = state; + return true; +} + + +//////////////////////////////////////// + +template +inline bool +LeafNode::medianAll() const +{ + const Index countTrue = mBuffer.mData.countOn(); + return countTrue > (NUM_VALUES >> 1); +} + +template +inline Index +LeafNode::medianOn(bool& state) const +{ + const Index countTrueOn = mBuffer.mData.countOn(); + state = true;//since value and state are the same for this specialization of the leaf node + return countTrueOn; +} + +template +inline Index +LeafNode::medianOff(bool& state) const +{ + const Index countFalseOff = mBuffer.mData.countOff(); + state = false;//since value and state are the same for this specialization of the leaf node + return countFalseOff; +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::addTile(Index /*level*/, const Coord& xyz, bool val, bool active) +{ + this->addTile(this->coordToOffset(xyz), val, active); +} + +template +inline void +LeafNode::addTile(Index offset, bool val, bool active) +{ + assert(offset < SIZE); + this->setValueOnly(offset, val); + this->setActiveState(offset, active); +} + +template +template +inline void +LeafNode::addTileAndCache(Index level, const Coord& xyz, + bool val, bool active, AccessorT&) +{ + this->addTile(level, xyz, val, active); +} + + +//////////////////////////////////////// + + +template +inline const bool& +LeafNode::getValue(const Coord& xyz) const +{ + // This *CANNOT* use operator ? because Visual C++ + if (mBuffer.mData.isOn(this->coordToOffset(xyz))) return Buffer::sOn; else return Buffer::sOff; +} + + +template +inline const bool& +LeafNode::getValue(Index offset) const +{ + assert(offset < SIZE); + // This *CANNOT* use operator ? for Windows + if (mBuffer.mData.isOn(offset)) return Buffer::sOn; else return Buffer::sOff; +} + + +template +inline bool +LeafNode::probeValue(const Coord& xyz, bool& val) const +{ + const Index offset = this->coordToOffset(xyz); + val = mBuffer.mData.isOn(offset); + return val; +} + + +template +inline void +LeafNode::setValueOn(const Coord& xyz, bool val) +{ + this->setValueOn(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setValueOn(Index offset, bool val) +{ + assert(offset < SIZE); + mBuffer.mData.set(offset, val); +} + + +template +inline void +LeafNode::setValueOnly(const Coord& xyz, bool val) +{ + this->setValueOnly(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setActiveState(const Coord& xyz, bool on) +{ + mBuffer.mData.set(this->coordToOffset(xyz), on); +} + + +template +inline void +LeafNode::setValueOff(const Coord& xyz, bool val) +{ + this->setValueOff(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setValueOff(Index offset, bool val) +{ + assert(offset < SIZE); + mBuffer.mData.set(offset, val); +} + + +template +template +inline void +LeafNode::modifyValue(Index offset, const ModifyOp& op) +{ + bool val = mBuffer.mData.isOn(offset); + op(val); + mBuffer.mData.set(offset, val); +} + + +template +template +inline void +LeafNode::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + this->modifyValue(this->coordToOffset(xyz), op); +} + + +template +template +inline void +LeafNode::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + const Index offset = this->coordToOffset(xyz); + bool val = mBuffer.mData.isOn(offset), state = val; + op(val, state); + mBuffer.mData.set(offset, val); +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::merge(const LeafNode& other, bool /*bg*/, bool /*otherBG*/) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy == MERGE_NODES) return; + mBuffer.mData |= other.mBuffer.mData; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +template +template +inline void +LeafNode::merge(bool tileValue, bool) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + if (tileValue) mBuffer.mData.setOn(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::topologyUnion(const LeafNode& other) +{ + mBuffer.mData |= other.valueMask(); +} + + +template +template +inline void +LeafNode::topologyIntersection(const LeafNode& other, + const bool&) +{ + mBuffer.mData &= other.valueMask(); +} + + +template +template +inline void +LeafNode::topologyDifference(const LeafNode& other, + const bool&) +{ + mBuffer.mData &= !other.valueMask(); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::clip(const CoordBBox& clipBBox, bool background) +{ + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. Fill it with background tiles. + this->fill(nodeBBox, background, /*active=*/false); + } else if (clipBBox.isInside(nodeBBox)) { + // This node lies completely inside the clipping region. Leave it intact. + return; + } + + // This node isn't completely contained inside the clipping region. + // Set any voxels that lie outside the region to the background value. + + // Construct a boolean mask that is on inside the clipping region and off outside it. + NodeMaskType mask; + nodeBBox.intersect(clipBBox); + Coord xyz; + int &x = xyz.x(), &y = xyz.y(), &z = xyz.z(); + for (x = nodeBBox.min().x(); x <= nodeBBox.max().x(); ++x) { + for (y = nodeBBox.min().y(); y <= nodeBBox.max().y(); ++y) { + for (z = nodeBBox.min().z(); z <= nodeBBox.max().z(); ++z) { + mask.setOn(static_cast(this->coordToOffset(xyz))); + } + } + } + + // Set voxels that lie in the inactive region of the mask (i.e., outside + // the clipping region) to the background value. + for (MaskOffIter maskIter = mask.beginOff(); maskIter; ++maskIter) { + this->setValueOff(maskIter.pos(), background); + } +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::fill(const CoordBBox& bbox, bool value, bool) +{ + auto clippedBBox = this->getNodeBoundingBox(); + clippedBBox.intersect(bbox); + if (!clippedBBox) return; + + for (Int32 x = clippedBBox.min().x(); x <= clippedBBox.max().x(); ++x) { + const Index offsetX = (x & (DIM-1u))<<2*Log2Dim; + for (Int32 y = clippedBBox.min().y(); y <= clippedBBox.max().y(); ++y) { + const Index offsetXY = offsetX + ((y & (DIM-1u))<< Log2Dim); + for (Int32 z = clippedBBox.min().z(); z <= clippedBBox.max().z(); ++z) { + const Index offset = offsetXY + (z & (DIM-1u)); + mBuffer.mData.set(offset, value); + } + } + } +} + +template +inline void +LeafNode::fill(const bool& value, bool) +{ + mBuffer.fill(value); +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ + using DenseValueType = typename DenseT::ValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + DenseValueType* t0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // target array + const Int32 n0 = bbox.min()[2] & (DIM-1u); + for (Int32 x = bbox.min()[0], ex = bbox.max()[0] + 1; x < ex; ++x) { + DenseValueType* t1 = t0 + xStride * (x - min[0]); + const Int32 n1 = n0 + ((x & (DIM-1u)) << 2*LOG2DIM); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1] + 1; y < ey; ++y) { + DenseValueType* t2 = t1 + yStride * (y - min[1]); + Int32 n2 = n1 + ((y & (DIM-1u)) << LOG2DIM); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2] + 1; z < ez; ++z, t2 += zStride) { + *t2 = DenseValueType(mBuffer.mData.isOn(n2++)); + } + } + } +} + + +template +template +inline void +LeafNode::copyFromDense(const CoordBBox& bbox, const DenseT& dense, + bool background, bool tolerance) +{ + using DenseValueType = typename DenseT::ValueType; + struct Local { + inline static bool toBool(const DenseValueType& v) { return !math::isZero(v); } + }; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + const DenseValueType* s0 = dense.data() + zStride * (bbox.min()[2] - min[2]); // source + const Int32 n0 = bbox.min()[2] & (DIM-1u); + for (Int32 x = bbox.min()[0], ex = bbox.max()[0] + 1; x < ex; ++x) { + const DenseValueType* s1 = s0 + xStride * (x - min[0]); + const Int32 n1 = n0 + ((x & (DIM-1u)) << 2*LOG2DIM); + for (Int32 y = bbox.min()[1], ey = bbox.max()[1] + 1; y < ey; ++y) { + const DenseValueType* s2 = s1 + yStride * (y - min[1]); + Int32 n2 = n1 + ((y & (DIM-1u)) << LOG2DIM); + for (Int32 z = bbox.min()[2], ez = bbox.max()[2]+1; z < ez; ++z, ++n2, s2 += zStride) { + // Note: if tolerance is true (i.e., 1), then all boolean values compare equal. + if (tolerance || (background == Local::toBool(*s2))) { + mBuffer.mData.set(n2, background); + } else { + mBuffer.mData.set(n2, Local::toBool(*s2)); + } + } + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine(const LeafNode& other, CombineOp& op) +{ + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + bool result = false, aVal = mBuffer.mData.isOn(i), bVal = other.mBuffer.mData.isOn(i); + op(args.setARef(aVal) + .setAIsActive(aVal) + .setBRef(bVal) + .setBIsActive(bVal) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine(bool value, bool valueIsActive, CombineOp& op) +{ + CombineArgs args; + args.setBRef(value).setBIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + bool result = false, aVal = mBuffer.mData.isOn(i); + op(args.setARef(aVal) + .setAIsActive(aVal) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine2(const LeafNode& other, const OtherType& value, + bool valueIsActive, CombineOp& op) +{ + CombineArgs args; + args.setBRef(value).setBIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + bool result = false, aVal = other.mBuffer.mData.isOn(i); + op(args.setARef(aVal) + .setAIsActive(aVal) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine2(bool value, const OtherNodeT& other, + bool valueIsActive, CombineOp& op) +{ + CombineArgs args; + args.setARef(value).setAIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + bool result = false, bVal = other.mBuffer.mData.isOn(i); + op(args.setBRef(bVal) + .setBIsActive(bVal) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp& op) +{ + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + bool result = false, b0Val = b0.mBuffer.mData.isOn(i), b1Val = b1.mBuffer.mData.isOn(i); + op(args.setARef(b0Val) + .setAIsActive(b0Val) + .setBRef(b1Val) + .setBIsActive(b1Val) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +//////////////////////////////////////// + +template +template +inline void +LeafNode::visitActiveBBox(BBoxOp& op) const +{ + if (op.template descent()) { + for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(CoordBBox::createCube(i.getCoord(), 1)); +#else + op.template operator()(CoordBBox::createCube(i.getCoord(), 1)); +#endif + } + } else { +#ifdef _MSC_VER + op.operator()(this->getNodeBoundingBox()); +#else + op.template operator()(this->getNodeBoundingBox()); +#endif + } +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::doVisit(NodeT& self, VisitorOp& op) +{ + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(iter); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) +{ + doVisit2Node(*this, other, op); +} + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const +{ + doVisit2Node(*this, other, op); +} + + +template +template< + typename NodeT, + typename OtherNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp& op) +{ + // Allow the two nodes to have different ValueTypes, but not different dimensions. + static_assert(OtherNodeT::SIZE == NodeT::SIZE, + "can't visit nodes of different sizes simultaneously"); + static_assert(OtherNodeT::LEVEL == NodeT::LEVEL, + "can't visit nodes at different tree levels simultaneously"); + + ChildAllIterT iter = self.beginChildAll(); + OtherChildAllIterT otherIter = other.beginChildAll(); + + for ( ; iter && otherIter; ++iter, ++otherIter) { + op(iter, otherIter); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) +{ + doVisit2(*this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const +{ + doVisit2(*this, otherIter, op, otherIsLHS); +} + + +template +template< + typename NodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::doVisit2(NodeT& self, OtherChildAllIterT& otherIter, + VisitorOp& op, bool otherIsLHS) +{ + if (!otherIter) return; + + if (otherIsLHS) { + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(otherIter, iter); + } + } else { + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(iter, otherIter); + } + } +} + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_LEAF_NODE_MASK_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/NodeManager.h b/openvdb/tree/NodeManager.h new file mode 100644 index 00000000..8cca7f25 --- /dev/null +++ b/openvdb/tree/NodeManager.h @@ -0,0 +1,1017 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file tree/NodeManager.h +/// +/// @author Ken Museth +/// +/// @brief NodeManager produces linear arrays of all tree nodes +/// allowing for efficient threading and bottom-up processing. +/// +/// @note A NodeManager can be constructed from a Tree or LeafManager. +/// The latter is slightly more efficient since the cached leaf nodes will be reused. + +#ifndef OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +// Produce linear arrays of all tree nodes, to facilitate efficient threading +// and bottom-up processing. +template +class NodeManager; + + +//////////////////////////////////////// + + +/// @brief This class caches tree nodes of a specific type in a linear array. +/// +/// @note It is for internal use and should rarely be used directly. +template +class NodeList +{ +public: + using value_type = NodeT*; + using ListT = std::deque; + + NodeList() {} + + void push_back(NodeT* node) { mList.push_back(node); } + + NodeT& operator()(size_t n) const { assert(nsize();} + + class Iterator + { + public: + Iterator(const NodeRange& range, size_t pos): mRange(range), mPos(pos) + { + assert(this->isValid()); + } + Iterator(const Iterator&) = default; + Iterator& operator=(const Iterator&) = default; + /// Advance to the next node. + Iterator& operator++() { ++mPos; return *this; } + /// Return a reference to the node to which this iterator is pointing. + NodeT& operator*() const { return mRange.mNodeList(mPos); } + /// Return a pointer to the node to which this iterator is pointing. + NodeT* operator->() const { return &(this->operator*()); } + /// Return the index into the list of the current node. + size_t pos() const { return mPos; } + bool isValid() const { return mPos>=mRange.mBegin && mPos<=mRange.mEnd; } + /// Return @c true if this iterator is not yet exhausted. + bool test() const { return mPos < mRange.mEnd; } + /// Return @c true if this iterator is not yet exhausted. + operator bool() const { return this->test(); } + /// Return @c true if this iterator is exhausted. + bool empty() const { return !this->test(); } + bool operator!=(const Iterator& other) const + { + return (mPos != other.mPos) || (&mRange != &other.mRange); + } + bool operator==(const Iterator& other) const { return !(*this != other); } + const NodeRange& nodeRange() const { return mRange; } + + private: + const NodeRange& mRange; + size_t mPos; + };// NodeList::NodeRange::Iterator + + Iterator begin() const {return Iterator(*this, mBegin);} + + Iterator end() const {return Iterator(*this, mEnd);} + + private: + size_t mEnd, mBegin, mGrainSize; + const NodeList& mNodeList; + + static size_t doSplit(NodeRange& r) + { + assert(r.is_divisible()); + size_t middle = r.mBegin + (r.mEnd - r.mBegin) / 2u; + r.mEnd = middle; + return middle; + } + };// NodeList::NodeRange + + /// Return a TBB-compatible NodeRange. + NodeRange nodeRange(size_t grainsize = 1) const + { + return NodeRange(0, this->nodeCount(), *this, grainsize); + } + + template + void foreach(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + NodeTransformer transform(op); + transform.run(this->nodeRange(grainSize), threaded); + } + + template + void reduce(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + NodeReducer transform(op); + transform.run(this->nodeRange(grainSize), threaded); + } + +private: + + // Private struct of NodeList that performs parallel_for + template + struct NodeTransformer + { + NodeTransformer(const NodeOp& nodeOp) : mNodeOp(nodeOp) + { + } + void run(const NodeRange& range, bool threaded = true) + { + threaded ? tbb::parallel_for(range, *this) : (*this)(range); + } + void operator()(const NodeRange& range) const + { + for (typename NodeRange::Iterator it = range.begin(); it; ++it) mNodeOp(*it); + } + const NodeOp mNodeOp; + };// NodeList::NodeTransformer + + // Private struct of NodeList that performs parallel_reduce + template + struct NodeReducer + { + NodeReducer(NodeOp& nodeOp) : mNodeOp(&nodeOp), mOwnsOp(false) + { + } + NodeReducer(const NodeReducer& other, tbb::split) : + mNodeOp(new NodeOp(*(other.mNodeOp), tbb::split())), mOwnsOp(true) + { + } + ~NodeReducer() { if (mOwnsOp) delete mNodeOp; } + void run(const NodeRange& range, bool threaded = true) + { + threaded ? tbb::parallel_reduce(range, *this) : (*this)(range); + } + void operator()(const NodeRange& range) + { + NodeOp &op = *mNodeOp; + for (typename NodeRange::Iterator it = range.begin(); it; ++it) op(*it); + } + void join(const NodeReducer& other) + { + mNodeOp->join(*(other.mNodeOp)); + } + NodeOp *mNodeOp; + const bool mOwnsOp; + };// NodeList::NodeReducer + + +protected: + ListT mList; +};// NodeList + + +///////////////////////////////////////////// + + +/// @brief This class is a link in a chain that each caches tree nodes +/// of a specific type in a linear array. +/// +/// @note It is for internal use and should rarely be used directly. +template +class NodeManagerLink +{ +public: + NodeManagerLink() {} + + virtual ~NodeManagerLink() {} + + void clear() { mList.clear(); mNext.clear(); } + + template + void init(ParentT& parent, TreeOrLeafManagerT& tree) + { + parent.getNodes(mList); + for (size_t i=0, n=mList.nodeCount(); i + void rebuild(ParentT& parent) + { + mList.clear(); + parent.getNodes(mList); + for (size_t i=0, n=mList.nodeCount(); i + void foreachBottomUp(const NodeOp& op, bool threaded, size_t grainSize) + { + mNext.foreachBottomUp(op, threaded, grainSize); + mList.foreach(op, threaded, grainSize); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded, size_t grainSize) + { + mList.foreach(op, threaded, grainSize); + mNext.foreachTopDown(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded, size_t grainSize) + { + mNext.reduceBottomUp(op, threaded, grainSize); + mList.reduce(op, threaded, grainSize); + } + + template + void reduceTopDown(NodeOp& op, bool threaded, size_t grainSize) + { + mList.reduce(op, threaded, grainSize); + mNext.reduceTopDown(op, threaded, grainSize); + } + +protected: + NodeList mList; + NodeManagerLink mNext; +};// NodeManagerLink class + + +//////////////////////////////////////// + + +/// @private +/// @brief Specialization that terminates the chain of cached tree nodes +/// @note It is for internal use and should rarely be used directly. +template +class NodeManagerLink +{ +public: + NodeManagerLink() {} + + virtual ~NodeManagerLink() {} + + /// @brief Clear all the cached tree nodes + void clear() { mList.clear(); } + + template + void rebuild(ParentT& parent) { mList.clear(); parent.getNodes(mList); } + + Index64 nodeCount() const { return mList.nodeCount(); } + + Index64 nodeCount(Index) const { return mList.nodeCount(); } + + template + void foreachBottomUp(const NodeOp& op, bool threaded, size_t grainSize) + { + mList.foreach(op, threaded, grainSize); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded, size_t grainSize) + { + mList.foreach(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded, size_t grainSize) + { + mList.reduce(op, threaded, grainSize); + } + + template + void reduceTopDown(NodeOp& op, bool threaded, size_t grainSize) + { + mList.reduce(op, threaded, grainSize); + } + + template + void init(ParentT& parent, TreeOrLeafManagerT& tree) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (TreeOrLeafManagerT::DEPTH == 2 && NodeT::LEVEL == 0) { + tree.getNodes(mList); + } else { + parent.getNodes(mList); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } +protected: + NodeList mList; +};// NodeManagerLink class + + +//////////////////////////////////////// + + +/// @brief To facilitate threading over the nodes of a tree, cache +/// node pointers in linear arrays, one for each level of the tree. +/// +/// @details This implementation works with trees of any depth, but +/// optimized specializations are provided for the most typical tree depths. +template +class NodeManager +{ +public: + static const Index LEVELS = _LEVELS; + static_assert(LEVELS > 0, + "expected instantiation of template specialization"); // see specialization below + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL >= LEVELS, "number of levels exceeds root node height"); + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { mChain.init(mRoot, tree); } + + virtual ~NodeManager() {} + + /// @brief Clear all the cached tree nodes + void clear() { mChain.clear(); } + + /// @brief Clear and recache all the tree nodes from the + /// tree. This is required if tree nodes have been added or removed. + void rebuild() { mChain.rebuild(mRoot); } + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + /// @brief Return the total number of cached nodes (excluding the root node) + Index64 nodeCount() const { return mChain.nodeCount(); } + + /// @brief Return the number of cached nodes at level @a i, where + /// 0 corresponds to the lowest level. + Index64 nodeCount(Index i) const { return mChain.nodeCount(i); } + + //@{ + /// @brief Threaded method that applies a user-supplied functor + /// to all the nodes in the tree. + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @warning The functor object is deep-copied to create TBB tasks. + /// + /// @par Example: + /// @code + /// // Functor to offset all the inactive values of a tree. Note + /// // this implementation also illustrates how different + /// // computation can be applied to the different node types. + /// template + /// struct OffsetOp + /// { + /// using ValueT = typename TreeT::ValueType; + /// using RootT = typename TreeT::RootNodeType; + /// using LeafT = typename TreeT::LeafNodeType; + /// OffsetOp(const ValueT& v) : mOffset(v) {} + /// + /// // Processes the root node. Required by the NodeManager + /// void operator()(RootT& root) const + /// { + /// for (typename RootT::ValueOffIter i = root.beginValueOff(); i; ++i) *i += mOffset; + /// } + /// // Processes the leaf nodes. Required by the NodeManager + /// void operator()(LeafT& leaf) const + /// { + /// for (typename LeafT::ValueOffIter i = leaf.beginValueOff(); i; ++i) *i += mOffset; + /// } + /// // Processes the internal nodes. Required by the NodeManager + /// template + /// void operator()(NodeT& node) const + /// { + /// for (typename NodeT::ValueOffIter i = node.beginValueOff(); i; ++i) *i += mOffset; + /// } + /// private: + /// const ValueT mOffset; + /// }; + /// + /// // usage: + /// OffsetOp op(3.0f); + /// tree::NodeManager nodes(tree); + /// nodes.foreachBottomUp(op); + /// + /// // or if a LeafManager already exists + /// using T = tree::LeafManager; + /// OffsetOp op(3.0f); + /// tree::NodeManager nodes(leafManager); + /// nodes.foreachBottomUp(op); + /// + /// @endcode + template + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mChain.foreachBottomUp(op, threaded, grainSize); + op(mRoot); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mChain.foreachTopDown(op, threaded, grainSize); + } + + //@} + + //@{ + /// @brief Threaded method that processes nodes with a user supplied functor + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @warning The functor object is deep-copied to create TBB tasks. + /// + /// @par Example: + /// @code + /// // Functor to count nodes in a tree + /// template + /// struct NodeCountOp + /// { + /// NodeCountOp() : nodeCount(TreeType::DEPTH, 0), totalCount(0) + /// { + /// } + /// NodeCountOp(const NodeCountOp& other, tbb::split) : + /// nodeCount(TreeType::DEPTH, 0), totalCount(0) + /// { + /// } + /// void join(const NodeCountOp& other) + /// { + /// for (size_t i = 0; i < nodeCount.size(); ++i) { + /// nodeCount[i] += other.nodeCount[i]; + /// } + /// totalCount += other.totalCount; + /// } + /// // do nothing for the root node + /// void operator()(const typename TreeT::RootNodeType& node) + /// { + /// } + /// // count the internal and leaf nodes + /// template + /// void operator()(const NodeT& node) + /// { + /// ++(nodeCount[NodeT::LEVEL]); + /// ++totalCount; + /// } + /// std::vector nodeCount; + /// openvdb::Index64 totalCount; + /// }; + /// + /// // usage: + /// NodeCountOp op; + /// tree::NodeManager nodes(tree); + /// nodes.reduceBottomUp(op); + /// + /// // or if a LeafManager already exists + /// NodeCountOp op; + /// using T = tree::LeafManager; + /// T leafManager(tree); + /// tree::NodeManager nodes(leafManager); + /// nodes.reduceBottomUp(op); + /// + /// @endcode + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mChain.reduceBottomUp(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mChain.reduceTopDown(op, threaded, grainSize); + } + //@} + +protected: + RootNodeType& mRoot; + NodeManagerLink mChain; + +private: + NodeManager(const NodeManager&) {}//disallow copy-construction +};// NodeManager class + + +//////////////////////////////////////////// + + +/// @private +/// Template specialization of the NodeManager with no caching of nodes +template +class NodeManager +{ +public: + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static const Index LEVELS = 0; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) {} + + virtual ~NodeManager() {} + + /// @brief Clear all the cached tree nodes + void clear() {} + + /// @brief Clear and recache all the tree nodes from the + /// tree. This is required if tree nodes have been added or removed. + void rebuild() {} + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + /// @brief Return the total number of cached nodes (excluding the root node) + Index64 nodeCount() const { return 0; } + + Index64 nodeCount(Index) const { return 0; } + + template + void foreachBottomUp(const NodeOp& op, bool, size_t) { op(mRoot); } + + template + void foreachTopDown(const NodeOp& op, bool, size_t) { op(mRoot); } + + template + void reduceBottomUp(NodeOp& op, bool, size_t) { op(mRoot); } + + template + void reduceTopDown(NodeOp& op, bool, size_t) { op(mRoot); } + +protected: + RootNodeType& mRoot; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<0> + + +//////////////////////////////////////////// + + +/// @private +/// Template specialization of the NodeManager with one level of nodes +template +class NodeManager +{ +public: + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL > 0, "expected instantiation of template specialization"); + static const Index LEVELS = 1; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (TreeOrLeafManagerT::DEPTH == 2 && NodeT0::LEVEL == 0) { + tree.getNodes(mList0); + } else { + mRoot.getNodes(mList0); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + virtual ~NodeManager() {} + + /// @brief Clear all the cached tree nodes + void clear() { mList0.clear(); } + + /// @brief Clear and recache all the tree nodes from the + /// tree. This is required if tree nodes have been added or removed. + void rebuild() { mList0.clear(); mRoot.getNodes(mList0); } + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + /// @brief Return the total number of cached nodes (excluding the root node) + Index64 nodeCount() const { return mList0.nodeCount(); } + + /// @brief Return the number of cached nodes at level @a i, where + /// 0 corresponds to the lowest level. + Index64 nodeCount(Index i) const { return i==0 ? mList0.nodeCount() : 0; } + + template + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.foreach(op, threaded, grainSize); + op(mRoot); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList0.foreach(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.reduce(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList0.reduce(op, threaded, grainSize); + } + +protected: + using NodeT1 = RootNodeType; + using NodeT0 = typename NodeT1::ChildNodeType; + using ListT0 = NodeList; + + NodeT1& mRoot; + ListT0 mList0; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<1> + + +//////////////////////////////////////////// + + +/// @private +/// Template specialization of the NodeManager with two levels of nodes +template +class NodeManager +{ +public: + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL > 1, "expected instantiation of template specialization"); + static const Index LEVELS = 2; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + { + mRoot.getNodes(mList1); + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (TreeOrLeafManagerT::DEPTH == 2 && NodeT0::LEVEL == 0) { + tree.getNodes(mList0); + } else { + for (size_t i=0, n=mList1.nodeCount(); iclear(); + mRoot.getNodes(mList1); + for (size_t i=0, n=mList1.nodeCount(); i + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + op(mRoot); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList1.foreach(op, threaded, grainSize); + mList0.foreach(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList1.reduce(op, threaded, grainSize); + mList0.reduce(op, threaded, grainSize); + } + +protected: + using NodeT2 = RootNodeType; + using NodeT1 = typename NodeT2::ChildNodeType; // upper level + using NodeT0 = typename NodeT1::ChildNodeType; // lower level + + using ListT1 = NodeList; // upper level + using ListT0 = NodeList; // lower level + + NodeT2& mRoot; + ListT1 mList1; + ListT0 mList0; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<2> + + +//////////////////////////////////////////// + + +/// @private +/// Template specialization of the NodeManager with three levels of nodes +template +class NodeManager +{ +public: + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL > 2, "expected instantiation of template specialization"); + static const Index LEVELS = 3; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + { + mRoot.getNodes(mList2); + for (size_t i=0, n=mList2.nodeCount(); iclear(); + mRoot.getNodes(mList2); + for (size_t i=0, n=mList2.nodeCount(); i + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + mList2.foreach(op, threaded, grainSize); + op(mRoot); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList2.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + mList0.foreach(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + mList2.reduce(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList2.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + mList0.reduce(op, threaded, grainSize); + } + +protected: + using NodeT3 = RootNodeType; + using NodeT2 = typename NodeT3::ChildNodeType; // upper level + using NodeT1 = typename NodeT2::ChildNodeType; // mid level + using NodeT0 = typename NodeT1::ChildNodeType; // lower level + + using ListT2 = NodeList; // upper level of internal nodes + using ListT1 = NodeList; // lower level of internal nodes + using ListT0 = NodeList; // lower level of internal nodes or leafs + + NodeT3& mRoot; + ListT2 mList2; + ListT1 mList1; + ListT0 mList0; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<3> + + +//////////////////////////////////////////// + + +/// @private +/// Template specialization of the NodeManager with four levels of nodes +template +class NodeManager +{ +public: + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL > 3, "expected instantiation of template specialization"); + static const Index LEVELS = 4; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + { + mRoot.getNodes(mList3); + for (size_t i=0, n=mList3.nodeCount(); iclear(); + mRoot.getNodes(mList3); + for (size_t i=0, n=mList3.nodeCount(); i + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + mList2.foreach(op, threaded, grainSize); + mList3.foreach(op, threaded, grainSize); + op(mRoot); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList3.foreach(op, threaded, grainSize); + mList2.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + mList0.foreach(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + mList2.reduce(op, threaded, grainSize); + mList3.reduce(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList3.reduce(op, threaded, grainSize); + mList2.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + mList0.reduce(op, threaded, grainSize); + } + +protected: + using NodeT4 = RootNodeType; + using NodeT3 = typename NodeT4::ChildNodeType; // upper level + using NodeT2 = typename NodeT3::ChildNodeType; // upper mid level + using NodeT1 = typename NodeT2::ChildNodeType; // lower mid level + using NodeT0 = typename NodeT1::ChildNodeType; // lower level + + using ListT3 = NodeList; // upper level of internal nodes + using ListT2 = NodeList; // upper mid level of internal nodes + using ListT1 = NodeList; // lower mid level of internal nodes + using ListT0 = NodeList; // lower level of internal nodes or leafs + + NodeT4& mRoot; + ListT3 mList3; + ListT2 mList2; + ListT1 mList1; + ListT0 mList0; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<4> + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/NodeUnion.h b/openvdb/tree/NodeUnion.h new file mode 100644 index 00000000..afe7234c --- /dev/null +++ b/openvdb/tree/NodeUnion.h @@ -0,0 +1,200 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file NodeUnion.h +/// +/// @details NodeUnion is a templated helper class that controls access to either +/// the child node pointer or the value for a particular element of a root +/// or internal node. For space efficiency, the child pointer and the value +/// are unioned when possible, since the two are never in use simultaneously. + +#ifndef OPENVDB_TREE_NODEUNION_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_NODEUNION_HAS_BEEN_INCLUDED + +#include +#include +#include // for std::memcpy() +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +#if OPENVDB_ABI_VERSION_NUMBER >= 4 + +// Forward declaration of traits class +template struct CopyTraits; + +// Default implementation that stores the child pointer and the value separately +// (i.e., not in a union) +// This implementation is not used for POD, math::Vec or math::Coord value types. +template +class NodeUnion +{ +private: + ChildT* mChild; + ValueT mValue; + +public: + NodeUnion(): mChild(nullptr), mValue() {} + + ChildT* getChild() const { return mChild; } + void setChild(ChildT* child) { mChild = child; } + + const ValueT& getValue() const { return mValue; } + ValueT& getValue() { return mValue; } + void setValue(const ValueT& val) { mValue = val; } +}; + + +// Template specialization for values of POD types (int, float, pointer, etc.) +template +class NodeUnion::value>::type> +{ +private: + union { ChildT* mChild; ValueT mValue; }; + +public: + NodeUnion(): mChild(nullptr) {} + + ChildT* getChild() const { return mChild; } + void setChild(ChildT* child) { mChild = child; } + + const ValueT& getValue() const { return mValue; } + ValueT& getValue() { return mValue; } + void setValue(const ValueT& val) { mValue = val; } +}; + + +// Template specialization for values of types such as math::Vec3f and math::Coord +// for which CopyTraits::IsCopyable is true +template +class NodeUnion::IsCopyable>::type> +{ +private: + union { ChildT* mChild; ValueT mValue; }; + +public: + NodeUnion(): mChild(nullptr) {} + NodeUnion(const NodeUnion& other): mChild(nullptr) + { std::memcpy(this, &other, sizeof(*this)); } + NodeUnion& operator=(const NodeUnion& rhs) + { std::memcpy(this, &rhs, sizeof(*this)); return *this; } + + ChildT* getChild() const { return mChild; } + void setChild(ChildT* child) { mChild = child; } + + const ValueT& getValue() const { return mValue; } + ValueT& getValue() { return mValue; } + void setValue(const ValueT& val) { mValue = val; } +}; + + +/// @details A type T is copyable if +/// # T stores member values by value (vs. by pointer or reference) +/// and T's true byte size is given by sizeof(T). +/// # T has a trivial destructor +/// # T has a default constructor +/// # T has an assignment operator +template struct CopyTraits { static const bool IsCopyable = false; }; +template struct CopyTraits> { static const bool IsCopyable = true; }; +template struct CopyTraits> { static const bool IsCopyable = true; }; +template struct CopyTraits> { static const bool IsCopyable = true; }; +template<> struct CopyTraits { static const bool IsCopyable = true; }; + + +//////////////////////////////////////// + + +#else // OPENVDB_ABI_VERSION_NUMBER <= 3 + +// Prior to OpenVDB 4 and the introduction of C++11, values of non-POD types +// were heap-allocated and stored by pointer due to C++98 restrictions on unions. + +// Internal implementation of a union of a child node pointer and a value +template class NodeUnionImpl; + + +// Partial specialization for values of non-class types +// (int, float, pointer, etc.) that stores elements by value +template +class NodeUnionImpl +{ +private: + union { ChildT* child; ValueT value; } mUnion; + +public: + NodeUnionImpl() { mUnion.child = nullptr; } + + ChildT* getChild() const { return mUnion.child; } + void setChild(ChildT* child) { mUnion.child = child; } + + const ValueT& getValue() const { return mUnion.value; } + ValueT& getValue() { return mUnion.value; } + void setValue(const ValueT& val) { mUnion.value = val; } +}; + + +// Partial specialization for values of class types (std::string, +// math::Vec, etc.) that stores elements by pointer +template +class NodeUnionImpl +{ +private: + union { ChildT* child; ValueT* value; } mUnion; + bool mHasChild; + +public: + NodeUnionImpl() : mHasChild(true) { this->setChild(nullptr); } + NodeUnionImpl(const NodeUnionImpl& other) : mHasChild(true) + { + if (other.mHasChild) { + this->setChild(other.getChild()); + } else { + this->setValue(other.getValue()); + } + } + NodeUnionImpl& operator=(const NodeUnionImpl& other) + { + if (other.mHasChild) { + this->setChild(other.getChild()); + } else { + this->setValue(other.getValue()); + } + return *this; + } + ~NodeUnionImpl() { this->setChild(nullptr); } + + ChildT* getChild() const { return mHasChild ? mUnion.child : nullptr; } + void setChild(ChildT* child) + { + if (!mHasChild) delete mUnion.value; + mUnion.child = child; + mHasChild = true; + } + + const ValueT& getValue() const { return *mUnion.value; } + ValueT& getValue() { return *mUnion.value; } + void setValue(const ValueT& val) + { + if (!mHasChild) delete mUnion.value; + mUnion.value = new ValueT(val); + mHasChild = false; + } +}; + + +template +struct NodeUnion: public NodeUnionImpl::value, ValueT, ChildT> +{ + NodeUnion() {} +}; + +#endif + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_NODEUNION_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/RootNode.h b/openvdb/tree/RootNode.h new file mode 100644 index 00000000..cb5b4a5a --- /dev/null +++ b/openvdb/tree/RootNode.h @@ -0,0 +1,3484 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +/// +/// @file RootNode.h +/// +/// @brief The root node of an OpenVDB tree + +#ifndef OPENVDB_TREE_ROOTNODE_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_ROOTNODE_HAS_BEEN_INCLUDED + +#include +#include +#include // for truncateRealToHalf() +#include // for isZero(), isExactlyEqual(), etc. +#include +#include // for backward compatibility only (see readTopology()) +#include +#include +#include //for boost::mpl::vector +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +// Forward declarations +template struct NodeChain; +template struct SameRootConfig; +template struct RootNodeCopyHelper; +template struct RootNodeCombineHelper; + + +template +class RootNode +{ +public: + using ChildNodeType = ChildType; + using LeafNodeType = typename ChildType::LeafNodeType; + using ValueType = typename ChildType::ValueType; + using BuildType = typename ChildType::BuildType; + + static const Index LEVEL = 1 + ChildType::LEVEL; // level 0 = leaf + + /// NodeChainType is a list of this tree's node types, from LeafNodeType to RootNode. + using NodeChainType = typename NodeChain::Type; + static_assert(boost::mpl::size::value == LEVEL + 1, + "wrong number of entries in RootNode node chain"); + + /// @brief ValueConverter::Type is the type of a RootNode having the same + /// child hierarchy as this node but a different value type, T. + template + struct ValueConverter { + using Type = RootNode::Type>; + }; + + /// @brief SameConfiguration::value is @c true if and only if + /// OtherNodeType is the type of a RootNode whose ChildNodeType has the same + /// configuration as this node's ChildNodeType. + template + struct SameConfiguration { + static const bool value = SameRootConfig::value; + }; + + + /// Construct a new tree with a background value of 0. + RootNode(); + + /// Construct a new tree with the given background value. + explicit RootNode(const ValueType& background); + + RootNode(const RootNode& other) { *this = other; } + + /// @brief Construct a new tree that reproduces the topology and active states + /// of a tree of a different ValueType but the same configuration (levels, + /// node dimensions and branching factors). Cast the other tree's values to + /// this tree's ValueType. + /// @throw TypeError if the other tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the other tree's ValueType. + template + explicit RootNode(const RootNode& other) { *this = other; } + + /// @brief Construct a new tree that reproduces the topology and active states of + /// another tree (which may have a different ValueType), but not the other tree's values. + /// @details All tiles and voxels that are active in the other tree are set to + /// @a foreground in the new tree, and all inactive tiles and voxels are set to @a background. + /// @param other the root node of a tree having (possibly) a different ValueType + /// @param background the value to which inactive tiles and voxels are initialized + /// @param foreground the value to which active tiles and voxels are initialized + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + RootNode(const RootNode& other, + const ValueType& background, const ValueType& foreground, TopologyCopy); + + /// @brief Construct a new tree that reproduces the topology and active states of + /// another tree (which may have a different ValueType), but not the other tree's values. + /// All tiles and voxels in the new tree are set to @a background regardless of + /// their active states in the other tree. + /// @param other the root node of a tree having (possibly) a different ValueType + /// @param background the value to which inactive tiles and voxels are initialized + /// @note This copy constructor is generally faster than the one that takes both + /// a foreground and a background value. Its main application is in multithreaded + /// operations where the topology of the output tree exactly matches the input tree. + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + RootNode(const RootNode& other, const ValueType& background, TopologyCopy); + + /// @brief Copy a root node of the same type as this node. + RootNode& operator=(const RootNode& other); + /// @brief Copy a root node of the same tree configuration as this node + /// but a different ValueType. + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + /// @note This node's ValueType must be constructible from the other node's ValueType. + /// For example, a root node with values of type float can be assigned to a root node + /// with values of type Vec3s, because a Vec3s can be constructed from a float. + /// But a Vec3s root node cannot be assigned to a float root node. + template + RootNode& operator=(const RootNode& other); + + ~RootNode() { this->clear(); } + +private: + struct Tile { + Tile(): value(zeroVal()), active(false) {} + Tile(const ValueType& v, bool b): value(v), active(b) {} + ValueType value; + bool active; + }; + + // This lightweight struct pairs child pointers and tiles. + struct NodeStruct { + ChildType* child; + Tile tile; + + NodeStruct(): child(nullptr) {} + NodeStruct(ChildType& c): child(&c) {} + NodeStruct(const Tile& t): child(nullptr), tile(t) {} + NodeStruct(const NodeStruct&) = default; + NodeStruct& operator=(const NodeStruct&) = default; + ~NodeStruct() {} ///< @note doesn't delete child + + bool isChild() const { return child != nullptr; } + bool isTile() const { return child == nullptr; } + bool isTileOff() const { return isTile() && !tile.active; } + bool isTileOn() const { return isTile() && tile.active; } + + void set(ChildType& c) { delete child; child = &c; } + void set(const Tile& t) { delete child; child = nullptr; tile = t; } + ChildType& steal(const Tile& t) { ChildType* c=child; child=nullptr; tile=t; return *c; } + }; + + using MapType = std::map; + using MapIter = typename MapType::iterator; + using MapCIter = typename MapType::const_iterator; + + using CoordSet = std::set; + using CoordSetIter = typename CoordSet::iterator; + using CoordSetCIter = typename CoordSet::const_iterator; + + static void setTile(const MapIter& i, const Tile& t) { i->second.set(t); } + static void setChild(const MapIter& i, ChildType& c) { i->second.set(c); } + static Tile& getTile(const MapIter& i) { return i->second.tile; } + static const Tile& getTile(const MapCIter& i) { return i->second.tile; } + static ChildType& getChild(const MapIter& i) { return *(i->second.child); } + static const ChildType& getChild(const MapCIter& i) { return *(i->second.child); } + static ChildType& stealChild(const MapIter& i, const Tile& t) {return i->second.steal(t);} + static const ChildType& stealChild(const MapCIter& i,const Tile& t) {return i->second.steal(t);} + + static bool isChild(const MapCIter& i) { return i->second.isChild(); } + static bool isChild(const MapIter& i) { return i->second.isChild(); } + static bool isTile(const MapCIter& i) { return i->second.isTile(); } + static bool isTile(const MapIter& i) { return i->second.isTile(); } + static bool isTileOff(const MapCIter& i) { return i->second.isTileOff(); } + static bool isTileOff(const MapIter& i) { return i->second.isTileOff(); } + static bool isTileOn(const MapCIter& i) { return i->second.isTileOn(); } + static bool isTileOn(const MapIter& i) { return i->second.isTileOn(); } + + struct NullPred { + static inline bool test(const MapIter&) { return true; } + static inline bool test(const MapCIter&) { return true; } + }; + struct ValueOnPred { + static inline bool test(const MapIter& i) { return isTileOn(i); } + static inline bool test(const MapCIter& i) { return isTileOn(i); } + }; + struct ValueOffPred { + static inline bool test(const MapIter& i) { return isTileOff(i); } + static inline bool test(const MapCIter& i) { return isTileOff(i); } + }; + struct ValueAllPred { + static inline bool test(const MapIter& i) { return isTile(i); } + static inline bool test(const MapCIter& i) { return isTile(i); } + }; + struct ChildOnPred { + static inline bool test(const MapIter& i) { return isChild(i); } + static inline bool test(const MapCIter& i) { return isChild(i); } + }; + struct ChildOffPred { + static inline bool test(const MapIter& i) { return isTile(i); } + static inline bool test(const MapCIter& i) { return isTile(i); } + }; + + template + class BaseIter + { + public: + using RootNodeT = _RootNodeT; + using MapIterT = _MapIterT; // either MapIter or MapCIter + + bool operator==(const BaseIter& other) const + { + return (mParentNode == other.mParentNode) && (mIter == other.mIter); + } + bool operator!=(const BaseIter& other) const { return !(*this == other); } + + RootNodeT* getParentNode() const { return mParentNode; } + /// Return a reference to the node over which this iterator iterates. + RootNodeT& parent() const + { + if (!mParentNode) OPENVDB_THROW(ValueError, "iterator references a null parent node"); + return *mParentNode; + } + + bool test() const { assert(mParentNode); return mIter != mParentNode->mTable.end(); } + operator bool() const { return this->test(); } + + void increment() { if (this->test()) { ++mIter; } this->skip(); } + bool next() { this->increment(); return this->test(); } + void increment(Index n) { for (Index i = 0; i < n && this->next(); ++i) {} } + + /// @brief Return this iterator's position as an offset from + /// the beginning of the parent node's map. + Index pos() const + { + return !mParentNode ? 0U : Index(std::distance(mParentNode->mTable.begin(), mIter)); + } + + bool isValueOn() const { return RootNodeT::isTileOn(mIter); } + bool isValueOff() const { return RootNodeT::isTileOff(mIter); } + void setValueOn(bool on = true) const { mIter->second.tile.active = on; } + void setValueOff() const { mIter->second.tile.active = false; } + + /// Return the coordinates of the item to which this iterator is pointing. + Coord getCoord() const { return mIter->first; } + /// Return in @a xyz the coordinates of the item to which this iterator is pointing. + void getCoord(Coord& xyz) const { xyz = this->getCoord(); } + + protected: + BaseIter(): mParentNode(nullptr) {} + BaseIter(RootNodeT& parent, const MapIterT& iter): mParentNode(&parent), mIter(iter) {} + + void skip() { while (this->test() && !FilterPredT::test(mIter)) ++mIter; } + + RootNodeT* mParentNode; + MapIterT mIter; + }; // BaseIter + + template + class ChildIter: public BaseIter + { + public: + using BaseT = BaseIter; + using NodeType = RootNodeT; + using ValueType = NodeType; + using ChildNodeType = ChildNodeT; + using NonConstNodeType = typename std::remove_const::type; + using NonConstValueType = typename std::remove_const::type; + using NonConstChildNodeType = typename std::remove_const::type; + using BaseT::mIter; + + ChildIter() {} + ChildIter(RootNodeT& parent, const MapIterT& iter): BaseT(parent, iter) { BaseT::skip(); } + + ChildIter& operator++() { BaseT::increment(); return *this; } + + ChildNodeT& getValue() const { return getChild(mIter); } + ChildNodeT& operator*() const { return this->getValue(); } + ChildNodeT* operator->() const { return &this->getValue(); } + }; // ChildIter + + template + class ValueIter: public BaseIter + { + public: + using BaseT = BaseIter; + using NodeType = RootNodeT; + using ValueType = ValueT; + using NonConstNodeType = typename std::remove_const::type; + using NonConstValueType = typename std::remove_const::type; + using BaseT::mIter; + + ValueIter() {} + ValueIter(RootNodeT& parent, const MapIterT& iter): BaseT(parent, iter) { BaseT::skip(); } + + ValueIter& operator++() { BaseT::increment(); return *this; } + + ValueT& getValue() const { return getTile(mIter).value; } + ValueT& operator*() const { return this->getValue(); } + ValueT* operator->() const { return &(this->getValue()); } + + void setValue(const ValueT& v) const { assert(isTile(mIter)); getTile(mIter).value = v; } + + template + void modifyValue(const ModifyOp& op) const + { + assert(isTile(mIter)); + op(getTile(mIter).value); + } + }; // ValueIter + + template + class DenseIter: public BaseIter + { + public: + using BaseT = BaseIter; + using NodeType = RootNodeT; + using ValueType = ValueT; + using ChildNodeType = ChildNodeT; + using NonConstNodeType = typename std::remove_const::type; + using NonConstValueType = typename std::remove_const::type; + using NonConstChildNodeType = typename std::remove_const::type; + using BaseT::mIter; + + DenseIter() {} + DenseIter(RootNodeT& parent, const MapIterT& iter): BaseT(parent, iter) {} + + DenseIter& operator++() { BaseT::increment(); return *this; } + + bool isChildNode() const { return isChild(mIter); } + + ChildNodeT* probeChild(NonConstValueType& value) const + { + if (isChild(mIter)) return &getChild(mIter); + value = getTile(mIter).value; + return nullptr; + } + bool probeChild(ChildNodeT*& child, NonConstValueType& value) const + { + child = this->probeChild(value); + return child != nullptr; + } + bool probeValue(NonConstValueType& value) const { return !this->probeChild(value); } + + void setChild(ChildNodeT& c) const { RootNodeT::setChild(mIter, c); } + void setChild(ChildNodeT* c) const { assert(c != nullptr); RootNodeT::setChild(mIter, *c); } + void setValue(const ValueT& v) const + { + if (isTile(mIter)) getTile(mIter).value = v; + /// @internal For consistency with iterators for other node types + /// (see, e.g., InternalNode::DenseIter::unsetItem()), we don't call + /// setTile() here, because that would also delete the child. + else stealChild(mIter, Tile(v, /*active=*/true)); + } + }; // DenseIter + +public: + using ChildOnIter = ChildIter; + using ChildOnCIter = ChildIter; + using ChildOffIter = ValueIter; + using ChildOffCIter = ValueIter; + using ChildAllIter = DenseIter; + using ChildAllCIter = DenseIter; + + using ValueOnIter = ValueIter; + using ValueOnCIter = ValueIter; + using ValueOffIter = ValueIter; + using ValueOffCIter = ValueIter; + using ValueAllIter = ValueIter; + using ValueAllCIter = ValueIter; + + + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(*this, mTable.begin()); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(*this, mTable.begin()); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(*this, mTable.begin()); } + ChildOnCIter beginChildOn() const { return cbeginChildOn(); } + ChildOffCIter beginChildOff() const { return cbeginChildOff(); } + ChildAllCIter beginChildAll() const { return cbeginChildAll(); } + ChildOnIter beginChildOn() { return ChildOnIter(*this, mTable.begin()); } + ChildOffIter beginChildOff() { return ChildOffIter(*this, mTable.begin()); } + ChildAllIter beginChildAll() { return ChildAllIter(*this, mTable.begin()); } + + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(*this, mTable.begin()); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(*this, mTable.begin()); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(*this, mTable.begin()); } + ValueOnCIter beginValueOn() const { return cbeginValueOn(); } + ValueOffCIter beginValueOff() const { return cbeginValueOff(); } + ValueAllCIter beginValueAll() const { return cbeginValueAll(); } + ValueOnIter beginValueOn() { return ValueOnIter(*this, mTable.begin()); } + ValueOffIter beginValueOff() { return ValueOffIter(*this, mTable.begin()); } + ValueAllIter beginValueAll() { return ValueAllIter(*this, mTable.begin()); } + + /// Return the total amount of memory in bytes occupied by this node and its children. + Index64 memUsage() const; + + /// @brief Expand the specified bbox so it includes the active tiles of + /// this root node as well as all the active values in its child + /// nodes. If visitVoxels is false LeafNodes will be approximated + /// as dense, i.e. with all voxels active. Else the individual + /// active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; + + /// Return the bounding box of this RootNode, i.e., an infinite bounding box. + static CoordBBox getNodeBoundingBox() { return CoordBBox::inf(); } + + /// @brief Change inactive tiles or voxels with a value equal to +/- the + /// old background to the specified value (with the same sign). Active values + /// are unchanged. + /// + /// @param value The new background value + /// @param updateChildNodes If true the background values of the + /// child nodes is also updated. Else only the background value + /// stored in the RootNode itself is changed. + /// + /// @note Instead of setting @a updateChildNodes to true, consider + /// using tools::changeBackground or + /// tools::changeLevelSetBackground which are multi-threaded! + void setBackground(const ValueType& value, bool updateChildNodes); + + /// Return this node's background value. + const ValueType& background() const { return mBackground; } + + /// Return @c true if the given tile is inactive and has the background value. + bool isBackgroundTile(const Tile&) const; + //@{ + /// Return @c true if the given iterator points to an inactive tile with the background value. + bool isBackgroundTile(const MapIter&) const; + bool isBackgroundTile(const MapCIter&) const; + //@} + + /// Return the number of background tiles. + size_t numBackgroundTiles() const; + /// @brief Remove all background tiles. + /// @return the number of tiles removed. + size_t eraseBackgroundTiles(); + inline void clear(); + + /// Return @c true if this node's table is either empty or contains only background tiles. + bool empty() const { return mTable.size() == numBackgroundTiles(); } + + /// @brief Expand this node's table so that (x, y, z) is included in the index range. + /// @return @c true if an expansion was performed (i.e., if (x, y, z) was not already + /// included in the index range). + bool expand(const Coord& xyz); + + static Index getLevel() { return LEVEL; } + static void getNodeLog2Dims(std::vector& dims); + static Index getChildDim() { return ChildType::DIM; } + + /// Return the number of entries in this node's table. + Index getTableSize() const { return static_cast(mTable.size()); } + + Index getWidth() const { return this->getMaxIndex()[0] - this->getMinIndex()[0]; } + Index getHeight() const { return this->getMaxIndex()[1] - this->getMinIndex()[1]; } + Index getDepth() const { return this->getMaxIndex()[2] - this->getMinIndex()[2]; } + + /// Return the smallest index of the current tree. + Coord getMinIndex() const; + /// Return the largest index of the current tree. + Coord getMaxIndex() const; + /// Return the current index range. Both min and max are inclusive. + void getIndexRange(CoordBBox& bbox) const; + + /// @brief Return @c true if the given tree has the same node and active value + /// topology as this tree (but possibly a different @c ValueType). + template + bool hasSameTopology(const RootNode& other) const; + + /// Return @c false if the other node's dimensions don't match this node's. + template + static bool hasSameConfiguration(const RootNode& other); + + /// Return @c true if values of the other node's ValueType can be converted + /// to values of this node's ValueType. + template + static bool hasCompatibleValueType(const RootNode& other); + + Index32 leafCount() const; + Index32 nonLeafCount() const; + Index64 onVoxelCount() const; + Index64 offVoxelCount() const; + Index64 onLeafVoxelCount() const; + Index64 offLeafVoxelCount() const; + Index64 onTileCount() const; + void nodeCount(std::vector &vec) const; + + bool isValueOn(const Coord& xyz) const; + + /// Return @c true if this root node, or any of its child nodes, have active tiles. + bool hasActiveTiles() const; + + const ValueType& getValue(const Coord& xyz) const; + bool probeValue(const Coord& xyz, ValueType& value) const; + + /// @brief Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides. + /// @details If (x, y, z) isn't explicitly represented in the tree (i.e., + /// it is implicitly a background voxel), return -1. + int getValueDepth(const Coord& xyz) const; + + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOn(const Coord& xyz, const ValueType& value); + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value); + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + //@{ + /// @brief Set all voxels within a given axis-aligned box to a constant value. + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box + /// @param value the value to which to set voxels within the box + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive + /// @note This operation generates a sparse, but not always optimally sparse, + /// representation of the filled box. Follow fill operations with a prune() + /// operation for optimal sparseness. + void fill(const CoordBBox& bbox, const ValueType& value, bool active = true); + void sparseFill(const CoordBBox& bbox, const ValueType& value, bool active = true) + { + this->fill(bbox, value, active); + } + //@} + + /// @brief Set all voxels within a given axis-aligned box to a constant value + /// and ensure that those voxels are all represented at the leaf level. + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box. + /// @param value the value to which to set voxels within the box. + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive. + /// @sa voxelizeActiveTiles() + void denseFill(const CoordBBox& bbox, const ValueType& value, bool active = true); + + /// @brief Densify active tiles, i.e., replace them with leaf-level active voxels. + /// + /// @param threaded if true, this operation is multi-threaded (over the internal nodes). + /// + /// @warning This method can explode the tree's memory footprint, especially if it + /// contains active tiles at the upper levels (in particular the root level)! + /// + /// @sa denseFill() + void voxelizeActiveTiles(bool threaded = true); + + /// @brief Copy into a dense grid the values of all voxels, both active and inactive, + /// that intersect a given bounding box. + /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid + /// @param dense dense grid with a stride in @e z of one (see tools::Dense + /// in tools/Dense.h for the required API) + template + void copyToDense(const CoordBBox& bbox, DenseT& dense) const; + + + // + // I/O + // + bool writeTopology(std::ostream&, bool toHalf = false) const; + bool readTopology(std::istream&, bool fromHalf = false); + + void writeBuffers(std::ostream&, bool toHalf = false) const; + void readBuffers(std::istream&, bool fromHalf = false); + void readBuffers(std::istream&, const CoordBBox&, bool fromHalf = false); + + + // + // Voxel access + // + /// Return the value of the voxel at the given coordinates and, if necessary, update + /// the accessor with pointers to the nodes along the path from the root node to + /// the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const; + /// Return @c true if the voxel at the given coordinates is active and, if necessary, + /// update the accessor with pointers to the nodes along the path from the root node + /// to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + bool isValueOnAndCache(const Coord& xyz, AccessorT&) const; + + /// Change the value of the voxel at the given coordinates and mark it as active. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + + /// Set the value of the voxel at the given coordinates without changing its active state. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + + /// Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); + + /// Apply a functor to the voxel at the given coordinates. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); + + /// Change the value of the voxel at the given coordinates and mark it as inactive. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + + /// Set the active state of the voxel at the given coordinates without changing its value. + /// If necessary, update the accessor with pointers to the nodes along the path + /// from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&); + + /// Return, in @a value, the value of the voxel at the given coordinates and, + /// if necessary, update the accessor with pointers to the nodes along + /// the path from the root node to the node containing the voxel. + /// @return @c true if the voxel at the given coordinates is active + /// @note Used internally by ValueAccessor. + template + bool probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT&) const; + + /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides. + /// If (x, y, z) isn't explicitly represented in the tree (i.e., it is implicitly + /// a background voxel), return -1. If necessary, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing the voxel. + /// @note Used internally by ValueAccessor. + template + int getValueDepthAndCache(const Coord& xyz, AccessorT&) const; + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&); + + /// @brief Reduce the memory footprint of this tree by replacing with tiles + /// any nodes whose values are all the same (optionally to within a tolerance) + /// and have the same active state. + /// + /// @note Consider instead using tools::prune which is multi-threaded! + void prune(const ValueType& tolerance = zeroVal()); + + /// @brief Add the given leaf node to this tree, creating a new branch if necessary. + /// If a leaf node with the same origin already exists, replace it. + void addLeaf(LeafNodeType* leaf); + + /// @brief Same as addLeaf() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + void addLeafAndCache(LeafNodeType* leaf, AccessorT&); + + /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z) + /// and replace it with a tile of the specified value and state. + /// If no such node exists, leave the tree unchanged and return @c nullptr. + /// + /// @note The caller takes ownership of the node and is responsible for deleting it. + /// + /// @warning Since this method potentially removes nodes and branches of the tree, + /// it is important to clear the caches of all ValueAccessors associated with this tree. + template + NodeT* stealNode(const Coord& xyz, const ValueType& value, bool state); + + /// @brief Add the given child node at the root level. + /// If a child node with the same origin already exists, delete the old node and add + /// the new node in its place (i.e. ownership of the new child node is transferred + /// to this RootNode). + /// @return @c true (for consistency with InternalNode::addChild) + bool addChild(ChildType* child); + + /// @brief Add a tile containing voxel (x, y, z) at the root level, + /// deleting the existing branch if necessary. + void addTile(const Coord& xyz, const ValueType& value, bool state); + + /// @brief Add a tile containing voxel (x, y, z) at the specified tree level, + /// creating a new branch if necessary. Delete any existing lower-level nodes + /// that contain (x, y, z). + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state); + + /// @brief Same as addTile() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + void addTileAndCache(Index level, const Coord& xyz, const ValueType&, bool state, AccessorT&); + + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, create one that preserves the values and + /// active states of all voxels. + /// @details Use this method to preallocate a static tree topology + /// over which to safely perform multithreaded processing. + LeafNodeType* touchLeaf(const Coord& xyz); + + /// @brief Same as touchLeaf() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + LeafNodeType* touchLeafAndCache(const Coord& xyz, AccessorT& acc); + + //@{ + /// @brief Return a pointer to the node that contains voxel (x, y, z). + /// If no such node exists, return @c nullptr. + template + NodeT* probeNode(const Coord& xyz); + template + const NodeT* probeConstNode(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeNode() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + NodeT* probeNodeAndCache(const Coord& xyz, AccessorT& acc); + template + const NodeT* probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const; + //@} + + //@{ + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, return @c nullptr. + LeafNodeType* probeLeaf(const Coord& xyz); + const LeafNodeType* probeConstLeaf(const Coord& xyz) const; + const LeafNodeType* probeLeaf(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeLeaf() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc); + template + const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const; + template + const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const; + //@} + + + // + // Aux methods + // + + //@{ + /// @brief Adds all nodes of a certain type to a container with the following API: + /// @code + /// struct ArrayT { + /// using value_type = ...;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// using value_type = LeafType*; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.getNodes(array); + /// @endcode + template void getNodes(ArrayT& array); + template void getNodes(ArrayT& array) const; + //@} + + //@{ + /// @brief Steals all nodes of a certain type from the tree and + /// adds them to a container with the following API: + /// @code + /// struct ArrayT { + /// using value_type = ...;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// using value_type = LeafType*; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.stealNodes(array); + /// @endcode + template + void stealNodes(ArrayT& array, const ValueType& value, bool state); + template + void stealNodes(ArrayT& array) { this->stealNodes(array, mBackground, false); } + //@} + + /// @brief Efficiently merge another tree into this tree using one of several schemes. + /// @details This operation is primarily intended to combine trees that are mostly + /// non-overlapping (for example, intermediate trees from computations that are + /// parallelized across disjoint regions of space). + /// @note This operation is not guaranteed to produce an optimally sparse tree. + /// Follow merge() with prune() for optimal sparseness. + /// @warning This operation always empties the other tree. + template void merge(RootNode& other); + + /// @brief Union this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. + /// @details The resulting state of a value is active if the corresponding value + /// was already active OR if it is active in the other tree. Also, a resulting + /// value maps to a voxel if the corresponding value already mapped to a voxel + /// OR if it is a voxel in the other tree. Thus, a resulting value can only + /// map to a tile if the corresponding value already mapped to a tile + /// AND if it is a tile value in other tree. + /// + /// @note This operation modifies only active states, not values. + /// Specifically, active tiles and voxels in this tree are not changed, and + /// tiles or voxels that were inactive in this tree but active in the other tree + /// are marked as active in this tree but left with their original values. + template + void topologyUnion(const RootNode& other); + + /// @brief Intersects this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. + /// @details The resulting state of a value is active only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with inactive tiles in the other grid. Likewise active + /// voxels can be turned into inactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template + void topologyIntersection(const RootNode& other); + + /// @brief Difference this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this tree and inactive in the other tree. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with active tiles in the other grid. Likewise active + /// voxels can be turned into inactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template + void topologyDifference(const RootNode& other); + + template + void combine(RootNode& other, CombineOp&, bool prune = false); + + template + void combine2(const RootNode& other0, const OtherRootNode& other1, + CombineOp& op, bool prune = false); + + /// @brief Call the templated functor BBoxOp with bounding box + /// information for all active tiles and leaf nodes in the tree. + /// An additional level argument is provided for each callback. + /// + /// @note The bounding boxes are guaranteed to be non-overlapping. + template void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2(OtherRootNodeType& other, VisitorOp&); + template + void visit2(OtherRootNodeType& other, VisitorOp&) const; + +private: + /// During topology-only construction, access is needed + /// to protected/private members of other template instances. + template friend class RootNode; + + template friend struct RootNodeCopyHelper; + template friend struct RootNodeCombineHelper; + + /// Currently no-op, but can be used to define empty and delete keys for mTable + void initTable() {} + //@{ + /// @internal Used by doVisit2(). + void resetTable(MapType& table) { mTable.swap(table); table.clear(); } + void resetTable(const MapType&) const {} + //@} + + Index getChildCount() const; + Index getTileCount() const; + Index getActiveTileCount() const; + Index getInactiveTileCount() const; + + /// Return a MapType key for the given coordinates. + static Coord coordToKey(const Coord& xyz) { return xyz & ~(ChildType::DIM - 1); } + + /// Insert this node's mTable keys into the given set. + void insertKeys(CoordSet&) const; + + /// Return @c true if this node's mTable contains the given key. + bool hasKey(const Coord& key) const { return mTable.find(key) != mTable.end(); } + //@{ + /// @brief Look up the given key in this node's mTable. + /// @return an iterator pointing to the matching mTable entry or to mTable.end(). + MapIter findKey(const Coord& key) { return mTable.find(key); } + MapCIter findKey(const Coord& key) const { return mTable.find(key); } + //@} + //@{ + /// @brief Convert the given coordinates to a key and look the key up in this node's mTable. + /// @return an iterator pointing to the matching mTable entry or to mTable.end(). + MapIter findCoord(const Coord& xyz) { return mTable.find(coordToKey(xyz)); } + MapCIter findCoord(const Coord& xyz) const { return mTable.find(coordToKey(xyz)); } + //@} + /// @brief Convert the given coordinates to a key and look the key up in this node's mTable. + /// @details If the key is not found, insert a background tile with that key. + /// @return an iterator pointing to the matching mTable entry. + MapIter findOrAddCoord(const Coord& xyz); + + /// @brief Verify that the tree rooted at @a other has the same configuration + /// (levels, branching factors and node dimensions) as this tree, but allow + /// their ValueTypes to differ. + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + static void enforceSameConfiguration(const RootNode& other); + + /// @brief Verify that @a other has values of a type that can be converted + /// to this node's ValueType. + /// @details For example, values of type float are compatible with values of type Vec3s, + /// because a Vec3s can be constructed from a float. But the reverse is not true. + /// @throw TypeError if the other node's ValueType is not convertible into this node's. + template + static void enforceCompatibleValueTypes(const RootNode& other); + + template + void doCombine2(const RootNode&, const OtherRootNode&, CombineOp&, bool prune); + + template + static inline void doVisit(RootNodeT&, VisitorOp&); + + template + static inline void doVisit2(RootNodeT&, OtherRootNodeT&, VisitorOp&); + + + MapType mTable; + ValueType mBackground; +}; // end of RootNode class + + +//////////////////////////////////////// + + +/// @brief NodeChain::Type is a boost::mpl::vector +/// that lists the types of the nodes of the tree rooted at RootNodeType in reverse order, +/// from LeafNode to RootNode. +/// @details For example, if RootNodeType is +/// @code +/// RootNode > > +/// @endcode +/// then NodeChain::Type is +/// @code +/// boost::mpl::vector< +/// LeafNode, +/// InternalNode, +/// InternalNode >, +/// RootNode > > > +/// @endcode +/// +/// @note Use the following to get the Nth node type, where N=0 is the LeafNodeType: +/// @code +/// boost::mpl::at >::type +/// @endcode +template +struct NodeChain { + using SubtreeT = typename NodeChain::Type; + using Type = typename boost::mpl::push_back::type; +}; + +/// Specialization to terminate NodeChain +template +struct NodeChain { + using Type = typename boost::mpl::vector::type; +}; + + +//////////////////////////////////////// + + +//@{ +/// Helper metafunction used to implement RootNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameRootConfig { + static const bool value = false; +}; + +template +struct SameRootConfig > { + static const bool value = ChildT1::template SameConfiguration::value; +}; +//@} + + +//////////////////////////////////////// + + +template +inline +RootNode::RootNode(): mBackground(zeroVal()) +{ + this->initTable(); +} + + +template +inline +RootNode::RootNode(const ValueType& background): mBackground(background) +{ + this->initTable(); +} + + +template +template +inline +RootNode::RootNode(const RootNode& other, + const ValueType& backgd, const ValueType& foregd, TopologyCopy): + mBackground(backgd) +{ + using OtherRootT = RootNode; + + enforceSameConfiguration(other); + + const Tile bgTile(backgd, /*active=*/false), fgTile(foregd, true); + this->initTable(); + + for (typename OtherRootT::MapCIter i=other.mTable.begin(), e=other.mTable.end(); i != e; ++i) { + mTable[i->first] = OtherRootT::isTile(i) + ? NodeStruct(OtherRootT::isTileOn(i) ? fgTile : bgTile) + : NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, foregd, TopologyCopy()))); + } +} + + +template +template +inline +RootNode::RootNode(const RootNode& other, + const ValueType& backgd, TopologyCopy): + mBackground(backgd) +{ + using OtherRootT = RootNode; + + enforceSameConfiguration(other); + + const Tile bgTile(backgd, /*active=*/false), fgTile(backgd, true); + this->initTable(); + for (typename OtherRootT::MapCIter i=other.mTable.begin(), e=other.mTable.end(); i != e; ++i) { + mTable[i->first] = OtherRootT::isTile(i) + ? NodeStruct(OtherRootT::isTileOn(i) ? fgTile : bgTile) + : NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, TopologyCopy()))); + } +} + + +//////////////////////////////////////// + + +// This helper class is a friend of RootNode and is needed so that assignment +// with value conversion can be specialized for compatible and incompatible +// pairs of RootNode types. +template +struct RootNodeCopyHelper +{ + static inline void copyWithValueConversion(RootT& self, const OtherRootT& other) + { + // If the two root nodes have different configurations or incompatible ValueTypes, + // throw an exception. + self.enforceSameConfiguration(other); + self.enforceCompatibleValueTypes(other); + // One of the above two tests should throw, so we should never get here: + std::ostringstream ostr; + ostr << "cannot convert a " << typeid(OtherRootT).name() + << " to a " << typeid(RootT).name(); + OPENVDB_THROW(TypeError, ostr.str()); + } +}; + +// Specialization for root nodes of compatible types +template +struct RootNodeCopyHelper +{ + static inline void copyWithValueConversion(RootT& self, const OtherRootT& other) + { + using ValueT = typename RootT::ValueType; + using ChildT = typename RootT::ChildNodeType; + using NodeStruct = typename RootT::NodeStruct; + using Tile = typename RootT::Tile; + using OtherValueT = typename OtherRootT::ValueType; + using OtherMapCIter = typename OtherRootT::MapCIter; + using OtherTile = typename OtherRootT::Tile; + + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline ValueT convertValue(const OtherValueT& val) { return ValueT(val); } + }; + + self.mBackground = Local::convertValue(other.mBackground); + + self.clear(); + self.initTable(); + + for (OtherMapCIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + if (other.isTile(i)) { + // Copy the other node's tile, but convert its value to this node's ValueType. + const OtherTile& otherTile = other.getTile(i); + self.mTable[i->first] = NodeStruct( + Tile(Local::convertValue(otherTile.value), otherTile.active)); + } else { + // Copy the other node's child, but convert its values to this node's ValueType. + self.mTable[i->first] = NodeStruct(*(new ChildT(other.getChild(i)))); + } + } + } +}; + + +// Overload for root nodes of the same type as this node +template +inline RootNode& +RootNode::operator=(const RootNode& other) +{ + if (&other != this) { + mBackground = other.mBackground; + + this->clear(); + this->initTable(); + + for (MapCIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + mTable[i->first] = + isTile(i) ? NodeStruct(getTile(i)) : NodeStruct(*(new ChildT(getChild(i)))); + } + } + return *this; +} + +// Overload for root nodes of different types +template +template +inline RootNode& +RootNode::operator=(const RootNode& other) +{ + using OtherRootT = RootNode; + using OtherValueT = typename OtherRootT::ValueType; + static const bool compatible = (SameConfiguration::value + && CanConvertType::value); + RootNodeCopyHelper::copyWithValueConversion(*this, other); + return *this; +} + + +//////////////////////////////////////// + +template +inline void +RootNode::setBackground(const ValueType& background, bool updateChildNodes) +{ + if (math::isExactlyEqual(background, mBackground)) return; + + if (updateChildNodes) { + // Traverse the tree, replacing occurrences of mBackground with background + // and -mBackground with -background. + for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + ChildT *child = iter->second.child; + if (child) { + child->resetBackground(/*old=*/mBackground, /*new=*/background); + } else { + Tile& tile = getTile(iter); + if (tile.active) continue;//only change inactive tiles + if (math::isApproxEqual(tile.value, mBackground)) { + tile.value = background; + } else if (math::isApproxEqual(tile.value, math::negative(mBackground))) { + tile.value = math::negative(background); + } + } + } + } + mBackground = background; +} + +template +inline bool +RootNode::isBackgroundTile(const Tile& tile) const +{ + return !tile.active && math::isApproxEqual(tile.value, mBackground); +} + +template +inline bool +RootNode::isBackgroundTile(const MapIter& iter) const +{ + return isTileOff(iter) && math::isApproxEqual(getTile(iter).value, mBackground); +} + +template +inline bool +RootNode::isBackgroundTile(const MapCIter& iter) const +{ + return isTileOff(iter) && math::isApproxEqual(getTile(iter).value, mBackground); +} + + +template +inline size_t +RootNode::numBackgroundTiles() const +{ + size_t count = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (this->isBackgroundTile(i)) ++count; + } + return count; +} + + +template +inline size_t +RootNode::eraseBackgroundTiles() +{ + std::set keysToErase; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (this->isBackgroundTile(i)) keysToErase.insert(i->first); + } + for (std::set::iterator i = keysToErase.begin(), e = keysToErase.end(); i != e; ++i) { + mTable.erase(*i); + } + return keysToErase.size(); +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::insertKeys(CoordSet& keys) const +{ + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + keys.insert(i->first); + } +} + + +template +inline typename RootNode::MapIter +RootNode::findOrAddCoord(const Coord& xyz) +{ + const Coord key = coordToKey(xyz); + std::pair result = mTable.insert( + typename MapType::value_type(key, NodeStruct(Tile(mBackground, /*active=*/false)))); + return result.first; +} + + +template +inline bool +RootNode::expand(const Coord& xyz) +{ + const Coord key = coordToKey(xyz); + std::pair result = mTable.insert( + typename MapType::value_type(key, NodeStruct(Tile(mBackground, /*active=*/false)))); + return result.second; // return true if the key did not already exist +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::getNodeLog2Dims(std::vector& dims) +{ + dims.push_back(0); // magic number; RootNode has no Log2Dim + ChildT::getNodeLog2Dims(dims); +} + + +template +inline Coord +RootNode::getMinIndex() const +{ + return mTable.empty() ? Coord(0) : mTable.begin()->first; +} + +template +inline Coord +RootNode::getMaxIndex() const +{ + return mTable.empty() ? Coord(0) : mTable.rbegin()->first + Coord(ChildT::DIM - 1); +} + + +template +inline void +RootNode::getIndexRange(CoordBBox& bbox) const +{ + bbox.min() = this->getMinIndex(); + bbox.max() = this->getMaxIndex(); +} + + +//////////////////////////////////////// + + +template +template +inline bool +RootNode::hasSameTopology(const RootNode& other) const +{ + using OtherRootT = RootNode; + using OtherMapT = typename OtherRootT::MapType; + using OtherIterT = typename OtherRootT::MapIter; + using OtherCIterT = typename OtherRootT::MapCIter; + + if (!hasSameConfiguration(other)) return false; + + // Create a local copy of the other node's table. + OtherMapT copyOfOtherTable = other.mTable; + + // For each entry in this node's table... + for (MapCIter thisIter = mTable.begin(); thisIter != mTable.end(); ++thisIter) { + if (this->isBackgroundTile(thisIter)) continue; // ignore background tiles + + // Fail if there is no corresponding entry in the other node's table. + OtherCIterT otherIter = other.findKey(thisIter->first); + if (otherIter == other.mTable.end()) return false; + + // Fail if this entry is a tile and the other is a child or vice-versa. + if (isChild(thisIter)) {//thisIter points to a child + if (OtherRootT::isTile(otherIter)) return false; + // Fail if both entries are children, but the children have different topology. + if (!getChild(thisIter).hasSameTopology(&OtherRootT::getChild(otherIter))) return false; + } else {//thisIter points to a tile + if (OtherRootT::isChild(otherIter)) return false; + if (getTile(thisIter).active != OtherRootT::getTile(otherIter).active) return false; + } + + // Remove tiles and child nodes with matching topology from + // the copy of the other node's table. This is required since + // the two root tables can include an arbitrary number of + // background tiles and still have the same topology! + copyOfOtherTable.erase(otherIter->first); + } + // Fail if the remaining entries in copyOfOtherTable are not all background tiles. + for (OtherIterT i = copyOfOtherTable.begin(), e = copyOfOtherTable.end(); i != e; ++i) { + if (!other.isBackgroundTile(i)) return false; + } + return true; +} + + +template +template +inline bool +RootNode::hasSameConfiguration(const RootNode&) +{ + std::vector thisDims, otherDims; + RootNode::getNodeLog2Dims(thisDims); + RootNode::getNodeLog2Dims(otherDims); + return (thisDims == otherDims); +} + + +template +template +inline void +RootNode::enforceSameConfiguration(const RootNode&) +{ + std::vector thisDims, otherDims; + RootNode::getNodeLog2Dims(thisDims); + RootNode::getNodeLog2Dims(otherDims); + if (thisDims != otherDims) { + std::ostringstream ostr; + ostr << "grids have incompatible configurations (" << thisDims[0]; + for (size_t i = 1, N = thisDims.size(); i < N; ++i) ostr << " x " << thisDims[i]; + ostr << " vs. " << otherDims[0]; + for (size_t i = 1, N = otherDims.size(); i < N; ++i) ostr << " x " << otherDims[i]; + ostr << ")"; + OPENVDB_THROW(TypeError, ostr.str()); + } +} + + +template +template +inline bool +RootNode::hasCompatibleValueType(const RootNode&) +{ + using OtherValueType = typename OtherChildType::ValueType; + return CanConvertType::value; +} + + +template +template +inline void +RootNode::enforceCompatibleValueTypes(const RootNode&) +{ + using OtherValueType = typename OtherChildType::ValueType; + if (!CanConvertType::value) { + std::ostringstream ostr; + ostr << "values of type " << typeNameAsString() + << " cannot be converted to type " << typeNameAsString(); + OPENVDB_THROW(TypeError, ostr.str()); + } +} + + +//////////////////////////////////////// + + +template +inline Index64 +RootNode::memUsage() const +{ + Index64 sum = sizeof(*this); + for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (const ChildT *child = iter->second.child) { + sum += child->memUsage(); + } + } + return sum; +} + + +template +inline void +RootNode::clear() +{ + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + delete i->second.child; + } + mTable.clear(); +} + + +template +inline void +RootNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (const ChildT *child = iter->second.child) { + child->evalActiveBoundingBox(bbox, visitVoxels); + } else if (isTileOn(iter)) { + bbox.expand(iter->first, ChildT::DIM); + } + } +} + + +template +inline Index +RootNode::getChildCount() const { + Index sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) ++sum; + } + return sum; +} + + +template +inline Index +RootNode::getTileCount() const +{ + Index sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isTile(i)) ++sum; + } + return sum; +} + + +template +inline Index +RootNode::getActiveTileCount() const +{ + Index sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isTileOn(i)) ++sum; + } + return sum; +} + + +template +inline Index +RootNode::getInactiveTileCount() const +{ + Index sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isTileOff(i)) ++sum; + } + return sum; +} + + +template +inline Index32 +RootNode::leafCount() const +{ + Index32 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) sum += getChild(i).leafCount(); + } + return sum; +} + + +template +inline Index32 +RootNode::nonLeafCount() const +{ + Index32 sum = 1; + if (ChildT::LEVEL != 0) { + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) sum += getChild(i).nonLeafCount(); + } + } + return sum; +} + + +template +inline Index64 +RootNode::onVoxelCount() const +{ + Index64 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) { + sum += getChild(i).onVoxelCount(); + } else if (isTileOn(i)) { + sum += ChildT::NUM_VOXELS; + } + } + return sum; +} + + +template +inline Index64 +RootNode::offVoxelCount() const +{ + Index64 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) { + sum += getChild(i).offVoxelCount(); + } else if (isTileOff(i) && !this->isBackgroundTile(i)) { + sum += ChildT::NUM_VOXELS; + } + } + return sum; +} + + +template +inline Index64 +RootNode::onLeafVoxelCount() const +{ + Index64 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) sum += getChild(i).onLeafVoxelCount(); + } + return sum; +} + + +template +inline Index64 +RootNode::offLeafVoxelCount() const +{ + Index64 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) sum += getChild(i).offLeafVoxelCount(); + } + return sum; +} + +template +inline Index64 +RootNode::onTileCount() const +{ + Index64 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) { + sum += getChild(i).onTileCount(); + } else if (isTileOn(i)) { + sum += 1; + } + } + return sum; +} + +template +inline void +RootNode::nodeCount(std::vector &vec) const +{ + assert(vec.size() > LEVEL); + Index32 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) { + ++sum; + getChild(i).nodeCount(vec); + } + } + vec[LEVEL] = 1;// one root node + vec[ChildNodeType::LEVEL] = sum; +} + +//////////////////////////////////////// + + +template +inline bool +RootNode::isValueOn(const Coord& xyz) const +{ + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTileOff(iter)) return false; + return isTileOn(iter) ? true : getChild(iter).isValueOn(xyz); +} + +template +inline bool +RootNode::hasActiveTiles() const +{ + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i) ? getChild(i).hasActiveTiles() : getTile(i).active) return true; + } + return false; +} + +template +template +inline bool +RootNode::isValueOnAndCache(const Coord& xyz, AccessorT& acc) const +{ + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTileOff(iter)) return false; + if (isTileOn(iter)) return true; + acc.insert(xyz, &getChild(iter)); + return getChild(iter).isValueOnAndCache(xyz, acc); +} + + +template +inline const typename ChildT::ValueType& +RootNode::getValue(const Coord& xyz) const +{ + MapCIter iter = this->findCoord(xyz); + return iter == mTable.end() ? mBackground + : (isTile(iter) ? getTile(iter).value : getChild(iter).getValue(xyz)); +} + +template +template +inline const typename ChildT::ValueType& +RootNode::getValueAndCache(const Coord& xyz, AccessorT& acc) const +{ + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end()) return mBackground; + if (isChild(iter)) { + acc.insert(xyz, &getChild(iter)); + return getChild(iter).getValueAndCache(xyz, acc); + } + return getTile(iter).value; +} + + +template +inline int +RootNode::getValueDepth(const Coord& xyz) const +{ + MapCIter iter = this->findCoord(xyz); + return iter == mTable.end() ? -1 + : (isTile(iter) ? 0 : int(LEVEL) - int(getChild(iter).getValueLevel(xyz))); +} + +template +template +inline int +RootNode::getValueDepthAndCache(const Coord& xyz, AccessorT& acc) const +{ + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end()) return -1; + if (isTile(iter)) return 0; + acc.insert(xyz, &getChild(iter)); + return int(LEVEL) - int(getChild(iter).getValueLevelAndCache(xyz, acc)); +} + + +template +inline void +RootNode::setValueOff(const Coord& xyz) +{ + MapIter iter = this->findCoord(xyz); + if (iter != mTable.end() && !isTileOff(iter)) { + if (isTileOn(iter)) { + setChild(iter, *new ChildT(xyz, getTile(iter).value, /*active=*/true)); + } + getChild(iter).setValueOff(xyz); + } +} + + +template +inline void +RootNode::setActiveState(const Coord& xyz, bool on) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + if (on) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else { + // Nothing to do; (x, y, z) is background and therefore already inactive. + } + } else if (isChild(iter)) { + child = &getChild(iter); + } else if (on != getTile(iter).active) { + child = new ChildT(xyz, getTile(iter).value, !on); + setChild(iter, *child); + } + if (child) child->setActiveState(xyz, on); +} + +template +template +inline void +RootNode::setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& acc) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + if (on) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else { + // Nothing to do; (x, y, z) is background and therefore already inactive. + } + } else if (isChild(iter)) { + child = &getChild(iter); + } else if (on != getTile(iter).active) { + child = new ChildT(xyz, getTile(iter).value, !on); + setChild(iter, *child); + } + if (child) { + acc.insert(xyz, child); + child->setActiveStateAndCache(xyz, on, acc); + } +} + + +template +inline void +RootNode::setValueOff(const Coord& xyz, const ValueType& value) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + if (!math::isExactlyEqual(mBackground, value)) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } + } else if (isChild(iter)) { + child = &getChild(iter); + } else if (isTileOn(iter) || !math::isExactlyEqual(getTile(iter).value, value)) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + if (child) child->setValueOff(xyz, value); +} + +template +template +inline void +RootNode::setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + if (!math::isExactlyEqual(mBackground, value)) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } + } else if (isChild(iter)) { + child = &getChild(iter); + } else if (isTileOn(iter) || !math::isExactlyEqual(getTile(iter).value, value)) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + if (child) { + acc.insert(xyz, child); + child->setValueOffAndCache(xyz, value, acc); + } +} + + +template +inline void +RootNode::setValueOn(const Coord& xyz, const ValueType& value) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else if (isTileOff(iter) || !math::isExactlyEqual(getTile(iter).value, value)) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + if (child) child->setValueOn(xyz, value); +} + +template +template +inline void +RootNode::setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else if (isTileOff(iter) || !math::isExactlyEqual(getTile(iter).value, value)) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + if (child) { + acc.insert(xyz, child); + child->setValueAndCache(xyz, value, acc); + } +} + + +template +inline void +RootNode::setValueOnly(const Coord& xyz, const ValueType& value) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else if (!math::isExactlyEqual(getTile(iter).value, value)) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + if (child) child->setValueOnly(xyz, value); +} + +template +template +inline void +RootNode::setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else if (!math::isExactlyEqual(getTile(iter).value, value)) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + if (child) { + acc.insert(xyz, child); + child->setValueOnlyAndCache(xyz, value, acc); + } +} + + +template +template +inline void +RootNode::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + bool createChild = isTileOff(iter); + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = getTile(iter).value; + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + } + if (child) child->modifyValue(xyz, op); +} + +template +template +inline void +RootNode::modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT& acc) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + bool createChild = isTileOff(iter); + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = getTile(iter).value; + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + } + if (child) { + acc.insert(xyz, child); + child->modifyValueAndCache(xyz, op, acc); + } +} + + +template +template +inline void +RootNode::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else { + const Tile& tile = getTile(iter); + bool modifiedState = tile.active; + ValueType modifiedVal = tile.value; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tile.active || !math::isExactlyEqual(modifiedVal, tile.value)) { + child = new ChildT(xyz, tile.value, tile.active); + setChild(iter, *child); + } + } + if (child) child->modifyValueAndActiveState(xyz, op); +} + +template +template +inline void +RootNode::modifyValueAndActiveStateAndCache( + const Coord& xyz, const ModifyOp& op, AccessorT& acc) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else { + const Tile& tile = getTile(iter); + bool modifiedState = tile.active; + ValueType modifiedVal = tile.value; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tile.active || !math::isExactlyEqual(modifiedVal, tile.value)) { + child = new ChildT(xyz, tile.value, tile.active); + setChild(iter, *child); + } + } + if (child) { + acc.insert(xyz, child); + child->modifyValueAndActiveStateAndCache(xyz, op, acc); + } +} + + +template +inline bool +RootNode::probeValue(const Coord& xyz, ValueType& value) const +{ + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + value = mBackground; + return false; + } else if (isChild(iter)) { + return getChild(iter).probeValue(xyz, value); + } + value = getTile(iter).value; + return isTileOn(iter); +} + +template +template +inline bool +RootNode::probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT& acc) const +{ + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + value = mBackground; + return false; + } else if (isChild(iter)) { + acc.insert(xyz, &getChild(iter)); + return getChild(iter).probeValueAndCache(xyz, value, acc); + } + value = getTile(iter).value; + return isTileOn(iter); +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::fill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + if (bbox.empty()) return; + + // Iterate over the fill region in axis-aligned, tile-sized chunks. + // (The first and last chunks along each axis might be smaller than a tile.) + Coord xyz, tileMax; + for (int x = bbox.min().x(); x <= bbox.max().x(); x = tileMax.x() + 1) { + xyz.setX(x); + for (int y = bbox.min().y(); y <= bbox.max().y(); y = tileMax.y() + 1) { + xyz.setY(y); + for (int z = bbox.min().z(); z <= bbox.max().z(); z = tileMax.z() + 1) { + xyz.setZ(z); + + // Get the bounds of the tile that contains voxel (x, y, z). + Coord tileMin = coordToKey(xyz); + tileMax = tileMin.offsetBy(ChildT::DIM - 1); + + if (xyz != tileMin || Coord::lessThan(bbox.max(), tileMax)) { + // If the box defined by (xyz, bbox.max()) doesn't completely enclose + // the tile to which xyz belongs, create a child node (or retrieve + // the existing one). + ChildT* child = nullptr; + MapIter iter = this->findKey(tileMin); + if (iter == mTable.end()) { + // No child or tile exists. Create a child and initialize it + // with the background value. + child = new ChildT(xyz, mBackground); + mTable[tileMin] = NodeStruct(*child); + } else if (isTile(iter)) { + // Replace the tile with a newly-created child that is filled + // with the tile's value and active state. + const Tile& tile = getTile(iter); + child = new ChildT(xyz, tile.value, tile.active); + mTable[tileMin] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } + // Forward the fill request to the child. + if (child) { + const Coord tmp = Coord::minComponent(bbox.max(), tileMax); + child->fill(CoordBBox(xyz, tmp), value, active); + } + } else { + // If the box given by (xyz, bbox.max()) completely encloses + // the tile to which xyz belongs, create the tile (if it + // doesn't already exist) and give it the fill value. + MapIter iter = this->findOrAddCoord(tileMin); + setTile(iter, Tile(value, active)); + } + } + } + } +} + + +template +inline void +RootNode::denseFill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + if (bbox.empty()) return; + + if (active && mTable.empty()) { + // If this tree is empty, then a sparse fill followed by (threaded) + // densification of active tiles is the more efficient approach. + sparseFill(bbox, value, active); + voxelizeActiveTiles(/*threaded=*/true); + return; + } + + // Iterate over the fill region in axis-aligned, tile-sized chunks. + // (The first and last chunks along each axis might be smaller than a tile.) + Coord xyz, tileMin, tileMax; + for (int x = bbox.min().x(); x <= bbox.max().x(); x = tileMax.x() + 1) { + xyz.setX(x); + for (int y = bbox.min().y(); y <= bbox.max().y(); y = tileMax.y() + 1) { + xyz.setY(y); + for (int z = bbox.min().z(); z <= bbox.max().z(); z = tileMax.z() + 1) { + xyz.setZ(z); + + // Get the bounds of the tile that contains voxel (x, y, z). + tileMin = coordToKey(xyz); + tileMax = tileMin.offsetBy(ChildT::DIM - 1); + + // Retrieve the table entry for the tile that contains xyz, + // or, if there is no table entry, add a background tile. + const auto iter = findOrAddCoord(tileMin); + + if (isTile(iter)) { + // If the table entry is a tile, replace it with a child node + // that is filled with the tile's value and active state. + const auto& tile = getTile(iter); + auto* child = new ChildT{tileMin, tile.value, tile.active}; + setChild(iter, *child); + } + // Forward the fill request to the child. + getChild(iter).denseFill(bbox, value, active); + } + } + } +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::voxelizeActiveTiles(bool threaded) +{ + // There is little point in threading over the root table since each tile + // spans a huge index space (by default 4096^3) and hence we expect few + // active tiles if any at all. In fact, you're very likely to run out of + // memory if this method is called on a tree with root-level active tiles! + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (this->isTileOff(i)) continue; + ChildT* child = i->second.child; + if (child == nullptr) { + // If this table entry is an active tile (i.e., not off and not a child node), + // replace it with a child node filled with active tiles of the same value. + child = new ChildT{i->first, this->getTile(i).value, true}; + i->second.child = child; + } + child->voxelizeActiveTiles(threaded); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +RootNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ + using DenseValueType = typename DenseT::ValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + CoordBBox nodeBBox; + for (Coord xyz = bbox.min(); xyz[0] <= bbox.max()[0]; xyz[0] = nodeBBox.max()[0] + 1) { + for (xyz[1] = bbox.min()[1]; xyz[1] <= bbox.max()[1]; xyz[1] = nodeBBox.max()[1] + 1) { + for (xyz[2] = bbox.min()[2]; xyz[2] <= bbox.max()[2]; xyz[2] = nodeBBox.max()[2] + 1) { + + // Get the coordinate bbox of the child node that contains voxel xyz. + nodeBBox = CoordBBox::createCube(coordToKey(xyz), ChildT::DIM); + + // Get the coordinate bbox of the interection of inBBox and nodeBBox + CoordBBox sub(xyz, Coord::minComponent(bbox.max(), nodeBBox.max())); + + MapCIter iter = this->findKey(nodeBBox.min()); + if (iter != mTable.end() && isChild(iter)) {//is a child + getChild(iter).copyToDense(sub, dense); + } else {//is background or a tile value + const ValueType value = iter==mTable.end() ? mBackground : getTile(iter).value; + sub.translate(-min); + DenseValueType* a0 = dense.data() + zStride*sub.min()[2]; + for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x +inline bool +RootNode::writeTopology(std::ostream& os, bool toHalf) const +{ + if (!toHalf) { + os.write(reinterpret_cast(&mBackground), sizeof(ValueType)); + } else { + ValueType truncatedVal = io::truncateRealToHalf(mBackground); + os.write(reinterpret_cast(&truncatedVal), sizeof(ValueType)); + } + io::setGridBackgroundValuePtr(os, &mBackground); + + const Index numTiles = this->getTileCount(), numChildren = this->getChildCount(); + os.write(reinterpret_cast(&numTiles), sizeof(Index)); + os.write(reinterpret_cast(&numChildren), sizeof(Index)); + + if (numTiles == 0 && numChildren == 0) return false; + + // Write tiles. + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) continue; + os.write(reinterpret_cast(i->first.asPointer()), 3 * sizeof(Int32)); + os.write(reinterpret_cast(&getTile(i).value), sizeof(ValueType)); + os.write(reinterpret_cast(&getTile(i).active), sizeof(bool)); + } + // Write child nodes. + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isTile(i)) continue; + os.write(reinterpret_cast(i->first.asPointer()), 3 * sizeof(Int32)); + getChild(i).writeTopology(os, toHalf); + } + + return true; // not empty +} + + +template +inline bool +RootNode::readTopology(std::istream& is, bool fromHalf) +{ + // Delete the existing tree. + this->clear(); + + if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_ROOTNODE_MAP) { + // Read and convert an older-format RootNode. + + // For backward compatibility with older file formats, read both + // outside and inside background values. + is.read(reinterpret_cast(&mBackground), sizeof(ValueType)); + ValueType inside; + is.read(reinterpret_cast(&inside), sizeof(ValueType)); + + io::setGridBackgroundValuePtr(is, &mBackground); + + // Read the index range. + Coord rangeMin, rangeMax; + is.read(reinterpret_cast(rangeMin.asPointer()), 3 * sizeof(Int32)); + is.read(reinterpret_cast(rangeMax.asPointer()), 3 * sizeof(Int32)); + + this->initTable(); + Index tableSize = 0, log2Dim[4] = { 0, 0, 0, 0 }; + Int32 offset[3]; + for (int i = 0; i < 3; ++i) { + offset[i] = rangeMin[i] >> ChildT::TOTAL; + rangeMin[i] = offset[i] << ChildT::TOTAL; + log2Dim[i] = 1 + util::FindHighestOn((rangeMax[i] >> ChildT::TOTAL) - offset[i]); + tableSize += log2Dim[i]; + rangeMax[i] = (((1 << log2Dim[i]) + offset[i]) << ChildT::TOTAL) - 1; + } + log2Dim[3] = log2Dim[1] + log2Dim[2]; + tableSize = 1U << tableSize; + + // Read masks. + util::RootNodeMask childMask(tableSize), valueMask(tableSize); + childMask.load(is); + valueMask.load(is); + + // Read child nodes/values. + for (Index i = 0; i < tableSize; ++i) { + // Compute origin = offset2coord(i). + Index n = i; + Coord origin; + origin[0] = (n >> log2Dim[3]) + offset[0]; + n &= (1U << log2Dim[3]) - 1; + origin[1] = (n >> log2Dim[2]) + offset[1]; + origin[2] = (n & ((1U << log2Dim[2]) - 1)) + offset[1]; + origin <<= ChildT::TOTAL; + + if (childMask.isOn(i)) { + // Read in and insert a child node. + ChildT* child = new ChildT(PartialCreate(), origin, mBackground); + child->readTopology(is); + mTable[origin] = NodeStruct(*child); + } else { + // Read in a tile value and insert a tile, but only if the value + // is either active or non-background. + ValueType value; + is.read(reinterpret_cast(&value), sizeof(ValueType)); + if (valueMask.isOn(i) || (!math::isApproxEqual(value, mBackground))) { + mTable[origin] = NodeStruct(Tile(value, valueMask.isOn(i))); + } + } + } + return true; + } + + // Read a RootNode that was stored in the current format. + + is.read(reinterpret_cast(&mBackground), sizeof(ValueType)); + io::setGridBackgroundValuePtr(is, &mBackground); + + Index numTiles = 0, numChildren = 0; + is.read(reinterpret_cast(&numTiles), sizeof(Index)); + is.read(reinterpret_cast(&numChildren), sizeof(Index)); + + if (numTiles == 0 && numChildren == 0) return false; + + Int32 vec[3]; + ValueType value; + bool active; + + // Read tiles. + for (Index n = 0; n < numTiles; ++n) { + is.read(reinterpret_cast(vec), 3 * sizeof(Int32)); + is.read(reinterpret_cast(&value), sizeof(ValueType)); + is.read(reinterpret_cast(&active), sizeof(bool)); + mTable[Coord(vec)] = NodeStruct(Tile(value, active)); + } + + // Read child nodes. + for (Index n = 0; n < numChildren; ++n) { + is.read(reinterpret_cast(vec), 3 * sizeof(Int32)); + Coord origin(vec); + ChildT* child = new ChildT(PartialCreate(), origin, mBackground); + child->readTopology(is, fromHalf); + mTable[Coord(vec)] = NodeStruct(*child); + } + + return true; // not empty +} + + +template +inline void +RootNode::writeBuffers(std::ostream& os, bool toHalf) const +{ + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) getChild(i).writeBuffers(os, toHalf); + } +} + + +template +inline void +RootNode::readBuffers(std::istream& is, bool fromHalf) +{ + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) getChild(i).readBuffers(is, fromHalf); + } +} + + +template +inline void +RootNode::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf) +{ + const Tile bgTile(mBackground, /*active=*/false); + + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) { + // Stream in and clip the branch rooted at this child. + // (We can't skip over children that lie outside the clipping region, + // because buffers are serialized in depth-first order and need to be + // unserialized in the same order.) + ChildT& child = getChild(i); + child.readBuffers(is, clipBBox, fromHalf); + } + } + // Clip root-level tiles and prune children that were clipped. + this->clip(clipBBox); +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::clip(const CoordBBox& clipBBox) +{ + const Tile bgTile(mBackground, /*active=*/false); + + // Iterate over a copy of this node's table so that we can modify the original. + // (Copying the table copies child node pointers, not the nodes themselves.) + MapType copyOfTable(mTable); + for (MapIter i = copyOfTable.begin(), e = copyOfTable.end(); i != e; ++i) { + const Coord& xyz = i->first; // tile or child origin + CoordBBox tileBBox(xyz, xyz.offsetBy(ChildT::DIM - 1)); // tile or child bounds + if (!clipBBox.hasOverlap(tileBBox)) { + // This table entry lies completely outside the clipping region. Delete it. + setTile(this->findCoord(xyz), bgTile); // delete any existing child node first + mTable.erase(xyz); + } else if (!clipBBox.isInside(tileBBox)) { + // This table entry does not lie completely inside the clipping region + // and must be clipped. + if (isChild(i)) { + getChild(i).clip(clipBBox, mBackground); + } else { + // Replace this tile with a background tile, then fill the clip region + // with the tile's original value. (This might create a child branch.) + tileBBox.intersect(clipBBox); + const Tile& origTile = getTile(i); + setTile(this->findCoord(xyz), bgTile); + this->sparseFill(tileBBox, origTile.value, origTile.active); + } + } else { + // This table entry lies completely inside the clipping region. Leave it intact. + } + } + this->prune(); // also erases root-level background tiles +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::prune(const ValueType& tolerance) +{ + bool state = false; + ValueType value = zeroVal(); + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (this->isTile(i)) continue; + this->getChild(i).prune(tolerance); + if (this->getChild(i).isConstant(value, state, tolerance)) { + this->setTile(i, Tile(value, state)); + } + } + this->eraseBackgroundTiles(); +} + + +//////////////////////////////////////// + + +template +template +inline NodeT* +RootNode::stealNode(const Coord& xyz, const ValueType& value, bool state) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return nullptr; + return (std::is_same::value) + ? reinterpret_cast(&stealChild(iter, Tile(value, state))) + : getChild(iter).template stealNode(xyz, value, state); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::addLeaf(LeafNodeType* leaf) +{ + if (leaf == nullptr) return; + ChildT* child = nullptr; + const Coord& xyz = leaf->origin(); + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + if (ChildT::LEVEL>0) { + child = new ChildT(xyz, mBackground, false); + } else { + child = reinterpret_cast(leaf); + } + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + if (ChildT::LEVEL>0) { + child = &getChild(iter); + } else { + child = reinterpret_cast(leaf); + setChild(iter, *child);//this also deletes the existing child node + } + } else {//tile + if (ChildT::LEVEL>0) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + } else { + child = reinterpret_cast(leaf); + } + setChild(iter, *child); + } + child->addLeaf(leaf); +} + + +template +template +inline void +RootNode::addLeafAndCache(LeafNodeType* leaf, AccessorT& acc) +{ + if (leaf == nullptr) return; + ChildT* child = nullptr; + const Coord& xyz = leaf->origin(); + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + if (ChildT::LEVEL>0) { + child = new ChildT(xyz, mBackground, false); + } else { + child = reinterpret_cast(leaf); + } + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + if (ChildT::LEVEL>0) { + child = &getChild(iter); + } else { + child = reinterpret_cast(leaf); + setChild(iter, *child);//this also deletes the existing child node + } + } else {//tile + if (ChildT::LEVEL>0) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + } else { + child = reinterpret_cast(leaf); + } + setChild(iter, *child); + } + acc.insert(xyz, child); + child->addLeafAndCache(leaf, acc); +} + +template +inline bool +RootNode::addChild(ChildT* child) +{ + if (!child) return false; + const Coord& xyz = child->origin(); + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) {//background + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else {//child or tile + setChild(iter, *child);//this also deletes the existing child node + } + return true; +} + +template +inline void +RootNode::addTile(const Coord& xyz, const ValueType& value, bool state) +{ + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) {//background + mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state)); + } else {//child or tile + setTile(iter, Tile(value, state));//this also deletes the existing child node + } +} + +template +inline void +RootNode::addTile(Index level, const Coord& xyz, + const ValueType& value, bool state) +{ + if (LEVEL >= level) { + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) {//background + if (LEVEL > level) { + ChildT* child = new ChildT(xyz, mBackground, false); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + child->addTile(level, xyz, value, state); + } else { + mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state)); + } + } else if (isChild(iter)) {//child + if (LEVEL > level) { + getChild(iter).addTile(level, xyz, value, state); + } else { + setTile(iter, Tile(value, state));//this also deletes the existing child node + } + } else {//tile + if (LEVEL > level) { + ChildT* child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + child->addTile(level, xyz, value, state); + } else { + setTile(iter, Tile(value, state)); + } + } + } +} + + +template +template +inline void +RootNode::addTileAndCache(Index level, const Coord& xyz, const ValueType& value, + bool state, AccessorT& acc) +{ + if (LEVEL >= level) { + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) {//background + if (LEVEL > level) { + ChildT* child = new ChildT(xyz, mBackground, false); + acc.insert(xyz, child); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + child->addTileAndCache(level, xyz, value, state, acc); + } else { + mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state)); + } + } else if (isChild(iter)) {//child + if (LEVEL > level) { + ChildT* child = &getChild(iter); + acc.insert(xyz, child); + child->addTileAndCache(level, xyz, value, state, acc); + } else { + setTile(iter, Tile(value, state));//this also deletes the existing child node + } + } else {//tile + if (LEVEL > level) { + ChildT* child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + acc.insert(xyz, child); + setChild(iter, *child); + child->addTileAndCache(level, xyz, value, state, acc); + } else { + setTile(iter, Tile(value, state)); + } + } + } +} + + +//////////////////////////////////////// + + +template +inline typename ChildT::LeafNodeType* +RootNode::touchLeaf(const Coord& xyz) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground, false); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + return child->touchLeaf(xyz); +} + + +template +template +inline typename ChildT::LeafNodeType* +RootNode::touchLeafAndCache(const Coord& xyz, AccessorT& acc) +{ + ChildT* child = nullptr; + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = new ChildT(xyz, mBackground, false); + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + child = &getChild(iter); + } else { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + acc.insert(xyz, child); + return child->touchLeafAndCache(xyz, acc); +} + + +//////////////////////////////////////// + + +template +template +inline NodeT* +RootNode::probeNode(const Coord& xyz) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return nullptr; + ChildT* child = &getChild(iter); + return (std::is_same::value) + ? reinterpret_cast(child) + : child->template probeNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline const NodeT* +RootNode::probeConstNode(const Coord& xyz) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return nullptr; + const ChildT* child = &getChild(iter); + return (std::is_same::value) + ? reinterpret_cast(child) + : child->template probeConstNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +inline typename ChildT::LeafNodeType* +RootNode::probeLeaf(const Coord& xyz) +{ + return this->template probeNode(xyz); +} + + +template +inline const typename ChildT::LeafNodeType* +RootNode::probeConstLeaf(const Coord& xyz) const +{ + return this->template probeConstNode(xyz); +} + + +template +template +inline typename ChildT::LeafNodeType* +RootNode::probeLeafAndCache(const Coord& xyz, AccessorT& acc) +{ + return this->template probeNodeAndCache(xyz, acc); +} + + +template +template +inline const typename ChildT::LeafNodeType* +RootNode::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->template probeConstNodeAndCache(xyz, acc); +} + + +template +template +inline const typename ChildT::LeafNodeType* +RootNode::probeLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->probeConstLeafAndCache(xyz, acc); +} + + +template +template +inline NodeT* +RootNode::probeNodeAndCache(const Coord& xyz, AccessorT& acc) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return nullptr; + ChildT* child = &getChild(iter); + acc.insert(xyz, child); + return (std::is_same::value) + ? reinterpret_cast(child) + : child->template probeNodeAndCache(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline const NodeT* +RootNode::probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(std::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return nullptr; + const ChildT* child = &getChild(iter); + acc.insert(xyz, child); + return (std::is_same::value) + ? reinterpret_cast(child) + : child->template probeConstNodeAndCache(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + +template +template +inline void +RootNode::getNodes(ArrayT& array) +{ + using NodePtr = typename ArrayT::value_type; + static_assert(std::is_pointer::value, + "argument to getNodes() must be a pointer array"); + using NodeType = typename std::remove_pointer::type; + using NonConstNodeType = typename std::remove_const::type; + using result = typename boost::mpl::contains::type; + static_assert(result::value, "can't extract non-const nodes from a const tree"); + using ArrayChildT = typename std::conditional< + std::is_const::value, const ChildT, ChildT>::type; + + for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (ChildT* child = iter->second.child) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (std::is_same::value) { + array.push_back(reinterpret_cast(iter->second.child)); + } else { + child->getNodes(array);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + } +} + +template +template +inline void +RootNode::getNodes(ArrayT& array) const +{ + using NodePtr = typename ArrayT::value_type; + static_assert(std::is_pointer::value, + "argument to getNodes() must be a pointer array"); + using NodeType = typename std::remove_pointer::type; + static_assert(std::is_const::value, + "argument to getNodes() must be an array of const node pointers"); + using NonConstNodeType = typename std::remove_const::type; + using result = typename boost::mpl::contains::type; + static_assert(result::value, "can't extract non-const nodes from a const tree"); + + for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (const ChildNodeType *child = iter->second.child) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (std::is_same::value) { + array.push_back(reinterpret_cast(iter->second.child)); + } else { + child->getNodes(array);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + } +} + +//////////////////////////////////////// + +template +template +inline void +RootNode::stealNodes(ArrayT& array, const ValueType& value, bool state) +{ + using NodePtr = typename ArrayT::value_type; + static_assert(std::is_pointer::value, + "argument to stealNodes() must be a pointer array"); + using NodeType = typename std::remove_pointer::type; + using NonConstNodeType = typename std::remove_const::type; + using result = typename boost::mpl::contains::type; + static_assert(result::value, "can't extract non-const nodes from a const tree"); + using ArrayChildT = typename std::conditional< + std::is_const::value, const ChildT, ChildT>::type; + + for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (ChildT* child = iter->second.child) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (std::is_same::value) { + array.push_back(reinterpret_cast(&stealChild(iter, Tile(value, state)))); + } else { + child->stealNodes(array, value, state);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +RootNode::merge(RootNode& other) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + switch (Policy) { + + default: + case MERGE_ACTIVE_STATES: + for (MapIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + MapIter j = mTable.find(i->first); + if (other.isChild(i)) { + if (j == mTable.end()) { // insert other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + mTable[i->first] = NodeStruct(child); + } else if (isTile(j)) { + if (isTileOff(j)) { // replace inactive tile with other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + setChild(j, child); + } + } else { // merge both child nodes + getChild(j).template merge(getChild(i), + other.mBackground, mBackground); + } + } else if (other.isTileOn(i)) { + if (j == mTable.end()) { // insert other node's active tile + mTable[i->first] = i->second; + } else if (!isTileOn(j)) { + // Replace anything except an active tile with the other node's active tile. + setTile(j, Tile(other.getTile(i).value, true)); + } + } + } + break; + + case MERGE_NODES: + for (MapIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + MapIter j = mTable.find(i->first); + if (other.isChild(i)) { + if (j == mTable.end()) { // insert other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + mTable[i->first] = NodeStruct(child); + } else if (isTile(j)) { // replace tile with other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + setChild(j, child); + } else { // merge both child nodes + getChild(j).template merge( + getChild(i), other.mBackground, mBackground); + } + } + } + break; + + case MERGE_ACTIVE_STATES_AND_NODES: + for (MapIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + MapIter j = mTable.find(i->first); + if (other.isChild(i)) { + if (j == mTable.end()) { + // Steal and insert the other node's child. + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + mTable[i->first] = NodeStruct(child); + } else if (isTile(j)) { + // Replace this node's tile with the other node's child. + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + const Tile tile = getTile(j); + setChild(j, child); + if (tile.active) { + // Merge the other node's child with this node's active tile. + child.template merge( + tile.value, tile.active); + } + } else /*if (isChild(j))*/ { + // Merge the other node's child into this node's child. + getChild(j).template merge(getChild(i), + other.mBackground, mBackground); + } + } else if (other.isTileOn(i)) { + if (j == mTable.end()) { + // Insert a copy of the other node's active tile. + mTable[i->first] = i->second; + } else if (isTileOff(j)) { + // Replace this node's inactive tile with a copy of the other's active tile. + setTile(j, Tile(other.getTile(i).value, true)); + } else if (isChild(j)) { + // Merge the other node's active tile into this node's child. + const Tile& tile = getTile(i); + getChild(j).template merge( + tile.value, tile.active); + } + } // else if (other.isTileOff(i)) {} // ignore the other node's inactive tiles + } + break; + } + + // Empty the other tree so as not to leave it in a partially cannibalized state. + other.clear(); + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +template +inline void +RootNode::topologyUnion(const RootNode& other) +{ + using OtherRootT = RootNode; + using OtherCIterT = typename OtherRootT::MapCIter; + + enforceSameConfiguration(other); + + for (OtherCIterT i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + MapIter j = mTable.find(i->first); + if (other.isChild(i)) { + if (j == mTable.end()) { // create child branch with identical topology + mTable[i->first] = NodeStruct( + *(new ChildT(other.getChild(i), mBackground, TopologyCopy()))); + } else if (this->isChild(j)) { // union with child branch + this->getChild(j).topologyUnion(other.getChild(i)); + } else {// this is a tile so replace it with a child branch with identical topology + ChildT* child = new ChildT( + other.getChild(i), this->getTile(j).value, TopologyCopy()); + if (this->isTileOn(j)) child->setValuesOn();//this is an active tile + this->setChild(j, *child); + } + } else if (other.isTileOn(i)) { // other is an active tile + if (j == mTable.end()) { // insert an active tile + mTable[i->first] = NodeStruct(Tile(mBackground, true)); + } else if (this->isChild(j)) { + this->getChild(j).setValuesOn(); + } else if (this->isTileOff(j)) { + this->setTile(j, Tile(this->getTile(j).value, true)); + } + } + } +} + +template +template +inline void +RootNode::topologyIntersection(const RootNode& other) +{ + using OtherRootT = RootNode; + using OtherCIterT = typename OtherRootT::MapCIter; + + enforceSameConfiguration(other); + + std::set tmp;//keys to erase + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + OtherCIterT j = other.mTable.find(i->first); + if (this->isChild(i)) { + if (j == other.mTable.end() || other.isTileOff(j)) { + tmp.insert(i->first);//delete child branch + } else if (other.isChild(j)) { // intersect with child branch + this->getChild(i).topologyIntersection(other.getChild(j), mBackground); + } + } else if (this->isTileOn(i)) { + if (j == other.mTable.end() || other.isTileOff(j)) { + this->setTile(i, Tile(this->getTile(i).value, false));//turn inactive + } else if (other.isChild(j)) { //replace with a child branch with identical topology + ChildT* child = + new ChildT(other.getChild(j), this->getTile(i).value, TopologyCopy()); + this->setChild(i, *child); + } + } + } + for (std::set::iterator i = tmp.begin(), e = tmp.end(); i != e; ++i) { + MapIter it = this->findCoord(*i); + setTile(it, Tile()); // delete any existing child node first + mTable.erase(it); + } +} + +template +template +inline void +RootNode::topologyDifference(const RootNode& other) +{ + using OtherRootT = RootNode; + using OtherCIterT = typename OtherRootT::MapCIter; + + enforceSameConfiguration(other); + + for (OtherCIterT i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + MapIter j = mTable.find(i->first); + if (other.isChild(i)) { + if (j == mTable.end() || this->isTileOff(j)) { + //do nothing + } else if (this->isChild(j)) { // difference with child branch + this->getChild(j).topologyDifference(other.getChild(i), mBackground); + } else if (this->isTileOn(j)) { + // this is an active tile so create a child node and descent + ChildT* child = new ChildT(j->first, this->getTile(j).value, true); + child->topologyDifference(other.getChild(i), mBackground); + this->setChild(j, *child); + } + } else if (other.isTileOn(i)) { // other is an active tile + if (j == mTable.end() || this->isTileOff(j)) { + // do nothing + } else if (this->isChild(j)) { + setTile(j, Tile()); // delete any existing child node first + mTable.erase(j); + } else if (this->isTileOn(j)) { + this->setTile(j, Tile(this->getTile(j).value, false)); + } + } + } +} + +//////////////////////////////////////// + + +template +template +inline void +RootNode::combine(RootNode& other, CombineOp& op, bool prune) +{ + CombineArgs args; + + CoordSet keys; + this->insertKeys(keys); + other.insertKeys(keys); + + for (CoordSetCIter i = keys.begin(), e = keys.end(); i != e; ++i) { + MapIter iter = findOrAddCoord(*i), otherIter = other.findOrAddCoord(*i); + if (isTile(iter) && isTile(otherIter)) { + // Both this node and the other node have constant values (tiles). + // Combine the two values and store the result as this node's new tile value. + op(args.setARef(getTile(iter).value) + .setAIsActive(isTileOn(iter)) + .setBRef(getTile(otherIter).value) + .setBIsActive(isTileOn(otherIter))); + setTile(iter, Tile(args.result(), args.resultIsActive())); + + } else if (isChild(iter) && isTile(otherIter)) { + // Combine this node's child with the other node's constant value. + ChildT& child = getChild(iter); + child.combine(getTile(otherIter).value, isTileOn(otherIter), op); + + } else if (isTile(iter) && isChild(otherIter)) { + // Combine this node's constant value with the other node's child, + // but use a new functor in which the A and B values are swapped, + // since the constant value is the A value, not the B value. + SwappedCombineOp swappedOp(op); + ChildT& child = getChild(otherIter); + child.combine(getTile(iter).value, isTileOn(iter), swappedOp); + + // Steal the other node's child. + setChild(iter, stealChild(otherIter, Tile())); + + } else /*if (isChild(iter) && isChild(otherIter))*/ { + // Combine this node's child with the other node's child. + ChildT &child = getChild(iter), &otherChild = getChild(otherIter); + child.combine(otherChild, op); + } + if (prune && isChild(iter)) getChild(iter).prune(); + } + + // Combine background values. + op(args.setARef(mBackground).setBRef(other.mBackground)); + mBackground = args.result(); + + // Empty the other tree so as not to leave it in a partially cannibalized state. + other.clear(); +} + + +//////////////////////////////////////// + + +// This helper class is a friend of RootNode and is needed so that combine2 +// can be specialized for compatible and incompatible pairs of RootNode types. +template +struct RootNodeCombineHelper +{ + static inline void combine2(RootT& self, const RootT&, const OtherRootT& other1, + CombineOp&, bool) + { + // If the two root nodes have different configurations or incompatible ValueTypes, + // throw an exception. + self.enforceSameConfiguration(other1); + self.enforceCompatibleValueTypes(other1); + // One of the above two tests should throw, so we should never get here: + std::ostringstream ostr; + ostr << "cannot combine a " << typeid(OtherRootT).name() + << " into a " << typeid(RootT).name(); + OPENVDB_THROW(TypeError, ostr.str()); + } +}; + +// Specialization for root nodes of compatible types +template +struct RootNodeCombineHelper +{ + static inline void combine2(RootT& self, const RootT& other0, const OtherRootT& other1, + CombineOp& op, bool prune) + { + self.doCombine2(other0, other1, op, prune); + } +}; + + +template +template +inline void +RootNode::combine2(const RootNode& other0, const OtherRootNode& other1, + CombineOp& op, bool prune) +{ + using OtherValueType = typename OtherRootNode::ValueType; + static const bool compatible = (SameConfiguration::value + && CanConvertType::value); + RootNodeCombineHelper::combine2( + *this, other0, other1, op, prune); +} + + +template +template +inline void +RootNode::doCombine2(const RootNode& other0, const OtherRootNode& other1, + CombineOp& op, bool prune) +{ + enforceSameConfiguration(other1); + + using OtherValueT = typename OtherRootNode::ValueType; + using OtherTileT = typename OtherRootNode::Tile; + using OtherNodeStructT = typename OtherRootNode::NodeStruct; + using OtherMapCIterT = typename OtherRootNode::MapCIter; + + CombineArgs args; + + CoordSet keys; + other0.insertKeys(keys); + other1.insertKeys(keys); + + const NodeStruct bg0(Tile(other0.mBackground, /*active=*/false)); + const OtherNodeStructT bg1(OtherTileT(other1.mBackground, /*active=*/false)); + + for (CoordSetCIter i = keys.begin(), e = keys.end(); i != e; ++i) { + MapIter thisIter = this->findOrAddCoord(*i); + MapCIter iter0 = other0.findKey(*i); + OtherMapCIterT iter1 = other1.findKey(*i); + const NodeStruct& ns0 = (iter0 != other0.mTable.end()) ? iter0->second : bg0; + const OtherNodeStructT& ns1 = (iter1 != other1.mTable.end()) ? iter1->second : bg1; + if (ns0.isTile() && ns1.isTile()) { + // Both input nodes have constant values (tiles). + // Combine the two values and add a new tile to this node with the result. + op(args.setARef(ns0.tile.value) + .setAIsActive(ns0.isTileOn()) + .setBRef(ns1.tile.value) + .setBIsActive(ns1.isTileOn())); + setTile(thisIter, Tile(args.result(), args.resultIsActive())); + } else { + if (!isChild(thisIter)) { + // Add a new child with the same coordinates, etc. as the other node's child. + const Coord& childOrigin = + ns0.isChild() ? ns0.child->origin() : ns1.child->origin(); + setChild(thisIter, *(new ChildT(childOrigin, getTile(thisIter).value))); + } + ChildT& child = getChild(thisIter); + + if (ns0.isTile()) { + // Combine node1's child with node0's constant value + // and write the result into this node's child. + child.combine2(ns0.tile.value, *ns1.child, ns0.isTileOn(), op); + } else if (ns1.isTile()) { + // Combine node0's child with node1's constant value + // and write the result into this node's child. + child.combine2(*ns0.child, ns1.tile.value, ns1.isTileOn(), op); + } else { + // Combine node0's child with node1's child + // and write the result into this node's child. + child.combine2(*ns0.child, *ns1.child, op); + } + } + if (prune && isChild(thisIter)) getChild(thisIter).prune(); + } + + // Combine background values. + op(args.setARef(other0.mBackground).setBRef(other1.mBackground)); + mBackground = args.result(); +} + + +//////////////////////////////////////// + + +template +template +inline void +RootNode::visitActiveBBox(BBoxOp& op) const +{ + const bool descent = op.template descent(); + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (this->isTileOff(i)) continue; + if (this->isChild(i) && descent) { + this->getChild(i).visitActiveBBox(op); + } else { +#ifdef _MSC_VER + op.operator()(CoordBBox::createCube(i->first, ChildT::DIM)); +#else + op.template operator()(CoordBBox::createCube(i->first, ChildT::DIM)); +#endif + } + } +} + + +template +template +inline void +RootNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +RootNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +RootNode::doVisit(RootNodeT& self, VisitorOp& op) +{ + typename RootNodeT::ValueType val; + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + if (op(iter)) continue; + if (typename ChildAllIterT::ChildNodeType* child = iter.probeChild(val)) { + child->visit(op); + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +RootNode::visit2(OtherRootNodeType& other, VisitorOp& op) +{ + doVisit2(*this, other, op); +} + + +template +template +inline void +RootNode::visit2(OtherRootNodeType& other, VisitorOp& op) const +{ + doVisit2(*this, other, op); +} + + +template +template< + typename RootNodeT, + typename OtherRootNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +RootNode::doVisit2(RootNodeT& self, OtherRootNodeT& other, VisitorOp& op) +{ + enforceSameConfiguration(other); + + typename RootNodeT::ValueType val; + typename OtherRootNodeT::ValueType otherVal; + + // The two nodes are required to have corresponding table entries, + // but since that might require background tiles to be added to one or both, + // and the nodes might be const, we operate on shallow copies of the nodes instead. + RootNodeT copyOfSelf(self.mBackground); + copyOfSelf.mTable = self.mTable; + OtherRootNodeT copyOfOther(other.mBackground); + copyOfOther.mTable = other.mTable; + + // Add background tiles to both nodes as needed. + CoordSet keys; + self.insertKeys(keys); + other.insertKeys(keys); + for (CoordSetCIter i = keys.begin(), e = keys.end(); i != e; ++i) { + copyOfSelf.findOrAddCoord(*i); + copyOfOther.findOrAddCoord(*i); + } + + ChildAllIterT iter = copyOfSelf.beginChildAll(); + OtherChildAllIterT otherIter = copyOfOther.beginChildAll(); + + for ( ; iter && otherIter; ++iter, ++otherIter) + { + const size_t skipBranch = static_cast(op(iter, otherIter)); + + typename ChildAllIterT::ChildNodeType* child = + (skipBranch & 1U) ? nullptr : iter.probeChild(val); + typename OtherChildAllIterT::ChildNodeType* otherChild = + (skipBranch & 2U) ? nullptr : otherIter.probeChild(otherVal); + + if (child != nullptr && otherChild != nullptr) { + child->visit2Node(*otherChild, op); + } else if (child != nullptr) { + child->visit2(otherIter, op); + } else if (otherChild != nullptr) { + otherChild->visit2(iter, op, /*otherIsLHS=*/true); + } + } + // Remove any background tiles that were added above, + // as well as any that were created by the visitors. + copyOfSelf.eraseBackgroundTiles(); + copyOfOther.eraseBackgroundTiles(); + + // If either input node is non-const, replace its table with + // the (possibly modified) copy. + self.resetTable(copyOfSelf.mTable); + other.resetTable(copyOfOther.mTable); +} + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_ROOTNODE_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/Tree.h b/openvdb/tree/Tree.h new file mode 100644 index 00000000..1b391c54 --- /dev/null +++ b/openvdb/tree/Tree.h @@ -0,0 +1,2353 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file tree/Tree.h + +#ifndef OPENVDB_TREE_TREE_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_TREE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include "RootNode.h" +#include "InternalNode.h" +#include "LeafNode.h" +#include "TreeIterator.h" +#include "ValueAccessor.h" +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +/// @brief Base class for typed trees +class OPENVDB_API TreeBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + TreeBase() = default; + TreeBase(const TreeBase&) = default; + TreeBase& operator=(const TreeBase&) = delete; // disallow assignment + virtual ~TreeBase() = default; + + /// Return the name of this tree's type. + virtual const Name& type() const = 0; + + /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d"). + virtual Name valueType() const = 0; + + /// Return a pointer to a deep copy of this tree + virtual TreeBase::Ptr copy() const = 0; + + // + // Tree methods + // + /// @brief Return this tree's background value wrapped as metadata. + /// @note Query the metadata object for the value's type. + virtual Metadata::Ptr getBackgroundValue() const { return Metadata::Ptr(); } + + /// @brief Return in @a bbox the axis-aligned bounding box of all + /// leaf nodes and active tiles. + /// @details This is faster than calling evalActiveVoxelBoundingBox, + /// which visits the individual active voxels, and hence + /// evalLeafBoundingBox produces a less tight, i.e. approximate, bbox. + /// @return @c false if the bounding box is empty (in which case + /// the bbox is set to its default value). + virtual bool evalLeafBoundingBox(CoordBBox& bbox) const = 0; + + /// @brief Return in @a dim the dimensions of the axis-aligned bounding box + /// of all leaf nodes. + /// @return @c false if the bounding box is empty. + virtual bool evalLeafDim(Coord& dim) const = 0; + + /// @brief Return in @a bbox the axis-aligned bounding box of all + /// active voxels and tiles. + /// @details This method produces a more accurate, i.e. tighter, + /// bounding box than evalLeafBoundingBox which is approximate but + /// faster. + /// @return @c false if the bounding box is empty (in which case + /// the bbox is set to its default value). + virtual bool evalActiveVoxelBoundingBox(CoordBBox& bbox) const = 0; + + /// @brief Return in @a dim the dimensions of the axis-aligned bounding box of all + /// active voxels. This is a tighter bounding box than the leaf node bounding box. + /// @return @c false if the bounding box is empty. + virtual bool evalActiveVoxelDim(Coord& dim) const = 0; + + virtual void getIndexRange(CoordBBox& bbox) const = 0; + + /// @brief Replace with background tiles any nodes whose voxel buffers + /// have not yet been allocated. + /// @details Typically, unallocated nodes are leaf nodes whose voxel buffers + /// are not yet resident in memory because delayed loading is in effect. + /// @sa readNonresidentBuffers, io::File::open + virtual void clipUnallocatedNodes() = 0; +#if OPENVDB_ABI_VERSION_NUMBER >= 4 + /// Return the total number of unallocated leaf nodes residing in this tree. + virtual Index32 unallocatedLeafCount() const = 0; +#endif + + + // + // Statistics + // + /// @brief Return the depth of this tree. + /// + /// A tree with only a root node and leaf nodes has depth 2, for example. + virtual Index treeDepth() const = 0; + /// Return the number of leaf nodes. + virtual Index32 leafCount() const = 0; +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + /// Return a vector with node counts. The number of nodes of type NodeType + /// is given as element NodeType::LEVEL in the return vector. Thus, the size + /// of this vector corresponds to the height (or depth) of this tree. + virtual std::vector nodeCount() const = 0; +#endif + /// Return the number of non-leaf nodes. + virtual Index32 nonLeafCount() const = 0; + /// Return the number of active voxels stored in leaf nodes. + virtual Index64 activeLeafVoxelCount() const = 0; + /// Return the number of inactive voxels stored in leaf nodes. + virtual Index64 inactiveLeafVoxelCount() const = 0; + /// Return the total number of active voxels. + virtual Index64 activeVoxelCount() const = 0; + /// Return the number of inactive voxels within the bounding box of all active voxels. + virtual Index64 inactiveVoxelCount() const = 0; + /// Return the total number of active tiles. + virtual Index64 activeTileCount() const = 0; + + /// Return the total amount of memory in bytes occupied by this tree. + virtual Index64 memUsage() const { return 0; } + + + // + // I/O methods + // + /// @brief Read the tree topology from a stream. + /// + /// This will read the tree structure and tile values, but not voxel data. + virtual void readTopology(std::istream&, bool saveFloatAsHalf = false); + /// @brief Write the tree topology to a stream. + /// + /// This will write the tree structure and tile values, but not voxel data. + virtual void writeTopology(std::ostream&, bool saveFloatAsHalf = false) const; + + /// Read all data buffers for this tree. + virtual void readBuffers(std::istream&, bool saveFloatAsHalf = false) = 0; + /// Read all of this tree's data buffers that intersect the given bounding box. + virtual void readBuffers(std::istream&, const CoordBBox&, bool saveFloatAsHalf = false) = 0; + /// @brief Read all of this tree's data buffers that are not yet resident in memory + /// (because delayed loading is in effect). + /// @details If this tree was read from a memory-mapped file, this operation + /// disconnects the tree from the file. + /// @sa clipUnallocatedNodes, io::File::open, io::MappedFile + virtual void readNonresidentBuffers() const = 0; + /// Write out all the data buffers for this tree. + virtual void writeBuffers(std::ostream&, bool saveFloatAsHalf = false) const = 0; + + /// @brief Print statistics, memory usage and other information about this tree. + /// @param os a stream to which to write textual information + /// @param verboseLevel 1: print tree configuration only; + /// 2: include node and voxel statistics; + /// 3: include memory usage; + /// 4: include minimum and maximum voxel values + /// @warning @a verboseLevel 4 forces loading of any unallocated nodes. + virtual void print(std::ostream& os = std::cout, int verboseLevel = 1) const; +}; + + +//////////////////////////////////////// + + +template +class Tree: public TreeBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + using RootNodeType = _RootNodeType; + using ValueType = typename RootNodeType::ValueType; + using BuildType = typename RootNodeType::BuildType; + using LeafNodeType = typename RootNodeType::LeafNodeType; + + static const Index DEPTH = RootNodeType::LEVEL + 1; + + /// @brief ValueConverter::Type is the type of a tree having the same + /// hierarchy as this tree but a different value type, T. + /// + /// For example, FloatTree::ValueConverter::Type is equivalent to DoubleTree. + /// @note If the source tree type is a template argument, it might be necessary + /// to write "typename SourceTree::template ValueConverter::Type". + template + struct ValueConverter { + using Type = Tree::Type>; + }; + + + Tree() {} + + Tree& operator=(const Tree&) = delete; // disallow assignment + + /// Deep copy constructor + Tree(const Tree& other): TreeBase(other), mRoot(other.mRoot) + { + } + + /// @brief Value conversion deep copy constructor + /// + /// Deep copy a tree of the same configuration as this tree type but a different + /// ValueType, casting the other tree's values to this tree's ValueType. + /// @throw TypeError if the other tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the other tree's ValueType. + template + explicit Tree(const Tree& other): TreeBase(other), mRoot(other.root()) + { + } + + /// @brief Topology copy constructor from a tree of a different type + /// + /// Copy the structure, i.e., the active states of tiles and voxels, of another + /// tree of a possibly different type, but don't copy any tile or voxel values. + /// Instead, initialize tiles and voxels with the given active and inactive values. + /// @param other a tree having (possibly) a different ValueType + /// @param inactiveValue background value for this tree, and the value to which + /// all inactive tiles and voxels are initialized + /// @param activeValue value to which active tiles and voxels are initialized + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + Tree(const OtherTreeType& other, + const ValueType& inactiveValue, + const ValueType& activeValue, + TopologyCopy): + TreeBase(other), + mRoot(other.root(), inactiveValue, activeValue, TopologyCopy()) + { + } + + /// @brief Topology copy constructor from a tree of a different type + /// + /// @note This topology copy constructor is generally faster than + /// the one that takes both a foreground and a background value. + /// + /// Copy the structure, i.e., the active states of tiles and voxels, of another + /// tree of a possibly different type, but don't copy any tile or voxel values. + /// Instead, initialize tiles and voxels with the given background value. + /// @param other a tree having (possibly) a different ValueType + /// @param background the value to which tiles and voxels are initialized + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + Tree(const OtherTreeType& other, const ValueType& background, TopologyCopy): + TreeBase(other), + mRoot(other.root(), background, TopologyCopy()) + { + } + + /// Empty tree constructor + Tree(const ValueType& background): mRoot(background) {} + + ~Tree() override { this->clear(); releaseAllAccessors(); } + + /// Return a pointer to a deep copy of this tree + TreeBase::Ptr copy() const override { return TreeBase::Ptr(new Tree(*this)); } + + /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d") + Name valueType() const override { return typeNameAsString(); } + + /// Return the name of this type of tree. + static const Name& treeType(); + /// Return the name of this type of tree. + const Name& type() const override { return this->treeType(); } + + bool operator==(const Tree&) const { OPENVDB_THROW(NotImplementedError, ""); } + bool operator!=(const Tree&) const { OPENVDB_THROW(NotImplementedError, ""); } + + //@{ + /// Return this tree's root node. + RootNodeType& root() { return mRoot; } + const RootNodeType& root() const { return mRoot; } + //@} + + + // + // Tree methods + // + /// @brief Return @c true if the given tree has the same node and active value + /// topology as this tree, whether or not it has the same @c ValueType. + template + bool hasSameTopology(const Tree& other) const; + + bool evalLeafBoundingBox(CoordBBox& bbox) const override; + bool evalActiveVoxelBoundingBox(CoordBBox& bbox) const override; + bool evalActiveVoxelDim(Coord& dim) const override; + bool evalLeafDim(Coord& dim) const override; + + /// @brief Traverse the type hierarchy of nodes, and return, in @a dims, a list + /// of the Log2Dims of nodes in order from RootNode to LeafNode. + /// @note Because RootNodes are resizable, the RootNode Log2Dim is 0 for all trees. + static void getNodeLog2Dims(std::vector& dims); + + + // + // I/O methods + // + /// @brief Read the tree topology from a stream. + /// + /// This will read the tree structure and tile values, but not voxel data. + void readTopology(std::istream&, bool saveFloatAsHalf = false) override; + /// @brief Write the tree topology to a stream. + /// + /// This will write the tree structure and tile values, but not voxel data. + void writeTopology(std::ostream&, bool saveFloatAsHalf = false) const override; + /// Read all data buffers for this tree. + void readBuffers(std::istream&, bool saveFloatAsHalf = false) override; + /// Read all of this tree's data buffers that intersect the given bounding box. + void readBuffers(std::istream&, const CoordBBox&, bool saveFloatAsHalf = false) override; + /// @brief Read all of this tree's data buffers that are not yet resident in memory + /// (because delayed loading is in effect). + /// @details If this tree was read from a memory-mapped file, this operation + /// disconnects the tree from the file. + /// @sa clipUnallocatedNodes, io::File::open, io::MappedFile + void readNonresidentBuffers() const override; + /// Write out all data buffers for this tree. + void writeBuffers(std::ostream&, bool saveFloatAsHalf = false) const override; + + void print(std::ostream& os = std::cout, int verboseLevel = 1) const override; + + + // + // Statistics + // + /// @brief Return the depth of this tree. + /// + /// A tree with only a root node and leaf nodes has depth 2, for example. + Index treeDepth() const override { return DEPTH; } + /// Return the number of leaf nodes. + Index32 leafCount() const override { return mRoot.leafCount(); } +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + /// Return a vector with node counts. The number of nodes of type NodeType + /// is given as element NodeType::LEVEL in the return vector. Thus, the size + /// of this vector corresponds to the height (or depth) of this tree. + std::vector nodeCount() const override + { + std::vector vec(DEPTH, 0); + mRoot.nodeCount( vec ); + return vec;// Named Return Value Optimization + } +#endif + /// Return the number of non-leaf nodes. + Index32 nonLeafCount() const override { return mRoot.nonLeafCount(); } + /// Return the number of active voxels stored in leaf nodes. + Index64 activeLeafVoxelCount() const override { return mRoot.onLeafVoxelCount(); } + /// Return the number of inactive voxels stored in leaf nodes. + Index64 inactiveLeafVoxelCount() const override { return mRoot.offLeafVoxelCount(); } + /// Return the total number of active voxels. + Index64 activeVoxelCount() const override { return mRoot.onVoxelCount(); } + /// Return the number of inactive voxels within the bounding box of all active voxels. + Index64 inactiveVoxelCount() const override; + /// Return the total number of active tiles. + Index64 activeTileCount() const override { return mRoot.onTileCount(); } + + /// Return the minimum and maximum active values in this tree. + void evalMinMax(ValueType &min, ValueType &max) const; + + Index64 memUsage() const override { return sizeof(*this) + mRoot.memUsage(); } + + + // + // Voxel access methods (using signed indexing) + // + /// Return the value of the voxel at the given coordinates. + const ValueType& getValue(const Coord& xyz) const; + /// @brief Return the value of the voxel at the given coordinates + /// and update the given accessor's node cache. + template const ValueType& getValue(const Coord& xyz, AccessT&) const; + + /// @brief Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides. + /// @details If (x, y, z) isn't explicitly represented in the tree (i.e., it is + /// implicitly a background voxel), return -1. + int getValueDepth(const Coord& xyz) const; + + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value); + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOn(const Coord& xyz, const ValueType& value); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, const ValueType& value); + /// @brief Set the value of the voxel at the given coordinates, mark the voxel as active, + /// and update the given accessor's node cache. + template void setValue(const Coord& xyz, const ValueType& value, AccessT&); + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value); + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details Provided that the functor can be inlined, this is typically + /// significantly faster than calling getValue() followed by setValueOn(). + /// @param xyz the coordinates of a voxel whose value is to be modified + /// @param op a functor of the form void op(ValueType&) const that modifies + /// its argument in place + /// @par Example: + /// @code + /// Coord xyz(1, 0, -2); + /// // Multiply the value of a voxel by a constant and mark the voxel as active. + /// floatTree.modifyValue(xyz, [](float& f) { f *= 0.25; }); // C++11 + /// // Set the value of a voxel to the maximum of its current value and 0.25, + /// // and mark the voxel as active. + /// floatTree.modifyValue(xyz, [](float& f) { f = std::max(f, 0.25f); }); // C++11 + /// @endcode + /// @note The functor is not guaranteed to be called only once. + /// @see tools::foreach() + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details Provided that the functor can be inlined, this is typically + /// significantly faster than calling getValue() followed by setValue(). + /// @param xyz the coordinates of a voxel to be modified + /// @param op a functor of the form void op(ValueType&, bool&) const that + /// modifies its arguments, a voxel's value and active state, in place + /// @par Example: + /// @code + /// Coord xyz(1, 0, -2); + /// // Multiply the value of a voxel by a constant and mark the voxel as inactive. + /// floatTree.modifyValueAndActiveState(xyz, + /// [](float& f, bool& b) { f *= 0.25; b = false; }); // C++11 + /// // Set the value of a voxel to the maximum of its current value and 0.25, + /// // but don't change the voxel's active state. + /// floatTree.modifyValueAndActiveState(xyz, + /// [](float& f, bool&) { f = std::max(f, 0.25f); }); // C++11 + /// @endcode + /// @note The functor is not guaranteed to be called only once. + /// @see tools::foreach() + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// @brief Get the value of the voxel at the given coordinates. + /// @return @c true if the value is active. + bool probeValue(const Coord& xyz, ValueType& value) const; + + /// Return @c true if the value at the given coordinates is active. + bool isValueOn(const Coord& xyz) const { return mRoot.isValueOn(xyz); } + /// Return @c true if the value at the given coordinates is inactive. + bool isValueOff(const Coord& xyz) const { return !this->isValueOn(xyz); } + /// Return @c true if this tree has any active tiles. + bool hasActiveTiles() const { return mRoot.hasActiveTiles(); } + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&); + /// @brief Replace with background tiles any nodes whose voxel buffers + /// have not yet been allocated. + /// @details Typically, unallocated nodes are leaf nodes whose voxel buffers + /// are not yet resident in memory because delayed loading is in effect. + /// @sa readNonresidentBuffers, io::File::open + void clipUnallocatedNodes() override; + +#if OPENVDB_ABI_VERSION_NUMBER >= 4 + /// Return the total number of unallocated leaf nodes residing in this tree. + Index32 unallocatedLeafCount() const override; +#endif + + //@{ + /// @brief Set all voxels within a given axis-aligned box to a constant value. + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box + /// @param value the value to which to set voxels within the box + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive + /// @note This operation generates a sparse, but not always optimally sparse, + /// representation of the filled box. Follow fill operations with a prune() + /// operation for optimal sparseness. + void sparseFill(const CoordBBox& bbox, const ValueType& value, bool active = true); + void fill(const CoordBBox& bbox, const ValueType& value, bool active = true) + { + this->sparseFill(bbox, value, active); + } + //@} + + /// @brief Set all voxels within a given axis-aligned box to a constant value + /// and ensure that those voxels are all represented at the leaf level. + /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box. + /// @param value the value to which to set voxels within the box. + /// @param active if true, mark voxels within the box as active, + /// otherwise mark them as inactive. + /// @sa voxelizeActiveTiles() + void denseFill(const CoordBBox& bbox, const ValueType& value, bool active = true); + + /// @brief Densify active tiles, i.e., replace them with leaf-level active voxels. + /// + /// @param threaded if true, this operation is multi-threaded (over the internal nodes). + /// + /// @warning This method can explode the tree's memory footprint, especially if it + /// contains active tiles at the upper levels (in particular the root level)! + /// + /// @sa denseFill() + void voxelizeActiveTiles(bool threaded = true); + + /// @brief Reduce the memory footprint of this tree by replacing with tiles + /// any nodes whose values are all the same (optionally to within a tolerance) + /// and have the same active state. + /// @warning Will soon be deprecated! + void prune(const ValueType& tolerance = zeroVal()) + { + this->clearAllAccessors(); + mRoot.prune(tolerance); + } + + /// @brief Add the given leaf node to this tree, creating a new branch if necessary. + /// If a leaf node with the same origin already exists, replace it. + /// + /// @warning Ownership of the leaf is transferred to the tree so + /// the client code should not attempt to delete the leaf pointer! + void addLeaf(LeafNodeType* leaf) { assert(leaf); mRoot.addLeaf(leaf); } + + /// @brief Add a tile containing voxel (x, y, z) at the specified tree level, + /// creating a new branch if necessary. Delete any existing lower-level nodes + /// that contain (x, y, z). + /// @note @a level must be less than this tree's depth. + void addTile(Index level, const Coord& xyz, const ValueType& value, bool active); + + /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z) + /// and replace it with a tile of the specified value and state. + /// If no such node exists, leave the tree unchanged and return @c nullptr. + /// @note The caller takes ownership of the node and is responsible for deleting it. + template + NodeT* stealNode(const Coord& xyz, const ValueType& value, bool active); + + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, create one that preserves the values and + /// active states of all voxels. + /// @details Use this method to preallocate a static tree topology over which to + /// safely perform multithreaded processing. + LeafNodeType* touchLeaf(const Coord& xyz); + + //@{ + /// @brief Return a pointer to the node of type @c NodeType that contains + /// voxel (x, y, z). If no such node exists, return @c nullptr. + template NodeType* probeNode(const Coord& xyz); + template const NodeType* probeConstNode(const Coord& xyz) const; + template const NodeType* probeNode(const Coord& xyz) const; + //@} + + //@{ + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, return @c nullptr. + LeafNodeType* probeLeaf(const Coord& xyz); + const LeafNodeType* probeConstLeaf(const Coord& xyz) const; + const LeafNodeType* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + //@} + + //@{ + /// @brief Adds all nodes of a certain type to a container with the following API: + /// @code + /// struct ArrayT { + /// using value_type = ...; // the type of node to be added to the array + /// void push_back(value_type nodePtr); // add a node to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// using value_type = LeafType*; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.getNodes(array); + /// @endcode + template void getNodes(ArrayT& array) { mRoot.getNodes(array); } + template void getNodes(ArrayT& array) const { mRoot.getNodes(array); } + //@} + + /// @brief Steals all nodes of a certain type from the tree and + /// adds them to a container with the following API: + /// @code + /// struct ArrayT { + /// using value_type = ...; // the type of node to be added to the array + /// void push_back(value_type nodePtr); // add a node to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// using value_type = LeafType*; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.stealNodes(array); + /// @endcode + template + void stealNodes(ArrayT& array) { this->clearAllAccessors(); mRoot.stealNodes(array); } + template + void stealNodes(ArrayT& array, const ValueType& value, bool state) + { + this->clearAllAccessors(); + mRoot.stealNodes(array, value, state); + } + + // + // Aux methods + // + /// @brief Return @c true if this tree contains no nodes other than + /// the root node and no tiles other than background tiles. + bool empty() const { return mRoot.empty(); } + + /// Remove all tiles from this tree and all nodes other than the root node. + void clear(); + + /// Clear all registered accessors. + void clearAllAccessors(); + + //@{ + /// @brief Register an accessor for this tree. Registered accessors are + /// automatically cleared whenever one of this tree's nodes is deleted. + void attachAccessor(ValueAccessorBase&) const; + void attachAccessor(ValueAccessorBase&) const; + //@} + + //@{ + /// Dummy implementations + void attachAccessor(ValueAccessorBase&) const {} + void attachAccessor(ValueAccessorBase&) const {} + //@} + + //@{ + /// Deregister an accessor so that it is no longer automatically cleared. + void releaseAccessor(ValueAccessorBase&) const; + void releaseAccessor(ValueAccessorBase&) const; + //@} + + //@{ + /// Dummy implementations + void releaseAccessor(ValueAccessorBase&) const {} + void releaseAccessor(ValueAccessorBase&) const {} + //@} + + /// @brief Return this tree's background value wrapped as metadata. + /// @note Query the metadata object for the value's type. + Metadata::Ptr getBackgroundValue() const override; + + /// @brief Return this tree's background value. + /// + /// @note Use tools::changeBackground to efficiently modify the + /// background values. Else use tree.root().setBackground, which + /// is serial and hence slower. + const ValueType& background() const { return mRoot.background(); } + + /// Min and max are both inclusive. + void getIndexRange(CoordBBox& bbox) const override { mRoot.getIndexRange(bbox); } + + /// @brief Efficiently merge another tree into this tree using one of several schemes. + /// @details This operation is primarily intended to combine trees that are mostly + /// non-overlapping (for example, intermediate trees from computations that are + /// parallelized across disjoint regions of space). + /// @note This operation is not guaranteed to produce an optimally sparse tree. + /// Follow merge() with prune() for optimal sparseness. + /// @warning This operation always empties the other tree. + void merge(Tree& other, MergePolicy = MERGE_ACTIVE_STATES); + + /// @brief Union this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. + /// @details The resulting state of a value is active if the corresponding value + /// was already active OR if it is active in the other tree. Also, a resulting + /// value maps to a voxel if the corresponding value already mapped to a voxel + /// OR if it is a voxel in the other tree. Thus, a resulting value can only + /// map to a tile if the corresponding value already mapped to a tile + /// AND if it is a tile value in other tree. + /// + /// @note This operation modifies only active states, not values. + /// Specifically, active tiles and voxels in this tree are not changed, and + /// tiles or voxels that were inactive in this tree but active in the other tree + /// are marked as active in this tree but left with their original values. + template + void topologyUnion(const Tree& other); + + /// @brief Intersects this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. + /// @details The resulting state of a value is active only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with inactive tiles in the other grid. Likewise active + /// voxels can be turned into unactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call tools::pruneInactive. + template + void topologyIntersection(const Tree& other); + + /// @brief Difference this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this tree and inactive in the other tree. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with active tiles in the other grid. Likewise active + /// voxels can be turned into inactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call tools::pruneInactive. + template + void topologyDifference(const Tree& other); + + /// For a given function @c f, use sparse traversal to compute f(this, other) + /// over all corresponding pairs of values (tile or voxel) of this tree and the other tree + /// and store the result in this tree. + /// This method is typically more space-efficient than the two-tree combine2(), + /// since it moves rather than copies nodes from the other tree into this tree. + /// @note This operation always empties the other tree. + /// @param other a tree of the same type as this tree + /// @param op a functor of the form void op(const T& a, const T& b, T& result), + /// where @c T is this tree's @c ValueType, that computes + /// result = f(a, b) + /// @param prune if true, prune the resulting tree one branch at a time (this is usually + /// more space-efficient than pruning the entire tree in one pass) + /// + /// @par Example: + /// Compute the per-voxel difference between two floating-point trees, + /// @c aTree and @c bTree, and store the result in @c aTree (leaving @c bTree empty). + /// @code + /// { + /// struct Local { + /// static inline void diff(const float& a, const float& b, float& result) { + /// result = a - b; + /// } + /// }; + /// aTree.combine(bTree, Local::diff); + /// } + /// @endcode + /// + /// @par Example: + /// Compute f * a + (1 - f) * b over all voxels of two floating-point trees, + /// @c aTree and @c bTree, and store the result in @c aTree (leaving @c bTree empty). + /// @code + /// namespace { + /// struct Blend { + /// Blend(float f): frac(f) {} + /// inline void operator()(const float& a, const float& b, float& result) const { + /// result = frac * a + (1.0 - frac) * b; + /// } + /// float frac; + /// }; + /// } + /// { + /// aTree.combine(bTree, Blend(0.25)); // 0.25 * a + 0.75 * b + /// } + /// @endcode + template + void combine(Tree& other, CombineOp& op, bool prune = false); +#ifndef _MSC_VER + template + void combine(Tree& other, const CombineOp& op, bool prune = false); +#endif + + /// Like combine(), but with + /// @param other a tree of the same type as this tree + /// @param op a functor of the form void op(CombineArgs& args) that + /// computes args.setResult(f(args.a(), args.b())) and, optionally, + /// args.setResultIsActive(g(args.aIsActive(), args.bIsActive())) + /// for some functions @c f and @c g + /// @param prune if true, prune the resulting tree one branch at a time (this is usually + /// more space-efficient than pruning the entire tree in one pass) + /// + /// This variant passes not only the @em a and @em b values but also the active states + /// of the @em a and @em b values to the functor, which may then return, by calling + /// @c args.setResultIsActive(), a computed active state for the result value. + /// By default, the result is active if either the @em a or the @em b value is active. + /// + /// @see openvdb/Types.h for the definition of the CombineArgs struct. + /// + /// @par Example: + /// Replace voxel values in floating-point @c aTree with corresponding values + /// from floating-point @c bTree (leaving @c bTree empty) wherever the @c bTree + /// values are larger. Also, preserve the active states of any transferred values. + /// @code + /// { + /// struct Local { + /// static inline void max(CombineArgs& args) { + /// if (args.b() > args.a()) { + /// // Transfer the B value and its active state. + /// args.setResult(args.b()); + /// args.setResultIsActive(args.bIsActive()); + /// } else { + /// // Preserve the A value and its active state. + /// args.setResult(args.a()); + /// args.setResultIsActive(args.aIsActive()); + /// } + /// } + /// }; + /// aTree.combineExtended(bTree, Local::max); + /// } + /// @endcode + template + void combineExtended(Tree& other, ExtendedCombineOp& op, bool prune = false); +#ifndef _MSC_VER + template + void combineExtended(Tree& other, const ExtendedCombineOp& op, bool prune = false); +#endif + + /// For a given function @c f, use sparse traversal to compute f(a, b) over all + /// corresponding pairs of values (tile or voxel) of trees A and B and store the result + /// in this tree. + /// @param a,b two trees with the same configuration (levels and node dimensions) + /// as this tree but with the B tree possibly having a different value type + /// @param op a functor of the form void op(const T1& a, const T2& b, T1& result), + /// where @c T1 is this tree's and the A tree's @c ValueType and @c T2 is the + /// B tree's @c ValueType, that computes result = f(a, b) + /// @param prune if true, prune the resulting tree one branch at a time (this is usually + /// more space-efficient than pruning the entire tree in one pass) + /// + /// @throw TypeError if the B tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the B tree's ValueType. + /// + /// @par Example: + /// Compute the per-voxel difference between two floating-point trees, + /// @c aTree and @c bTree, and store the result in a third tree. + /// @code + /// { + /// struct Local { + /// static inline void diff(const float& a, const float& b, float& result) { + /// result = a - b; + /// } + /// }; + /// FloatTree resultTree; + /// resultTree.combine2(aTree, bTree, Local::diff); + /// } + /// @endcode + template + void combine2(const Tree& a, const OtherTreeType& b, CombineOp& op, bool prune = false); +#ifndef _MSC_VER + template + void combine2(const Tree& a, const OtherTreeType& b, const CombineOp& op, bool prune = false); +#endif + + /// Like combine2(), but with + /// @param a,b two trees with the same configuration (levels and node dimensions) + /// as this tree but with the B tree possibly having a different value type + /// @param op a functor of the form void op(CombineArgs& args), where + /// @c T1 is this tree's and the A tree's @c ValueType and @c T2 is the B tree's + /// @c ValueType, that computes args.setResult(f(args.a(), args.b())) + /// and, optionally, + /// args.setResultIsActive(g(args.aIsActive(), args.bIsActive())) + /// for some functions @c f and @c g + /// @param prune if true, prune the resulting tree one branch at a time (this is usually + /// more space-efficient than pruning the entire tree in one pass) + /// This variant passes not only the @em a and @em b values but also the active states + /// of the @em a and @em b values to the functor, which may then return, by calling + /// args.setResultIsActive(), a computed active state for the result value. + /// By default, the result is active if either the @em a or the @em b value is active. + /// + /// @throw TypeError if the B tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the B tree's ValueType. + /// + /// @see openvdb/Types.h for the definition of the CombineArgs struct. + /// + /// @par Example: + /// Compute the per-voxel maximum values of two single-precision floating-point trees, + /// @c aTree and @c bTree, and store the result in a third tree. Set the active state + /// of each output value to that of the larger of the two input values. + /// @code + /// { + /// struct Local { + /// static inline void max(CombineArgs& args) { + /// if (args.b() > args.a()) { + /// // Transfer the B value and its active state. + /// args.setResult(args.b()); + /// args.setResultIsActive(args.bIsActive()); + /// } else { + /// // Preserve the A value and its active state. + /// args.setResult(args.a()); + /// args.setResultIsActive(args.aIsActive()); + /// } + /// } + /// }; + /// FloatTree aTree = ...; + /// FloatTree bTree = ...; + /// FloatTree resultTree; + /// resultTree.combine2Extended(aTree, bTree, Local::max); + /// } + /// @endcode + /// + /// @par Example: + /// Compute the per-voxel maximum values of a double-precision and a single-precision + /// floating-point tree, @c aTree and @c bTree, and store the result in a third, + /// double-precision tree. Set the active state of each output value to that of + /// the larger of the two input values. + /// @code + /// { + /// struct Local { + /// static inline void max(CombineArgs& args) { + /// if (args.b() > args.a()) { + /// // Transfer the B value and its active state. + /// args.setResult(args.b()); + /// args.setResultIsActive(args.bIsActive()); + /// } else { + /// // Preserve the A value and its active state. + /// args.setResult(args.a()); + /// args.setResultIsActive(args.aIsActive()); + /// } + /// } + /// }; + /// DoubleTree aTree = ...; + /// FloatTree bTree = ...; + /// DoubleTree resultTree; + /// resultTree.combine2Extended(aTree, bTree, Local::max); + /// } + /// @endcode + template + void combine2Extended(const Tree& a, const OtherTreeType& b, ExtendedCombineOp& op, + bool prune = false); +#ifndef _MSC_VER + template + void combine2Extended(const Tree& a, const OtherTreeType& b, const ExtendedCombineOp&, + bool prune = false); +#endif + + /// @brief Use sparse traversal to call the given functor with bounding box + /// information for all active tiles and leaf nodes or active voxels in the tree. + /// + /// @note The bounding boxes are guaranteed to be non-overlapping. + /// @param op a functor with a templated call operator of the form + /// template void operator()(const CoordBBox& bbox), + /// where bbox is the bounding box of either an active tile + /// (if @c LEVEL > 0), a leaf node or an active voxel. + /// The functor must also provide a templated method of the form + /// template bool descent() that returns @c false + /// if bounding boxes below the specified tree level are not to be visited. + /// In such cases of early tree termination, a bounding box is instead + /// derived from each terminating child node. + /// + /// @par Example: + /// Visit and process all active tiles and leaf nodes in a tree, but don't + /// descend to the active voxels. The smallest bounding boxes that will be + /// visited are those of leaf nodes or level-1 active tiles. + /// @code + /// { + /// struct ProcessTilesAndLeafNodes { + /// // Descend to leaf nodes, but no further. + /// template inline bool descent() { return LEVEL > 0; } + /// // Use this version to descend to voxels: + /// //template inline bool descent() { return true; } + /// + /// template + /// inline void operator()(const CoordBBox &bbox) { + /// if (LEVEL > 0) { + /// // code to process an active tile + /// } else { + /// // code to process a leaf node + /// } + /// } + /// }; + /// ProcessTilesAndLeafNodes op; + /// aTree.visitActiveBBox(op); + /// } + /// @endcode + /// @see openvdb/unittest/TestTree.cc for another example. + template void visitActiveBBox(BBoxOp& op) const { mRoot.visitActiveBBox(op); } + + /// Traverse this tree in depth-first order, and at each node call the given functor + /// with a @c DenseIterator (see Iterator.h) that points to either a child node or a + /// tile value. If the iterator points to a child node and the functor returns true, + /// do not descend to the child node; instead, continue the traversal at the next + /// iterator position. + /// @param op a functor of the form template bool op(IterT&), + /// where @c IterT is either a RootNode::ChildAllIter, + /// an InternalNode::ChildAllIter or a LeafNode::ChildAllIter + /// + /// @note There is no iterator that points to a RootNode, so to visit the root node, + /// retrieve the @c parent() of a RootNode::ChildAllIter. + /// + /// @par Example: + /// Print information about the nodes and tiles of a tree, but not individual voxels. + /// @code + /// namespace { + /// template + /// struct PrintTreeVisitor + /// { + /// using RootT = typename TreeT::RootNodeType; + /// bool visitedRoot; + /// + /// PrintTreeVisitor(): visitedRoot(false) {} + /// + /// template + /// inline bool operator()(IterT& iter) + /// { + /// if (!visitedRoot && iter.parent().getLevel() == RootT::LEVEL) { + /// visitedRoot = true; + /// std::cout << "Level-" << RootT::LEVEL << " node" << std::endl; + /// } + /// typename IterT::NonConstValueType value; + /// typename IterT::ChildNodeType* child = iter.probeChild(value); + /// if (child == nullptr) { + /// std::cout << "Tile with value " << value << std::endl; + /// return true; // no child to visit, so stop descending + /// } + /// std::cout << "Level-" << child->getLevel() << " node" << std::endl; + /// return (child->getLevel() == 0); // don't visit leaf nodes + /// } + /// + /// // The generic method, above, calls iter.probeChild(), which is not defined + /// // for LeafNode::ChildAllIter. These overloads ensure that the generic + /// // method template doesn't get instantiated for LeafNode iterators. + /// bool operator()(typename TreeT::LeafNodeType::ChildAllIter&) { return true; } + /// bool operator()(typename TreeT::LeafNodeType::ChildAllCIter&) { return true; } + /// }; + /// } + /// { + /// PrintTreeVisitor visitor; + /// tree.visit(visitor); + /// } + /// @endcode + template void visit(VisitorOp& op); + template void visit(const VisitorOp& op); + + /// Like visit(), but using @c const iterators, i.e., with + /// @param op a functor of the form template bool op(IterT&), + /// where @c IterT is either a RootNode::ChildAllCIter, + /// an InternalNode::ChildAllCIter or a LeafNode::ChildAllCIter + template void visit(VisitorOp& op) const; + template void visit(const VisitorOp& op) const; + + /// Traverse this tree and another tree in depth-first order, and for corresponding + /// subregions of index space call the given functor with two @c DenseIterators + /// (see Iterator.h), each of which points to either a child node or a tile value + /// of this tree and the other tree. If the A iterator points to a child node + /// and the functor returns a nonzero value with bit 0 set (e.g., 1), do not descend + /// to the child node; instead, continue the traversal at the next A iterator position. + /// Similarly, if the B iterator points to a child node and the functor returns a value + /// with bit 1 set (e.g., 2), continue the traversal at the next B iterator position. + /// @note The other tree must have the same index space and fan-out factors as + /// this tree, but it may have a different @c ValueType and a different topology. + /// @param other a tree of the same type as this tree + /// @param op a functor of the form + /// template int op(AIterT&, BIterT&), + /// where @c AIterT and @c BIterT are any combination of a + /// RootNode::ChildAllIter, an InternalNode::ChildAllIter or a + /// LeafNode::ChildAllIter with an @c OtherTreeType::RootNode::ChildAllIter, + /// an @c OtherTreeType::InternalNode::ChildAllIter + /// or an @c OtherTreeType::LeafNode::ChildAllIter + /// + /// @par Example: + /// Given two trees of the same type, @c aTree and @c bTree, replace leaf nodes of + /// @c aTree with corresponding leaf nodes of @c bTree, leaving @c bTree partially empty. + /// @code + /// namespace { + /// template + /// inline int stealLeafNodes(AIterT& aIter, BIterT& bIter) + /// { + /// typename AIterT::NonConstValueType aValue; + /// typename AIterT::ChildNodeType* aChild = aIter.probeChild(aValue); + /// typename BIterT::NonConstValueType bValue; + /// typename BIterT::ChildNodeType* bChild = bIter.probeChild(bValue); + /// + /// const Index aLevel = aChild->getLevel(), bLevel = bChild->getLevel(); + /// if (aChild && bChild && aLevel == 0 && bLevel == 0) { // both are leaf nodes + /// aIter.setChild(bChild); // give B's child to A + /// bIter.setValue(bValue); // replace B's child with a constant tile value + /// } + /// // Don't iterate over leaf node voxels of either A or B. + /// int skipBranch = (aLevel == 0) ? 1 : 0; + /// if (bLevel == 0) skipBranch = skipBranch | 2; + /// return skipBranch; + /// } + /// } + /// { + /// aTree.visit2(bTree, stealLeafNodes); + /// } + /// @endcode + template + void visit2(OtherTreeType& other, VisitorOp& op); + template + void visit2(OtherTreeType& other, const VisitorOp& op); + + /// Like visit2(), but using @c const iterators, i.e., with + /// @param other a tree of the same type as this tree + /// @param op a functor of the form + /// template int op(AIterT&, BIterT&), + /// where @c AIterT and @c BIterT are any combination of a + /// RootNode::ChildAllCIter, an InternalNode::ChildAllCIter + /// or a LeafNode::ChildAllCIter with an + /// @c OtherTreeType::RootNode::ChildAllCIter, + /// an @c OtherTreeType::InternalNode::ChildAllCIter + /// or an @c OtherTreeType::LeafNode::ChildAllCIter + template + void visit2(OtherTreeType& other, VisitorOp& op) const; + template + void visit2(OtherTreeType& other, const VisitorOp& op) const; + + + // + // Iteration + // + //@{ + /// Return an iterator over children of the root node. + typename RootNodeType::ChildOnCIter beginRootChildren() const { return mRoot.cbeginChildOn(); } + typename RootNodeType::ChildOnCIter cbeginRootChildren() const { return mRoot.cbeginChildOn(); } + typename RootNodeType::ChildOnIter beginRootChildren() { return mRoot.beginChildOn(); } + //@} + + //@{ + /// Return an iterator over non-child entries of the root node's table. + typename RootNodeType::ChildOffCIter beginRootTiles() const { return mRoot.cbeginChildOff(); } + typename RootNodeType::ChildOffCIter cbeginRootTiles() const { return mRoot.cbeginChildOff(); } + typename RootNodeType::ChildOffIter beginRootTiles() { return mRoot.beginChildOff(); } + //@} + + //@{ + /// Return an iterator over all entries of the root node's table. + typename RootNodeType::ChildAllCIter beginRootDense() const { return mRoot.cbeginChildAll(); } + typename RootNodeType::ChildAllCIter cbeginRootDense() const { return mRoot.cbeginChildAll(); } + typename RootNodeType::ChildAllIter beginRootDense() { return mRoot.beginChildAll(); } + //@} + + + //@{ + /// Iterator over all nodes in this tree + using NodeIter = NodeIteratorBase; + using NodeCIter = NodeIteratorBase; + //@} + + //@{ + /// Iterator over all leaf nodes in this tree + using LeafIter = LeafIteratorBase; + using LeafCIter = LeafIteratorBase; + //@} + + //@{ + /// Return an iterator over all nodes in this tree. + NodeIter beginNode() { return NodeIter(*this); } + NodeCIter beginNode() const { return NodeCIter(*this); } + NodeCIter cbeginNode() const { return NodeCIter(*this); } + //@} + + //@{ + /// Return an iterator over all leaf nodes in this tree. + LeafIter beginLeaf() { return LeafIter(*this); } + LeafCIter beginLeaf() const { return LeafCIter(*this); } + LeafCIter cbeginLeaf() const { return LeafCIter(*this); } + //@} + + using ValueAllIter = TreeValueIteratorBase; + using ValueAllCIter = TreeValueIteratorBase; + using ValueOnIter = TreeValueIteratorBase; + using ValueOnCIter = TreeValueIteratorBase; + using ValueOffIter = TreeValueIteratorBase; + using ValueOffCIter = TreeValueIteratorBase; + + //@{ + /// Return an iterator over all values (tile and voxel) across all nodes. + ValueAllIter beginValueAll() { return ValueAllIter(*this); } + ValueAllCIter beginValueAll() const { return ValueAllCIter(*this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(*this); } + //@} + //@{ + /// Return an iterator over active values (tile and voxel) across all nodes. + ValueOnIter beginValueOn() { return ValueOnIter(*this); } + ValueOnCIter beginValueOn() const { return ValueOnCIter(*this); } + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(*this); } + //@} + //@{ + /// Return an iterator over inactive values (tile and voxel) across all nodes. + ValueOffIter beginValueOff() { return ValueOffIter(*this); } + ValueOffCIter beginValueOff() const { return ValueOffCIter(*this); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(*this); } + //@} + + /// @brief Return an iterator of type @c IterT (for example, begin() is + /// equivalent to beginValueOn()). + template IterT begin(); + /// @brief Return a const iterator of type CIterT (for example, cbegin() + /// is equivalent to cbeginValueOn()). + template CIterT cbegin() const; + + +protected: + using AccessorRegistry = tbb::concurrent_hash_map*, bool>; + using ConstAccessorRegistry = tbb::concurrent_hash_map*, bool>; + + /// @brief Notify all registered accessors, by calling ValueAccessor::release(), + /// that this tree is about to be deleted. + void releaseAllAccessors(); + + // TBB body object used to deallocates nodes in parallel. + template + struct DeallocateNodes { + DeallocateNodes(std::vector& nodes) + : mNodes(nodes.empty() ? nullptr : &nodes.front()) { } + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + delete mNodes[n]; mNodes[n] = nullptr; + } + } + NodeType ** const mNodes; + }; + + // + // Data members + // + RootNodeType mRoot; // root node of the tree + mutable AccessorRegistry mAccessorRegistry; + mutable ConstAccessorRegistry mConstAccessorRegistry; + + static std::unique_ptr sTreeTypeName; +}; // end of Tree class + +template +std::unique_ptr Tree<_RootNodeType>::sTreeTypeName; + + +/// @brief Tree3::Type is the type of a three-level tree +/// (Root, Internal, Leaf) with value type T and +/// internal and leaf node log dimensions N1 and N2, respectively. +/// @note This is NOT the standard tree configuration (Tree4 is). +template +struct Tree3 { + using Type = Tree, N1>>>; +}; + + +/// @brief Tree4::Type is the type of a four-level tree +/// (Root, Internal, Internal, Leaf) with value type T and +/// internal and leaf node log dimensions N1, N2 and N3, respectively. +/// @note This is the standard tree configuration. +template +struct Tree4 { + using Type = Tree, N2>, N1>>>; +}; + +/// @brief Tree5::Type is the type of a five-level tree +/// (Root, Internal, Internal, Internal, Leaf) with value type T and +/// internal and leaf node log dimensions N1, N2, N3 and N4, respectively. +/// @note This is NOT the standard tree configuration (Tree4 is). +template +struct Tree5 { + using Type = + Tree, N3>, N2>, N1>>>; +}; + + +//////////////////////////////////////// + + +inline void +TreeBase::readTopology(std::istream& is, bool /*saveFloatAsHalf*/) +{ + int32_t bufferCount; + is.read(reinterpret_cast(&bufferCount), sizeof(int32_t)); + if (bufferCount != 1) OPENVDB_LOG_WARN("multi-buffer trees are no longer supported"); +} + + +inline void +TreeBase::writeTopology(std::ostream& os, bool /*saveFloatAsHalf*/) const +{ + int32_t bufferCount = 1; + os.write(reinterpret_cast(&bufferCount), sizeof(int32_t)); +} + + +inline void +TreeBase::print(std::ostream& os, int /*verboseLevel*/) const +{ + os << " Tree Type: " << type() + << " Active Voxel Count: " << activeVoxelCount() << std::endl + << " Active tile Count: " << activeTileCount() << std::endl + << " Inactive Voxel Count: " << inactiveVoxelCount() << std::endl + << " Leaf Node Count: " << leafCount() << std::endl + << " Non-leaf Node Count: " << nonLeafCount() << std::endl; +} + + +//////////////////////////////////////// + + +// +// Type traits for tree iterators +// + +/// @brief TreeIterTraits provides, for all tree iterators, a begin(tree) function +/// that returns an iterator over a tree of arbitrary type. +template struct TreeIterTraits; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildOnIter begin(TreeT& tree) { + return tree.beginRootChildren(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildOnCIter begin(const TreeT& tree) { + return tree.cbeginRootChildren(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildOffIter begin(TreeT& tree) { + return tree.beginRootTiles(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildOffCIter begin(const TreeT& tree) { + return tree.cbeginRootTiles(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildAllIter begin(TreeT& tree) { + return tree.beginRootDense(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildAllCIter begin(const TreeT& tree) { + return tree.cbeginRootDense(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::NodeIter begin(TreeT& tree) { return tree.beginNode(); } +}; + +template struct TreeIterTraits { + static typename TreeT::NodeCIter begin(const TreeT& tree) { return tree.cbeginNode(); } +}; + +template struct TreeIterTraits { + static typename TreeT::LeafIter begin(TreeT& tree) { return tree.beginLeaf(); } +}; + +template struct TreeIterTraits { + static typename TreeT::LeafCIter begin(const TreeT& tree) { return tree.cbeginLeaf(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueOnIter begin(TreeT& tree) { return tree.beginValueOn(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueOnCIter begin(const TreeT& tree) { return tree.cbeginValueOn(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueOffIter begin(TreeT& tree) { return tree.beginValueOff(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueOffCIter begin(const TreeT& tree) { return tree.cbeginValueOff(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueAllIter begin(TreeT& tree) { return tree.beginValueAll(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueAllCIter begin(const TreeT& tree) { return tree.cbeginValueAll(); } +}; + + +template +template +inline IterT +Tree::begin() +{ + return TreeIterTraits::begin(*this); +} + + +template +template +inline IterT +Tree::cbegin() const +{ + return TreeIterTraits::begin(*this); +} + + +//////////////////////////////////////// + + +template +void +Tree::readTopology(std::istream& is, bool saveFloatAsHalf) +{ + this->clearAllAccessors(); + TreeBase::readTopology(is, saveFloatAsHalf); + mRoot.readTopology(is, saveFloatAsHalf); +} + + +template +void +Tree::writeTopology(std::ostream& os, bool saveFloatAsHalf) const +{ + TreeBase::writeTopology(os, saveFloatAsHalf); + mRoot.writeTopology(os, saveFloatAsHalf); +} + + +template +inline void +Tree::readBuffers(std::istream &is, bool saveFloatAsHalf) +{ + this->clearAllAccessors(); + mRoot.readBuffers(is, saveFloatAsHalf); +} + + +template +inline void +Tree::readBuffers(std::istream &is, const CoordBBox& bbox, bool saveFloatAsHalf) +{ + this->clearAllAccessors(); + mRoot.readBuffers(is, bbox, saveFloatAsHalf); +} + + +template +inline void +Tree::readNonresidentBuffers() const +{ + for (LeafCIter it = this->cbeginLeaf(); it; ++it) { + // Retrieving the value of a leaf voxel forces loading of the leaf node's voxel buffer. + it->getValue(Index(0)); + } +} + + +template +inline void +Tree::writeBuffers(std::ostream &os, bool saveFloatAsHalf) const +{ + mRoot.writeBuffers(os, saveFloatAsHalf); +} + + +template +inline void +Tree::clear() +{ + std::vector leafnodes; + this->stealNodes(leafnodes); + + tbb::parallel_for(tbb::blocked_range(0, leafnodes.size()), + DeallocateNodes(leafnodes)); + + std::vector internalNodes; + this->stealNodes(internalNodes); + + tbb::parallel_for(tbb::blocked_range(0, internalNodes.size()), + DeallocateNodes(internalNodes)); + + mRoot.clear(); + + this->clearAllAccessors(); +} + + +//////////////////////////////////////// + + +template +inline void +Tree::attachAccessor(ValueAccessorBase& accessor) const +{ + typename AccessorRegistry::accessor a; + mAccessorRegistry.insert(a, &accessor); +} + + +template +inline void +Tree::attachAccessor(ValueAccessorBase& accessor) const +{ + typename ConstAccessorRegistry::accessor a; + mConstAccessorRegistry.insert(a, &accessor); +} + + +template +inline void +Tree::releaseAccessor(ValueAccessorBase& accessor) const +{ + mAccessorRegistry.erase(&accessor); +} + + +template +inline void +Tree::releaseAccessor(ValueAccessorBase& accessor) const +{ + mConstAccessorRegistry.erase(&accessor); +} + + +template +inline void +Tree::clearAllAccessors() +{ + for (typename AccessorRegistry::iterator it = mAccessorRegistry.begin(); + it != mAccessorRegistry.end(); ++it) + { + if (it->first) it->first->clear(); + } + + for (typename ConstAccessorRegistry::iterator it = mConstAccessorRegistry.begin(); + it != mConstAccessorRegistry.end(); ++it) + { + if (it->first) it->first->clear(); + } +} + + +template +inline void +Tree::releaseAllAccessors() +{ + mAccessorRegistry.erase(nullptr); + for (typename AccessorRegistry::iterator it = mAccessorRegistry.begin(); + it != mAccessorRegistry.end(); ++it) + { + it->first->release(); + } + mAccessorRegistry.clear(); + + mAccessorRegistry.erase(nullptr); + for (typename ConstAccessorRegistry::iterator it = mConstAccessorRegistry.begin(); + it != mConstAccessorRegistry.end(); ++it) + { + it->first->release(); + } + mConstAccessorRegistry.clear(); +} + + +//////////////////////////////////////// + + +template +inline const typename RootNodeType::ValueType& +Tree::getValue(const Coord& xyz) const +{ + return mRoot.getValue(xyz); +} + + +template +template +inline const typename RootNodeType::ValueType& +Tree::getValue(const Coord& xyz, AccessT& accessor) const +{ + return accessor.getValue(xyz); +} + + +template +inline int +Tree::getValueDepth(const Coord& xyz) const +{ + return mRoot.getValueDepth(xyz); +} + + +template +inline void +Tree::setValueOff(const Coord& xyz) +{ + mRoot.setValueOff(xyz); +} + + +template +inline void +Tree::setValueOff(const Coord& xyz, const ValueType& value) +{ + mRoot.setValueOff(xyz, value); +} + + +template +inline void +Tree::setActiveState(const Coord& xyz, bool on) +{ + mRoot.setActiveState(xyz, on); +} + + +template +inline void +Tree::setValue(const Coord& xyz, const ValueType& value) +{ + mRoot.setValueOn(xyz, value); +} + +template +inline void +Tree::setValueOnly(const Coord& xyz, const ValueType& value) +{ + mRoot.setValueOnly(xyz, value); +} + +template +template +inline void +Tree::setValue(const Coord& xyz, const ValueType& value, AccessT& accessor) +{ + accessor.setValue(xyz, value); +} + + +template +inline void +Tree::setValueOn(const Coord& xyz) +{ + mRoot.setActiveState(xyz, true); +} + + +template +inline void +Tree::setValueOn(const Coord& xyz, const ValueType& value) +{ + mRoot.setValueOn(xyz, value); +} + + +template +template +inline void +Tree::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + mRoot.modifyValue(xyz, op); +} + + +template +template +inline void +Tree::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + mRoot.modifyValueAndActiveState(xyz, op); +} + + +template +inline bool +Tree::probeValue(const Coord& xyz, ValueType& value) const +{ + return mRoot.probeValue(xyz, value); +} + + +//////////////////////////////////////// + + +template +inline void +Tree::addTile(Index level, const Coord& xyz, + const ValueType& value, bool active) +{ + mRoot.addTile(level, xyz, value, active); +} + + +template +template +inline NodeT* +Tree::stealNode(const Coord& xyz, const ValueType& value, bool active) +{ + this->clearAllAccessors(); + return mRoot.template stealNode(xyz, value, active); +} + + +template +inline typename RootNodeType::LeafNodeType* +Tree::touchLeaf(const Coord& xyz) +{ + return mRoot.touchLeaf(xyz); +} + + +template +inline typename RootNodeType::LeafNodeType* +Tree::probeLeaf(const Coord& xyz) +{ + return mRoot.probeLeaf(xyz); +} + + +template +inline const typename RootNodeType::LeafNodeType* +Tree::probeConstLeaf(const Coord& xyz) const +{ + return mRoot.probeConstLeaf(xyz); +} + + +template +template +inline NodeType* +Tree::probeNode(const Coord& xyz) +{ + return mRoot.template probeNode(xyz); +} + + +template +template +inline const NodeType* +Tree::probeNode(const Coord& xyz) const +{ + return this->template probeConstNode(xyz); +} + + +template +template +inline const NodeType* +Tree::probeConstNode(const Coord& xyz) const +{ + return mRoot.template probeConstNode(xyz); +} + + +//////////////////////////////////////// + + +template +inline void +Tree::clip(const CoordBBox& bbox) +{ + this->clearAllAccessors(); + return mRoot.clip(bbox); +} + + +template +inline void +Tree::clipUnallocatedNodes() +{ + this->clearAllAccessors(); + for (LeafIter it = this->beginLeaf(); it; ) { + const LeafNodeType* leaf = it.getLeaf(); + ++it; // advance the iterator before deleting the leaf node + if (!leaf->isAllocated()) { + this->addTile(/*level=*/0, leaf->origin(), this->background(), /*active=*/false); + } + } +} + +#if OPENVDB_ABI_VERSION_NUMBER >= 4 +template +inline Index32 +Tree::unallocatedLeafCount() const +{ + Index32 sum = 0; + for (auto it = this->cbeginLeaf(); it; ++it) if (!it->isAllocated()) ++sum; + return sum; +} +#endif + + +template +inline void +Tree::sparseFill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + this->clearAllAccessors(); + return mRoot.sparseFill(bbox, value, active); +} + + +template +inline void +Tree::denseFill(const CoordBBox& bbox, const ValueType& value, bool active) +{ + this->clearAllAccessors(); + return mRoot.denseFill(bbox, value, active); +} + + +template +inline void +Tree::voxelizeActiveTiles(bool threaded) +{ + this->clearAllAccessors(); + mRoot.voxelizeActiveTiles(threaded); +} + + +template +Metadata::Ptr +Tree::getBackgroundValue() const +{ + Metadata::Ptr result; + if (Metadata::isRegisteredType(valueType())) { + using MetadataT = TypedMetadata; + result = Metadata::createMetadata(valueType()); + if (result->typeName() == MetadataT::staticTypeName()) { + MetadataT* m = static_cast(result.get()); + m->value() = mRoot.background(); + } + } + return result; +} + + +//////////////////////////////////////// + + +template +inline void +Tree::merge(Tree& other, MergePolicy policy) +{ + this->clearAllAccessors(); + other.clearAllAccessors(); + switch (policy) { + case MERGE_ACTIVE_STATES: + mRoot.template merge(other.mRoot); break; + case MERGE_NODES: + mRoot.template merge(other.mRoot); break; + case MERGE_ACTIVE_STATES_AND_NODES: + mRoot.template merge(other.mRoot); break; + } +} + + +template +template +inline void +Tree::topologyUnion(const Tree& other) +{ + this->clearAllAccessors(); + mRoot.topologyUnion(other.root()); +} + +template +template +inline void +Tree::topologyIntersection(const Tree& other) +{ + this->clearAllAccessors(); + mRoot.topologyIntersection(other.root()); +} + +template +template +inline void +Tree::topologyDifference(const Tree& other) +{ + this->clearAllAccessors(); + mRoot.topologyDifference(other.root()); +} + +//////////////////////////////////////// + + +/// @brief Helper class to adapt a three-argument (a, b, result) CombineOp functor +/// into a single-argument functor that accepts a CombineArgs struct +template +struct CombineOpAdapter +{ + CombineOpAdapter(CombineOp& _op): op(_op) {} + + void operator()(CombineArgs& args) const { + op(args.a(), args.b(), args.result()); + } + + CombineOp& op; +}; + + +template +template +inline void +Tree::combine(Tree& other, CombineOp& op, bool prune) +{ + CombineOpAdapter extendedOp(op); + this->combineExtended(other, extendedOp, prune); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: aTree.combine(bTree, MyCombineOp(...)). +#ifndef _MSC_VER +template +template +inline void +Tree::combine(Tree& other, const CombineOp& op, bool prune) +{ + CombineOpAdapter extendedOp(op); + this->combineExtended(other, extendedOp, prune); +} +#endif + + +template +template +inline void +Tree::combineExtended(Tree& other, ExtendedCombineOp& op, bool prune) +{ + this->clearAllAccessors(); + mRoot.combine(other.root(), op, prune); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: aTree.combineExtended(bTree, MyCombineOp(...)). +#ifndef _MSC_VER +template +template +inline void +Tree::combineExtended(Tree& other, const ExtendedCombineOp& op, bool prune) +{ + this->clearAllAccessors(); + mRoot.template combine(other.mRoot, op, prune); +} +#endif + + +template +template +inline void +Tree::combine2(const Tree& a, const OtherTreeType& b, CombineOp& op, bool prune) +{ + CombineOpAdapter extendedOp(op); + this->combine2Extended(a, b, extendedOp, prune); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: tree.combine2(aTree, bTree, MyCombineOp(...)). +#ifndef _MSC_VER +template +template +inline void +Tree::combine2(const Tree& a, const OtherTreeType& b, const CombineOp& op, bool prune) +{ + CombineOpAdapter extendedOp(op); + this->combine2Extended(a, b, extendedOp, prune); +} +#endif + + +template +template +inline void +Tree::combine2Extended(const Tree& a, const OtherTreeType& b, + ExtendedCombineOp& op, bool prune) +{ + this->clearAllAccessors(); + mRoot.combine2(a.root(), b.root(), op, prune); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like the following, where the functor argument is a temporary: +/// tree.combine2Extended(aTree, bTree, MyCombineOp(...)). +#ifndef _MSC_VER +template +template +inline void +Tree::combine2Extended(const Tree& a, const OtherTreeType& b, + const ExtendedCombineOp& op, bool prune) +{ + this->clearAllAccessors(); + mRoot.template combine2(a.root(), b.root(), op, prune); +} +#endif + + +//////////////////////////////////////// + + +template +template +inline void +Tree::visit(VisitorOp& op) +{ + this->clearAllAccessors(); + mRoot.template visit(op); +} + + +template +template +inline void +Tree::visit(VisitorOp& op) const +{ + mRoot.template visit(op); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: tree.visit(MyVisitorOp(...)). +template +template +inline void +Tree::visit(const VisitorOp& op) +{ + this->clearAllAccessors(); + mRoot.template visit(op); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: tree.visit(MyVisitorOp(...)). +template +template +inline void +Tree::visit(const VisitorOp& op) const +{ + mRoot.template visit(op); +} + + +//////////////////////////////////////// + + +template +template +inline void +Tree::visit2(OtherTreeType& other, VisitorOp& op) +{ + this->clearAllAccessors(); + using OtherRootNodeType = typename OtherTreeType::RootNodeType; + mRoot.template visit2(other.root(), op); +} + + +template +template +inline void +Tree::visit2(OtherTreeType& other, VisitorOp& op) const +{ + using OtherRootNodeType = typename OtherTreeType::RootNodeType; + mRoot.template visit2(other.root(), op); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: aTree.visit2(bTree, MyVisitorOp(...)). +template +template +inline void +Tree::visit2(OtherTreeType& other, const VisitorOp& op) +{ + this->clearAllAccessors(); + using OtherRootNodeType = typename OtherTreeType::RootNodeType; + mRoot.template visit2(other.root(), op); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: aTree.visit2(bTree, MyVisitorOp(...)). +template +template +inline void +Tree::visit2(OtherTreeType& other, const VisitorOp& op) const +{ + using OtherRootNodeType = typename OtherTreeType::RootNodeType; + mRoot.template visit2(other.root(), op); +} + + +//////////////////////////////////////// + + +template +inline const Name& +Tree::treeType() +{ + static std::once_flag once; + std::call_once(once, []() + { + std::vector dims; + Tree::getNodeLog2Dims(dims); + std::ostringstream ostr; + ostr << "Tree_" << typeNameAsString(); + for (size_t i = 1, N = dims.size(); i < N; ++i) { // start from 1 to skip the RootNode + ostr << "_" << dims[i]; + } + sTreeTypeName.reset(new Name(ostr.str())); + }); + return *sTreeTypeName; +} + + +template +template +inline bool +Tree::hasSameTopology(const Tree& other) const +{ + return mRoot.hasSameTopology(other.root()); +} + + +template +Index64 +Tree::inactiveVoxelCount() const +{ + Coord dim(0, 0, 0); + this->evalActiveVoxelDim(dim); + const Index64 + totalVoxels = dim.x() * dim.y() * dim.z(), + activeVoxels = this->activeVoxelCount(); + assert(totalVoxels >= activeVoxels); + return totalVoxels - activeVoxels; +} + + +template +inline bool +Tree::evalLeafBoundingBox(CoordBBox& bbox) const +{ + bbox.reset(); // default invalid bbox + + if (this->empty()) return false; // empty + + mRoot.evalActiveBoundingBox(bbox, false); + + return true;// not empty +} + +template +inline bool +Tree::evalActiveVoxelBoundingBox(CoordBBox& bbox) const +{ + bbox.reset(); // default invalid bbox + + if (this->empty()) return false; // empty + + mRoot.evalActiveBoundingBox(bbox, true); + + return true;// not empty +} + + +template +inline bool +Tree::evalActiveVoxelDim(Coord& dim) const +{ + CoordBBox bbox; + bool notEmpty = this->evalActiveVoxelBoundingBox(bbox); + dim = bbox.extents(); + return notEmpty; +} + + +template +inline bool +Tree::evalLeafDim(Coord& dim) const +{ + CoordBBox bbox; + bool notEmpty = this->evalLeafBoundingBox(bbox); + dim = bbox.extents(); + return notEmpty; +} + + +template +inline void +Tree::evalMinMax(ValueType& minVal, ValueType& maxVal) const +{ + /// @todo optimize + minVal = maxVal = zeroVal(); + if (ValueOnCIter iter = this->cbeginValueOn()) { + minVal = maxVal = *iter; + for (++iter; iter; ++iter) { + const ValueType& val = *iter; + if (val < minVal) minVal = val; + if (val > maxVal) maxVal = val; + } + } +} + + +template +inline void +Tree::getNodeLog2Dims(std::vector& dims) +{ + dims.clear(); + RootNodeType::getNodeLog2Dims(dims); +} + + +template +inline void +Tree::print(std::ostream& os, int verboseLevel) const +{ + if (verboseLevel <= 0) return; + + /// @todo Consider using boost::io::ios_precision_saver instead. + struct OnExit { + std::ostream& os; + std::streamsize savedPrecision; + OnExit(std::ostream& _os): os(_os), savedPrecision(os.precision()) {} + ~OnExit() { os.precision(savedPrecision); } + }; + OnExit restorePrecision(os); + + std::vector dims; + Tree::getNodeLog2Dims(dims);// leaf is the last element + + os << "Information about Tree:\n" + << " Type: " << this->type() << "\n"; + + os << " Configuration:\n"; + + if (verboseLevel <= 1) { + // Print node types and sizes. + os << " Root(" << mRoot.getTableSize() << ")"; + if (dims.size() > 1) { + for (size_t i = 1, N = dims.size() - 1; i < N; ++i) { + os << ", Internal(" << (1 << dims[i]) << "^3)"; + } + os << ", Leaf(" << (1 << dims.back()) << "^3)\n"; + } + os << " Background value: " << mRoot.background() << "\n"; + return; + } + + // The following is tree information that is expensive to extract. + + ValueType minVal = zeroVal(), maxVal = zeroVal(); + if (verboseLevel > 3) { + // This forces loading of all non-resident nodes. + this->evalMinMax(minVal, maxVal); + } + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + const auto nodeCount = this->nodeCount();//fast + const Index32 leafCount = nodeCount.front();// leaf is the first element +#else + std::vector nodeCount(dims.size()); + for (NodeCIter it = cbeginNode(); it; ++it) ++(nodeCount[it.getDepth()]);//slow + const Index64 leafCount = *nodeCount.rbegin();// leaf is the last element +#endif + assert(dims.size() == nodeCount.size()); + + Index64 totalNodeCount = 0; + for (size_t i = 0; i < nodeCount.size(); ++i) totalNodeCount += nodeCount[i]; + + // Print node types, counts and sizes. + os << " Root(1 x " << mRoot.getTableSize() << ")"; + if (dims.size() >= 2) { + for (size_t i = 1, N = dims.size() - 1; i < N; ++i) { +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + os << ", Internal(" << util::formattedInt(nodeCount[N - i]); +#else + os << ", Internal(" << util::formattedInt(nodeCount[i]); +#endif + os << " x " << (1 << dims[i]) << "^3)"; + } + os << ", Leaf(" << util::formattedInt(leafCount); + os << " x " << (1 << dims.back()) << "^3)\n"; + } + os << " Background value: " << mRoot.background() << "\n"; + + // Statistics of topology and values + + if (verboseLevel > 3) { + os << " Min value: " << minVal << "\n"; + os << " Max value: " << maxVal << "\n"; + } + + const Index64 + numActiveVoxels = this->activeVoxelCount(), + numActiveLeafVoxels = this->activeLeafVoxelCount(), + numActiveTiles = this->activeTileCount(); + + os << " Number of active voxels: " << util::formattedInt(numActiveVoxels) << "\n"; + os << " Number of active tiles: " << util::formattedInt(numActiveTiles) << "\n"; + + Coord dim(0, 0, 0); + Index64 totalVoxels = 0; + if (numActiveVoxels) { // nonempty + CoordBBox bbox; + this->evalActiveVoxelBoundingBox(bbox); + dim = bbox.extents(); + totalVoxels = dim.x() * uint64_t(dim.y()) * dim.z(); + + os << " Bounding box of active voxels: " << bbox << "\n"; + os << " Dimensions of active voxels: " + << dim[0] << " x " << dim[1] << " x " << dim[2] << "\n"; + + const double activeRatio = (100.0 * double(numActiveVoxels)) / double(totalVoxels); + os << " Percentage of active voxels: " << std::setprecision(3) << activeRatio << "%\n"; + + if (leafCount > 0) { + const double fillRatio = (100.0 * double(numActiveLeafVoxels)) + / (double(leafCount) * double(LeafNodeType::NUM_VOXELS)); + os << " Average leaf node fill ratio: " << fillRatio << "%\n"; + } + + if (verboseLevel > 2) { + Index64 sum = 0;// count the number of unallocated leaf nodes + for (auto it = this->cbeginLeaf(); it; ++it) if (!it->isAllocated()) ++sum; + os << " Number of unallocated nodes: " + << util::formattedInt(sum) << " (" + << (100.0 * double(sum) / double(totalNodeCount)) << "%)\n"; + } + } else { + os << " Tree is empty!\n"; + } + os << std::flush; + + if (verboseLevel == 2) return; + + // Memory footprint in bytes + const Index64 + actualMem = this->memUsage(), + denseMem = sizeof(ValueType) * totalVoxels, + voxelsMem = sizeof(ValueType) * numActiveLeafVoxels; + ///< @todo not accurate for BoolTree (and probably should count tile values) + + os << "Memory footprint:\n"; + util::printBytes(os, actualMem, " Actual: "); + util::printBytes(os, voxelsMem, " Active leaf voxels: "); + + if (numActiveVoxels) { + util::printBytes(os, denseMem, " Dense equivalent: "); + os << " Actual footprint is " << (100.0 * double(actualMem) / double(denseMem)) + << "% of an equivalent dense volume\n"; + os << " Leaf voxel footprint is " << (100.0 * double(voxelsMem) / double(actualMem)) + << "% of actual footprint\n"; + } +} + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_TREE_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/TreeIterator.h b/openvdb/tree/TreeIterator.h new file mode 100644 index 00000000..cabb842c --- /dev/null +++ b/openvdb/tree/TreeIterator.h @@ -0,0 +1,1369 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file tree/TreeIterator.h + +#ifndef OPENVDB_TREE_TREEITERATOR_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_TREEITERATOR_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Prior to 0.96.1, depth-bounded value iterators always descended to the leaf level +// and iterated past leaf nodes. Now, they never descend past the maximum depth. +// Comment out the following line to restore the older, less-efficient behavior: +#define ENABLE_TREE_VALUE_DEPTH_BOUND_OPTIMIZATION + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +namespace iter { + +template +struct InvertedTree { + using SubtreeT = typename InvertedTree::Type; + using Type = typename boost::mpl::push_back::type; +}; +template +struct InvertedTree { + using Type = typename boost::mpl::vector::type; +}; + +} // namespace iter + + +//////////////////////////////////////// + + +/// IterTraits provides the following for iterators of the standard types, +/// i.e., for {Child,Value}{On,Off,All}{Iter,CIter}: +/// - a NodeConverter template to convert an iterator for one type of node +/// to an iterator of the same type for another type of node; for example, +/// IterTraits::NodeConverter::Type +/// is synonymous with LeafNode::ValueOnIter. +/// - a begin(node) function that returns a begin iterator for a node of arbitrary type; +/// for example, IterTraits::begin(leaf) returns +/// leaf.beginValueOn() +/// - a getChild() function that returns a pointer to the child node to which the iterator +/// is currently pointing (always null if the iterator is a Value iterator) +template +struct IterTraits +{ + template static ChildT* getChild(const IterT&) { return nullptr; } +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ChildOnIter; + static IterT begin(NodeT& node) { return node.beginChildOn(); } + template static ChildT* getChild(const IterT& iter) { + return &iter.getValue(); + } + template struct NodeConverter { + using Type = typename OtherNodeT::ChildOnIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ChildOnCIter; + static IterT begin(const NodeT& node) { return node.cbeginChildOn(); } + template static const ChildT* getChild(const IterT& iter) { + return &iter.getValue(); + } + template struct NodeConverter { + using Type = typename OtherNodeT::ChildOnCIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ChildOffIter; + static IterT begin(NodeT& node) { return node.beginChildOff(); } + template struct NodeConverter { + using Type = typename OtherNodeT::ChildOffIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ChildOffCIter; + static IterT begin(const NodeT& node) { return node.cbeginChildOff(); } + template struct NodeConverter { + using Type = typename OtherNodeT::ChildOffCIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ChildAllIter; + static IterT begin(NodeT& node) { return node.beginChildAll(); } + template static ChildT* getChild(const IterT& iter) { + typename IterT::NonConstValueType val; + return iter.probeChild(val); + } + template struct NodeConverter { + using Type = typename OtherNodeT::ChildAllIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ChildAllCIter; + static IterT begin(const NodeT& node) { return node.cbeginChildAll(); } + template static ChildT* getChild(const IterT& iter) { + typename IterT::NonConstValueType val; + return iter.probeChild(val); + } + template struct NodeConverter { + using Type = typename OtherNodeT::ChildAllCIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ValueOnIter; + static IterT begin(NodeT& node) { return node.beginValueOn(); } + template struct NodeConverter { + using Type = typename OtherNodeT::ValueOnIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ValueOnCIter; + static IterT begin(const NodeT& node) { return node.cbeginValueOn(); } + template struct NodeConverter { + using Type = typename OtherNodeT::ValueOnCIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ValueOffIter; + static IterT begin(NodeT& node) { return node.beginValueOff(); } + template struct NodeConverter { + using Type = typename OtherNodeT::ValueOffIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ValueOffCIter; + static IterT begin(const NodeT& node) { return node.cbeginValueOff(); } + template struct NodeConverter { + using Type = typename OtherNodeT::ValueOffCIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ValueAllIter; + static IterT begin(NodeT& node) { return node.beginValueAll(); } + template struct NodeConverter { + using Type = typename OtherNodeT::ValueAllIter; + }; +}; + +template +struct IterTraits +{ + using IterT = typename NodeT::ValueAllCIter; + static IterT begin(const NodeT& node) { return node.cbeginValueAll(); } + template struct NodeConverter { + using Type = typename OtherNodeT::ValueAllCIter; + }; +}; + + +//////////////////////////////////////// + + +/// @brief An IterListItem is an element of a compile-time linked list of iterators +/// to nodes of different types. +/// +/// The list is constructed by traversing the template hierarchy of a Tree in reverse order, +/// so typically the elements will be a LeafNode iterator of some type (e.g., ValueOnCIter), +/// followed by one or more InternalNode iterators of the same type, followed by a RootNode +/// iterator of the same type. +/// +/// The length of the list is fixed at compile time, and because it is implemented using +/// nested, templated classes, much of the list traversal logic can be optimized away. +template +class IterListItem +{ +public: + /// The type of iterator stored in the previous list item + using PrevIterT = typename PrevItemT::IterT; + /// The type of node (non-const) whose iterator is stored in this list item + using _NodeT = typename boost::mpl::front::type; + /// The type of iterator stored in this list item (e.g., InternalNode::ValueOnCIter) + using IterT = typename IterTraits::template + NodeConverter<_NodeT>::Type; + + /// The type of node (const or non-const) over which IterT iterates (e.g., const RootNode<...>) + using NodeT = typename IterT::NodeType; + /// The type of the node with const qualifiers removed ("Non-Const") + using NCNodeT = typename IterT::NonConstNodeType; + /// The type of value (with const qualifiers removed) to which the iterator points + using NCValueT = typename IterT::NonConstValueType; + /// NodeT's child node type, with the same constness (e.g., const InternalNode<...>) + using ChildT = typename CopyConstness::Type; + /// NodeT's child node type with const qualifiers removed + using NCChildT = typename CopyConstness::Type; + using ITraits = IterTraits; + /// NodeT's level in its tree (0 = LeafNode) + static const Index Level = _Level; + + IterListItem(PrevItemT* prev): mNext(this), mPrev(prev) {} + + IterListItem(const IterListItem& other): + mIter(other.mIter), mNext(other.mNext), mPrev(nullptr) {} + IterListItem& operator=(const IterListItem& other) + { + if (&other != this) { + mIter = other.mIter; + mNext = other.mNext; + mPrev = nullptr; ///< @note external call to updateBackPointers() required + } + return *this; + } + + void updateBackPointers(PrevItemT* prev) { mPrev = prev; mNext.updateBackPointers(this); } + + void setIter(const IterT& iter) { mIter = iter; } + template + void setIter(const OtherIterT& iter) { mNext.setIter(iter); } + + /// Return the node over which this list element's iterator iterates. + void getNode(Index lvl, NodeT*& node) const + { + node = (lvl <= Level) ? mIter.getParentNode() : nullptr; + } + /// Return the node over which one of the following list elements' iterator iterates. + template + void getNode(Index lvl, OtherNodeT*& node) const { mNext.getNode(lvl, node); } + + /// @brief Initialize the iterator for level @a lvl of the tree with the node + /// over which the corresponding iterator of @a otherListItem is iterating. + /// + /// For example, if @a otherListItem contains a LeafNode::ValueOnIter, + /// initialize this list's leaf iterator with the same LeafNode. + template + void initLevel(Index lvl, OtherIterListItemT& otherListItem) + { + if (lvl == Level) { + const NodeT* node = nullptr; + otherListItem.getNode(lvl, node); + mIter = (node == nullptr) ? IterT() : ITraits::begin(*const_cast(node)); + } else { + // Forward to one of the following list elements. + mNext.initLevel(lvl, otherListItem); + } + } + + /// Return The table offset of the iterator at level @a lvl of the tree. + Index pos(Index lvl) const { return (lvl == Level) ? mIter.pos() : mNext.pos(lvl); } + + /// Return @c true if the iterator at level @a lvl of the tree has not yet reached its end. + bool test(Index lvl) const { return (lvl == Level) ? mIter.test() : mNext.test(lvl); } + + /// Increment the iterator at level @a lvl of the tree. + bool next(Index lvl) { return (lvl == Level) ? mIter.next() : mNext.next(lvl); } + + /// @brief If the iterator at level @a lvl of the tree points to a child node, + /// initialize the next iterator in this list with that child node. + bool down(Index lvl) + { + if (lvl == Level && mPrev != nullptr && mIter) { + if (ChildT* child = ITraits::template getChild(mIter)) { + mPrev->setIter(PrevItemT::ITraits::begin(*child)); + return true; + } + } + return (lvl > Level) ? mNext.down(lvl) : false; + } + + /// @brief Return the global coordinates of the voxel or tile to which the iterator + /// at level @a lvl of the tree is currently pointing. + Coord getCoord(Index lvl) const + { + return (lvl == Level) ? mIter.getCoord() : mNext.getCoord(lvl); + } + Index getChildDim(Index lvl) const + { + return (lvl == Level) ? NodeT::getChildDim() : mNext.getChildDim(lvl); + } + /// Return the number of (virtual) voxels spanned by a tile value or child node + Index64 getVoxelCount(Index lvl) const + { + return (lvl == Level) ? ChildT::NUM_VOXELS : mNext.getVoxelCount(lvl); + } + + /// Return @c true if the iterator at level @a lvl of the tree points to an active value. + bool isValueOn(Index lvl) const + { + return (lvl == Level) ? mIter.isValueOn() : mNext.isValueOn(lvl); + } + + /// Return the value to which the iterator at level @a lvl of the tree points. + const NCValueT& getValue(Index lvl) const + { + if (lvl == Level) return mIter.getValue(); + return mNext.getValue(lvl); + } + + /// @brief Set the value (to @a val) to which the iterator at level @a lvl + /// of the tree points and mark the value as active. + /// @note Not valid when @c IterT is a const iterator type + void setValue(Index lvl, const NCValueT& val) const + { + if (lvl == Level) mIter.setValue(val); else mNext.setValue(lvl, val); + } + /// @brief Set the value (to @a val) to which the iterator at level @a lvl of the tree + /// points and mark the value as active if @a on is @c true, or inactive otherwise. + /// @note Not valid when @c IterT is a const iterator type + void setValueOn(Index lvl, bool on = true) const + { + if (lvl == Level) mIter.setValueOn(on); else mNext.setValueOn(lvl, on); + } + /// @brief Mark the value to which the iterator at level @a lvl of the tree points + /// as inactive. + /// @note Not valid when @c IterT is a const iterator type + void setValueOff(Index lvl) const + { + if (lvl == Level) mIter.setValueOff(); else mNext.setValueOff(lvl); + } + + /// @brief Apply a functor to the item to which this iterator is pointing. + /// @note Not valid when @c IterT is a const iterator type + template + void modifyValue(Index lvl, const ModifyOp& op) const + { + if (lvl == Level) mIter.modifyValue(op); else mNext.modifyValue(lvl, op); + } + +private: + using RestT = typename boost::mpl::pop_front::type; // NodeVecT minus its first item + using NextItem = IterListItem; + + IterT mIter; + NextItem mNext; + PrevItemT* mPrev; +}; + + +/// The initial element of a compile-time linked list of iterators to nodes of different types +template +class IterListItem +{ +public: + /// The type of iterator stored in the previous list item + using PrevIterT = typename PrevItemT::IterT; + /// The type of node (non-const) whose iterator is stored in this list item + using _NodeT = typename boost::mpl::front::type; + /// The type of iterator stored in this list item (e.g., InternalNode::ValueOnCIter) + using IterT = typename IterTraits::template + NodeConverter<_NodeT>::Type; + + /// The type of node (const or non-const) over which IterT iterates (e.g., const RootNode<...>) + using NodeT = typename IterT::NodeType; + /// The type of the node with const qualifiers removed ("Non-Const") + using NCNodeT = typename IterT::NonConstNodeType; + /// The type of value (with const qualifiers removed) to which the iterator points + using NCValueT = typename IterT::NonConstValueType; + using ITraits = IterTraits; + /// NodeT's level in its tree (0 = LeafNode) + static const Index Level = 0; + + IterListItem(PrevItemT*): mNext(this), mPrev(nullptr) {} + + IterListItem(const IterListItem& other): + mIter(other.mIter), mNext(other.mNext), mPrev(nullptr) {} + IterListItem& operator=(const IterListItem& other) + { + if (&other != this) { + mIter = other.mIter; + mNext = other.mNext; + mPrev = nullptr; + } + return *this; + } + + void updateBackPointers(PrevItemT* = nullptr) + { + mPrev = nullptr; mNext.updateBackPointers(this); + } + + void setIter(const IterT& iter) { mIter = iter; } + template + void setIter(const OtherIterT& iter) { mNext.setIter(iter); } + + void getNode(Index lvl, NodeT*& node) const + { + node = (lvl == 0) ? mIter.getParentNode() : nullptr; + } + template + void getNode(Index lvl, OtherNodeT*& node) const { mNext.getNode(lvl, node); } + + template + void initLevel(Index lvl, OtherIterListItemT& otherListItem) + { + if (lvl == 0) { + const NodeT* node = nullptr; + otherListItem.getNode(lvl, node); + mIter = (node == nullptr) ? IterT() : ITraits::begin(*const_cast(node)); + } else { + mNext.initLevel(lvl, otherListItem); + } + } + + Index pos(Index lvl) const { return (lvl == 0) ? mIter.pos() : mNext.pos(lvl); } + + bool test(Index lvl) const { return (lvl == 0) ? mIter.test() : mNext.test(lvl); } + + bool next(Index lvl) { return (lvl == 0) ? mIter.next() : mNext.next(lvl); } + + bool down(Index lvl) { return (lvl == 0) ? false : mNext.down(lvl); } + + Coord getCoord(Index lvl) const + { + return (lvl == 0) ? mIter.getCoord() : mNext.getCoord(lvl); + } + Index getChildDim(Index lvl) const + { + return (lvl == 0) ? NodeT::getChildDim() : mNext.getChildDim(lvl); + } + + Index64 getVoxelCount(Index lvl) const + { + return (lvl == 0) ? 1 : mNext.getVoxelCount(lvl); + } + + bool isValueOn(Index lvl) const + { + return (lvl == 0) ? mIter.isValueOn() : mNext.isValueOn(lvl); + } + + const NCValueT& getValue(Index lvl) const + { + if (lvl == 0) return mIter.getValue(); + return mNext.getValue(lvl); + } + + void setValue(Index lvl, const NCValueT& val) const + { + if (lvl == 0) mIter.setValue(val); else mNext.setValue(lvl, val); + } + void setValueOn(Index lvl, bool on = true) const + { + if (lvl == 0) mIter.setValueOn(on); else mNext.setValueOn(lvl, on); + } + void setValueOff(Index lvl) const + { + if (lvl == 0) mIter.setValueOff(); else mNext.setValueOff(lvl); + } + + template + void modifyValue(Index lvl, const ModifyOp& op) const + { + if (lvl == 0) mIter.modifyValue(op); else mNext.modifyValue(lvl, op); + } + +private: + using RestT = typename boost::mpl::pop_front::type; // NodeVecT minus its first item + using NextItem = IterListItem; + + IterT mIter; + NextItem mNext; + PrevItemT* mPrev; +}; + + +/// The final element of a compile-time linked list of iterators to nodes of different types +template +class IterListItem +{ +public: + using _NodeT = typename boost::mpl::front::type; + /// The type of iterator stored in the previous list item + using PrevIterT = typename PrevItemT::IterT; + /// The type of iterator stored in this list item (e.g., RootNode::ValueOnCIter) + using IterT = typename IterTraits::template + NodeConverter<_NodeT>::Type; + + /// The type of node over which IterT iterates (e.g., const RootNode<...>) + using NodeT = typename IterT::NodeType; + /// The type of the node with const qualifiers removed ("Non-Const") + using NCNodeT = typename IterT::NonConstNodeType; + /// The type of value (with const qualifiers removed) to which the iterator points + using NCValueT = typename IterT::NonConstValueType; + /// NodeT's child node type, with the same constness (e.g., const InternalNode<...>) + using ChildT = typename CopyConstness::Type; + /// NodeT's child node type with const qualifiers removed + using NCChildT = typename CopyConstness::Type; + using ITraits = IterTraits; + /// NodeT's level in its tree (0 = LeafNode) + static const Index Level = _Level; + + IterListItem(PrevItemT* prev): mPrev(prev) {} + + IterListItem(const IterListItem& other): mIter(other.mIter), mPrev(nullptr) {} + IterListItem& operator=(const IterListItem& other) + { + if (&other != this) { + mIter = other.mIter; + mPrev = nullptr; ///< @note external call to updateBackPointers() required + } + return *this; + } + + void updateBackPointers(PrevItemT* prev) { mPrev = prev; } + + // The following method specializations differ from the default template + // implementations mainly in that they don't forward. + + void setIter(const IterT& iter) { mIter = iter; } + + void getNode(Index lvl, NodeT*& node) const + { + node = (lvl <= Level) ? mIter.getParentNode() : nullptr; + } + + template + void initLevel(Index lvl, OtherIterListItemT& otherListItem) + { + if (lvl == Level) { + const NodeT* node = nullptr; + otherListItem.getNode(lvl, node); + mIter = (node == nullptr) ? IterT() : ITraits::begin(*const_cast(node)); + } + } + + Index pos(Index lvl) const { return (lvl == Level) ? mIter.pos() : Index(-1); } + + bool test(Index lvl) const { return (lvl == Level) ? mIter.test() : false; } + + bool next(Index lvl) { return (lvl == Level) ? mIter.next() : false; } + + bool down(Index lvl) + { + if (lvl == Level && mPrev != nullptr && mIter) { + if (ChildT* child = ITraits::template getChild(mIter)) { + mPrev->setIter(PrevItemT::ITraits::begin(*child)); + return true; + } + } + return false; + } + + Coord getCoord(Index lvl) const { return (lvl == Level) ? mIter.getCoord() : Coord(); } + Index getChildDim(Index lvl) const { return (lvl == Level) ? NodeT::getChildDim() : 0; } + Index64 getVoxelCount(Index lvl) const { return (lvl == Level) ? ChildT::NUM_VOXELS : 0; } + + bool isValueOn(Index lvl) const { return (lvl == Level) ? mIter.isValueOn() : false; } + + const NCValueT& getValue(Index lvl) const + { + assert(lvl == Level); + (void)lvl; // avoid unused variable warning in optimized builds + return mIter.getValue(); + } + + void setValue(Index lvl, const NCValueT& val) const { if (lvl == Level) mIter.setValue(val); } + void setValueOn(Index lvl, bool on = true) const { if (lvl == Level) mIter.setValueOn(on); } + void setValueOff(Index lvl) const { if (lvl == Level) mIter.setValueOff(); } + + template + void modifyValue(Index lvl, const ModifyOp& op) const + { + if (lvl == Level) mIter.modifyValue(op); + } + +private: + IterT mIter; + PrevItemT* mPrev; +}; + + +//////////////////////////////////////// + + +//#define DEBUG_TREE_VALUE_ITERATOR + +/// @brief Base class for tree-traversal iterators over tile and voxel values +template +class TreeValueIteratorBase +{ +public: + using TreeT = _TreeT; + using ValueIterT = _ValueIterT; + using NodeT = typename ValueIterT::NodeType; + using ValueT = typename ValueIterT::NonConstValueType; + using ChildOnIterT = typename NodeT::ChildOnCIter; + static const Index ROOT_LEVEL = NodeT::LEVEL; + static_assert(ValueIterT::NodeType::LEVEL == ROOT_LEVEL, "invalid value iterator node type"); + static const Index LEAF_LEVEL = 0, ROOT_DEPTH = 0, LEAF_DEPTH = ROOT_LEVEL; + + TreeValueIteratorBase(TreeT&); + + TreeValueIteratorBase(const TreeValueIteratorBase& other); + TreeValueIteratorBase& operator=(const TreeValueIteratorBase& other); + + /// Specify the depth of the highest level of the tree to which to ascend (depth 0 = root). + void setMinDepth(Index minDepth); + /// Return the depth of the highest level of the tree to which this iterator ascends. + Index getMinDepth() const { return ROOT_LEVEL - Index(mMaxLevel); } + /// Specify the depth of the lowest level of the tree to which to descend (depth 0 = root). + void setMaxDepth(Index maxDepth); + /// Return the depth of the lowest level of the tree to which this iterator ascends. + Index getMaxDepth() const { return ROOT_LEVEL - Index(mMinLevel); } + + //@{ + /// Return @c true if this iterator is not yet exhausted. + bool test() const { return mValueIterList.test(mLevel); } + operator bool() const { return this->test(); } + //@} + + /// @brief Advance to the next tile or voxel value. + /// Return @c true if this iterator is not yet exhausted. + bool next(); + /// Advance to the next tile or voxel value. + TreeValueIteratorBase& operator++() { this->next(); return *this; } + + /// @brief Return the level in the tree (0 = leaf) of the node to which + /// this iterator is currently pointing. + Index getLevel() const { return mLevel; } + /// @brief Return the depth in the tree (0 = root) of the node to which + /// this iterator is currently pointing. + Index getDepth() const { return ROOT_LEVEL - mLevel; } + static Index getLeafDepth() { return LEAF_DEPTH; } + + /// @brief Return in @a node a pointer to the node over which this iterator is + /// currently iterating or one of that node's parents, as determined by @a NodeType. + /// @return a null pointer if @a NodeType specifies a node at a lower level + /// of the tree than that given by getLevel(). + template + void getNode(NodeType*& node) const { mValueIterList.getNode(mLevel, node); } + + /// @brief Return the global coordinates of the voxel or tile to which + /// this iterator is currently pointing. + Coord getCoord() const { return mValueIterList.getCoord(mLevel); } + /// @brief Return in @a bbox the axis-aligned bounding box of + /// the voxel or tile to which this iterator is currently pointing. + /// @return false if the bounding box is empty. + bool getBoundingBox(CoordBBox&) const; + /// @brief Return the axis-aligned bounding box of the voxel or tile to which + /// this iterator is currently pointing. + CoordBBox getBoundingBox() const { CoordBBox b; this->getBoundingBox(b); return b; } + + /// Return the number of (virtual) voxels corresponding to the value + Index64 getVoxelCount() const { return mValueIterList.getVoxelCount(mLevel);} + + /// Return @c true if this iterator is currently pointing to a (non-leaf) tile value. + bool isTileValue() const { return mLevel != 0 && this->test(); } + /// Return @c true if this iterator is currently pointing to a (leaf) voxel value. + bool isVoxelValue() const { return mLevel == 0 && this->test(); } + /// Return @c true if the value to which this iterator is currently pointing is active. + bool isValueOn() const { return mValueIterList.isValueOn(mLevel); } + + //@{ + /// Return the tile or voxel value to which this iterator is currently pointing. + const ValueT& getValue() const { return mValueIterList.getValue(mLevel); } + const ValueT& operator*() const { return this->getValue(); } + const ValueT* operator->() const { return &(this->operator*()); } + //@} + + /// @brief Change the tile or voxel value to which this iterator is currently pointing + /// and mark it as active. + void setValue(const ValueT& val) const { mValueIterList.setValue(mLevel, val); } + /// @brief Change the active/inactive state of the tile or voxel value to which + /// this iterator is currently pointing. + void setActiveState(bool on) const { mValueIterList.setValueOn(mLevel, on); } + /// Mark the tile or voxel value to which this iterator is currently pointing as inactive. + void setValueOff() const { mValueIterList.setValueOff(mLevel); } + + /// @brief Apply a functor to the item to which this iterator is pointing. + /// (Not valid for const iterators.) + /// @param op a functor of the form void op(ValueType&) const that modifies + /// its argument in place + /// @see Tree::modifyValue() + template + void modifyValue(const ModifyOp& op) const { mValueIterList.modifyValue(mLevel, op); } + + /// Return a pointer to the tree over which this iterator is iterating. + TreeT* getTree() const { return mTree; } + + /// Return a string (for debugging, mainly) describing this iterator's current state. + std::string summary() const; + +private: + bool advance(bool dontIncrement = false); + + using InvTreeT = typename iter::InvertedTree::Type; + struct PrevChildItem { using IterT = ChildOnIterT; }; + struct PrevValueItem { using IterT = ValueIterT; }; + + IterListItem mChildIterList; + IterListItem mValueIterList; + Index mLevel; + int mMinLevel, mMaxLevel; + TreeT* mTree; +}; // class TreeValueIteratorBase + + +template +inline +TreeValueIteratorBase::TreeValueIteratorBase(TreeT& tree): + mChildIterList(nullptr), + mValueIterList(nullptr), + mLevel(ROOT_LEVEL), + mMinLevel(int(LEAF_LEVEL)), + mMaxLevel(int(ROOT_LEVEL)), + mTree(&tree) +{ + mChildIterList.setIter(IterTraits::begin(tree.root())); + mValueIterList.setIter(IterTraits::begin(tree.root())); + this->advance(/*dontIncrement=*/true); +} + + +template +inline +TreeValueIteratorBase::TreeValueIteratorBase(const TreeValueIteratorBase& other): + mChildIterList(other.mChildIterList), + mValueIterList(other.mValueIterList), + mLevel(other.mLevel), + mMinLevel(other.mMinLevel), + mMaxLevel(other.mMaxLevel), + mTree(other.mTree) +{ + mChildIterList.updateBackPointers(); + mValueIterList.updateBackPointers(); +} + + +template +inline TreeValueIteratorBase& +TreeValueIteratorBase::operator=(const TreeValueIteratorBase& other) +{ + if (&other != this) { + mChildIterList = other.mChildIterList; + mValueIterList = other.mValueIterList; + mLevel = other.mLevel; + mMinLevel = other.mMinLevel; + mMaxLevel = other.mMaxLevel; + mTree = other.mTree; + mChildIterList.updateBackPointers(); + mValueIterList.updateBackPointers(); + } + return *this; +} + + +template +inline void +TreeValueIteratorBase::setMinDepth(Index minDepth) +{ + mMaxLevel = int(ROOT_LEVEL - minDepth); // level = ROOT_LEVEL - depth + if (int(mLevel) > mMaxLevel) this->next(); +} + + +template +inline void +TreeValueIteratorBase::setMaxDepth(Index maxDepth) +{ + // level = ROOT_LEVEL - depth + mMinLevel = int(ROOT_LEVEL - std::min(maxDepth, this->getLeafDepth())); + if (int(mLevel) < mMinLevel) this->next(); +} + + +template +inline bool +TreeValueIteratorBase::next() +{ + do { + if (!this->advance()) return false; + } while (int(mLevel) < mMinLevel || int(mLevel) > mMaxLevel); + return true; +} + + +template +inline bool +TreeValueIteratorBase::advance(bool dontIncrement) +{ + bool recurse = false; + do { + recurse = false; + Index + vPos = mValueIterList.pos(mLevel), + cPos = mChildIterList.pos(mLevel); + if (vPos == cPos && mChildIterList.test(mLevel)) { + /// @todo Once ValueOff iterators properly skip child pointers, remove this block. + mValueIterList.next(mLevel); + vPos = mValueIterList.pos(mLevel); + } + if (vPos < cPos) { + if (dontIncrement) return true; + if (mValueIterList.next(mLevel)) { + if (mValueIterList.pos(mLevel) == cPos && mChildIterList.test(mLevel)) { + /// @todo Once ValueOff iterators properly skip child pointers, + /// remove this block. + mValueIterList.next(mLevel); + } + // If there is a next value and it precedes the next child, return. + if (mValueIterList.pos(mLevel) < cPos) return true; + } + } else { + // Advance to the next child, which may or may not precede the next value. + if (!dontIncrement) mChildIterList.next(mLevel); + } +#ifdef DEBUG_TREE_VALUE_ITERATOR + std::cout << "\n" << this->summary() << std::flush; +#endif + + // Descend to the lowest level at which the next value precedes the next child. + while (mChildIterList.pos(mLevel) < mValueIterList.pos(mLevel)) { +#ifdef ENABLE_TREE_VALUE_DEPTH_BOUND_OPTIMIZATION + if (int(mLevel) == mMinLevel) { + // If the current node lies at the lowest allowed level, none of its + // children can be visited, so just advance its child iterator. + mChildIterList.next(mLevel); + if (mValueIterList.pos(mLevel) == mChildIterList.pos(mLevel) + && mChildIterList.test(mLevel)) + { + /// @todo Once ValueOff iterators properly skip child pointers, + /// remove this block. + mValueIterList.next(mLevel); + } + } else +#endif + if (mChildIterList.down(mLevel)) { + --mLevel; // descend one level + mValueIterList.initLevel(mLevel, mChildIterList); + if (mValueIterList.pos(mLevel) == mChildIterList.pos(mLevel) + && mChildIterList.test(mLevel)) + { + /// @todo Once ValueOff iterators properly skip child pointers, + /// remove this block. + mValueIterList.next(mLevel); + } + } else break; +#ifdef DEBUG_TREE_VALUE_ITERATOR + std::cout << "\n" << this->summary() << std::flush; +#endif + } + // Ascend to the nearest level at which one of the iterators is not yet exhausted. + while (!mChildIterList.test(mLevel) && !mValueIterList.test(mLevel)) { + if (mLevel == ROOT_LEVEL) return false; + ++mLevel; + mChildIterList.next(mLevel); + dontIncrement = true; + recurse = true; + } + } while (recurse); + return true; +} + + +template +inline bool +TreeValueIteratorBase::getBoundingBox(CoordBBox& bbox) const +{ + if (!this->test()) { + bbox = CoordBBox(); + return false; + } + bbox.min() = mValueIterList.getCoord(mLevel); + bbox.max() = bbox.min().offsetBy(mValueIterList.getChildDim(mLevel) - 1); + return true; +} + + +template +inline std::string +TreeValueIteratorBase::summary() const +{ + std::ostringstream ostr; + for (int lvl = int(ROOT_LEVEL); lvl >= 0 && lvl >= int(mLevel); --lvl) { + if (lvl == 0) ostr << "leaf"; + else if (lvl == int(ROOT_LEVEL)) ostr << "root"; + else ostr << "int" << (ROOT_LEVEL - lvl); + ostr << " v" << mValueIterList.pos(lvl) + << " c" << mChildIterList.pos(lvl); + if (lvl > int(mLevel)) ostr << " / "; + } + if (this->test() && mValueIterList.pos(mLevel) < mChildIterList.pos(mLevel)) { + if (mLevel == 0) { + ostr << " " << this->getCoord(); + } else { + ostr << " " << this->getBoundingBox(); + } + } + return ostr.str(); +} + + +//////////////////////////////////////// + + +/// @brief Base class for tree-traversal iterators over all nodes +template +class NodeIteratorBase +{ +public: + using TreeT = _TreeT; + using RootIterT = RootChildOnIterT; + using RootNodeT = typename RootIterT::NodeType; + using NCRootNodeT = typename RootIterT::NonConstNodeType; + static const Index ROOT_LEVEL = RootNodeT::LEVEL; + using InvTreeT = typename iter::InvertedTree::Type; + static const Index LEAF_LEVEL = 0, ROOT_DEPTH = 0, LEAF_DEPTH = ROOT_LEVEL; + + using RootIterTraits = IterTraits; + + NodeIteratorBase(); + NodeIteratorBase(TreeT&); + + NodeIteratorBase(const NodeIteratorBase& other); + NodeIteratorBase& operator=(const NodeIteratorBase& other); + + /// Specify the depth of the highest level of the tree to which to ascend (depth 0 = root). + void setMinDepth(Index minDepth); + /// Return the depth of the highest level of the tree to which this iterator ascends. + Index getMinDepth() const { return ROOT_LEVEL - Index(mMaxLevel); } + /// Specify the depth of the lowest level of the tree to which to descend (depth 0 = root). + void setMaxDepth(Index maxDepth); + /// Return the depth of the lowest level of the tree to which this iterator ascends. + Index getMaxDepth() const { return ROOT_LEVEL - Index(mMinLevel); } + + //@{ + /// Return @c true if this iterator is not yet exhausted. + bool test() const { return !mDone; } + operator bool() const { return this->test(); } + //@} + + /// @brief Advance to the next tile or voxel value. + /// @return @c true if this iterator is not yet exhausted. + bool next(); + /// Advance the iterator to the next leaf node. + void increment() { this->next(); } + NodeIteratorBase& operator++() { this->increment(); return *this; } + /// Increment the iterator n times. + void increment(Index n) { for (Index i = 0; i < n && this->next(); ++i) {} } + + /// @brief Return the level in the tree (0 = leaf) of the node to which + /// this iterator is currently pointing. + Index getLevel() const { return mLevel; } + /// @brief Return the depth in the tree (0 = root) of the node to which + /// this iterator is currently pointing. + Index getDepth() const { return ROOT_LEVEL - mLevel; } + static Index getLeafDepth() { return LEAF_DEPTH; } + + /// @brief Return the global coordinates of the voxel or tile to which + /// this iterator is currently pointing. + Coord getCoord() const; + /// @brief Return in @a bbox the axis-aligned bounding box of + /// the voxel or tile to which this iterator is currently pointing. + /// @return false if the bounding box is empty. + bool getBoundingBox(CoordBBox& bbox) const; + /// @brief Return the axis-aligned bounding box of the voxel or tile to which + /// this iterator is currently pointing. + CoordBBox getBoundingBox() const { CoordBBox b; this->getBoundingBox(b); return b; } + + //@{ + /// @brief Return the node to which the iterator is pointing. + /// @note This iterator doesn't have the usual dereference operators (* and ->), + /// because they would have to be overloaded by the returned node type. + template + void getNode(NodeT*& node) const { node = nullptr; mIterList.getNode(mLevel, node); } + template + void getNode(const NodeT*& node) const { node = nullptr; mIterList.getNode(mLevel, node); } + //@} + + TreeT* getTree() const { return mTree; } + + std::string summary() const; + +private: + struct PrevItem { using IterT = RootIterT; }; + + IterListItem mIterList; + Index mLevel; + int mMinLevel, mMaxLevel; + bool mDone; + TreeT* mTree; +}; // class NodeIteratorBase + + +template +inline +NodeIteratorBase::NodeIteratorBase(): + mIterList(nullptr), + mLevel(ROOT_LEVEL), + mMinLevel(int(LEAF_LEVEL)), + mMaxLevel(int(ROOT_LEVEL)), + mDone(true), + mTree(nullptr) +{ +} + + +template +inline +NodeIteratorBase::NodeIteratorBase(TreeT& tree): + mIterList(nullptr), + mLevel(ROOT_LEVEL), + mMinLevel(int(LEAF_LEVEL)), + mMaxLevel(int(ROOT_LEVEL)), + mDone(false), + mTree(&tree) +{ + mIterList.setIter(RootIterTraits::begin(tree.root())); +} + + +template +inline +NodeIteratorBase::NodeIteratorBase(const NodeIteratorBase& other): + mIterList(other.mIterList), + mLevel(other.mLevel), + mMinLevel(other.mMinLevel), + mMaxLevel(other.mMaxLevel), + mDone(other.mDone), + mTree(other.mTree) +{ + mIterList.updateBackPointers(); +} + + +template +inline NodeIteratorBase& +NodeIteratorBase::operator=(const NodeIteratorBase& other) +{ + if (&other != this) { + mLevel = other.mLevel; + mMinLevel = other.mMinLevel; + mMaxLevel = other.mMaxLevel; + mDone = other.mDone; + mTree = other.mTree; + mIterList = other.mIterList; + mIterList.updateBackPointers(); + } + return *this; +} + + +template +inline void +NodeIteratorBase::setMinDepth(Index minDepth) +{ + mMaxLevel = int(ROOT_LEVEL - minDepth); // level = ROOT_LEVEL - depth + if (int(mLevel) > mMaxLevel) this->next(); +} + + +template +inline void +NodeIteratorBase::setMaxDepth(Index maxDepth) +{ + // level = ROOT_LEVEL - depth + mMinLevel = int(ROOT_LEVEL - std::min(maxDepth, this->getLeafDepth())); + if (int(mLevel) < mMinLevel) this->next(); +} + + +template +inline bool +NodeIteratorBase::next() +{ + do { + if (mDone) return false; + + // If the iterator over the current node points to a child, + // descend to the child (depth-first traversal). + if (int(mLevel) > mMinLevel && mIterList.test(mLevel)) { + if (!mIterList.down(mLevel)) return false; + --mLevel; + } else { + // Ascend to the nearest ancestor that has other children. + while (!mIterList.test(mLevel)) { + if (mLevel == ROOT_LEVEL) { + // Can't ascend higher than the root. + mDone = true; + return false; + } + ++mLevel; // ascend one level + mIterList.next(mLevel); // advance to the next child, if there is one + } + // Descend to the child. + if (!mIterList.down(mLevel)) return false; + --mLevel; + } + } while (int(mLevel) < mMinLevel || int(mLevel) > mMaxLevel); + return true; +} + + +template +inline Coord +NodeIteratorBase::getCoord() const +{ + if (mLevel != ROOT_LEVEL) return mIterList.getCoord(mLevel + 1); + RootNodeT* root = nullptr; + this->getNode(root); + return root ? root->getMinIndex() : Coord::min(); +} + + +template +inline bool +NodeIteratorBase::getBoundingBox(CoordBBox& bbox) const +{ + if (mLevel == ROOT_LEVEL) { + RootNodeT* root = nullptr; + this->getNode(root); + if (root == nullptr) { + bbox = CoordBBox(); + return false; + } + root->getIndexRange(bbox); + return true; + } + bbox.min() = mIterList.getCoord(mLevel + 1); + bbox.max() = bbox.min().offsetBy(mIterList.getChildDim(mLevel + 1) - 1); + return true; +} + + +template +inline std::string +NodeIteratorBase::summary() const +{ + std::ostringstream ostr; + for (int lvl = int(ROOT_LEVEL); lvl >= 0 && lvl >= int(mLevel); --lvl) { + if (lvl == 0) ostr << "leaf"; + else if (lvl == int(ROOT_LEVEL)) ostr << "root"; + else ostr << "int" << (ROOT_LEVEL - lvl); + ostr << " c" << mIterList.pos(lvl); + if (lvl > int(mLevel)) ostr << " / "; + } + CoordBBox bbox; + this->getBoundingBox(bbox); + ostr << " " << bbox; + return ostr.str(); +} + + +//////////////////////////////////////// + + +/// @brief Base class for tree-traversal iterators over all leaf nodes (but not leaf voxels) +template +class LeafIteratorBase +{ +public: + using RootIterT = RootChildOnIterT; + using RootNodeT = typename RootIterT::NodeType; + using NCRootNodeT = typename RootIterT::NonConstNodeType; + static const Index ROOT_LEVEL = RootNodeT::LEVEL; + using InvTreeT = typename iter::InvertedTree::Type; + using NCLeafNodeT = typename boost::mpl::front::type; + using LeafNodeT = typename CopyConstness::Type; + static const Index LEAF_LEVEL = 0, LEAF_PARENT_LEVEL = LEAF_LEVEL + 1; + + using RootIterTraits = IterTraits; + + LeafIteratorBase(): mIterList(nullptr), mTree(nullptr) {} + + LeafIteratorBase(TreeT& tree): mIterList(nullptr), mTree(&tree) + { + // Initialize the iterator list with a root node iterator. + mIterList.setIter(RootIterTraits::begin(tree.root())); + // Descend along the first branch, initializing the node iterator at each level. + Index lvl = ROOT_LEVEL; + for ( ; lvl > 0 && mIterList.down(lvl); --lvl) {} + // If the first branch terminated above the leaf level, backtrack to the next branch. + if (lvl > 0) this->next(); + } + + LeafIteratorBase(const LeafIteratorBase& other): mIterList(other.mIterList), mTree(other.mTree) + { + mIterList.updateBackPointers(); + } + LeafIteratorBase& operator=(const LeafIteratorBase& other) + { + if (&other != this) { + mTree = other.mTree; + mIterList = other.mIterList; + mIterList.updateBackPointers(); + } + return *this; + } + + //@{ + /// Return the leaf node to which the iterator is pointing. + LeafNodeT* getLeaf() const + { + LeafNodeT* n = nullptr; + mIterList.getNode(LEAF_LEVEL, n); + return n; + } + LeafNodeT& operator*() const { return *this->getLeaf(); } + LeafNodeT* operator->() const { return this->getLeaf(); } + //@} + + bool test() const { return mIterList.test(LEAF_PARENT_LEVEL); } + operator bool() const { return this->test(); } + + //@{ + /// Advance the iterator to the next leaf node. + bool next(); + void increment() { this->next(); } + LeafIteratorBase& operator++() { this->increment(); return *this; } + //@} + /// Increment the iterator n times. + void increment(Index n) { for (Index i = 0; i < n && this->next(); ++i) {} } + + TreeT* getTree() const { return mTree; } + +private: + struct PrevItem { using IterT = RootIterT; }; + + /// @note Even though a LeafIterator doesn't iterate over leaf voxels, + /// the first item of this linked list of node iterators is a leaf node iterator, + /// whose purpose is only to provide access to its parent leaf node. + IterListItem mIterList; + TreeT* mTree; +}; // class LeafIteratorBase + + +template +inline bool +LeafIteratorBase::next() +{ + // If the iterator is valid for the current node one level above the leaf level, + // advance the iterator to the node's next child. + if (mIterList.test(LEAF_PARENT_LEVEL) && mIterList.next(LEAF_PARENT_LEVEL)) { + mIterList.down(LEAF_PARENT_LEVEL); // initialize the leaf iterator + return true; + } + + Index lvl = LEAF_PARENT_LEVEL; + while (!mIterList.test(LEAF_PARENT_LEVEL)) { + if (mIterList.test(lvl)) { + mIterList.next(lvl); + } else { + do { + // Ascend to the nearest level at which + // one of the iterators is not yet exhausted. + if (lvl == ROOT_LEVEL) return false; + ++lvl; + if (mIterList.test(lvl)) mIterList.next(lvl); + } while (!mIterList.test(lvl)); + } + // Descend to the lowest child, but not as far as the leaf iterator. + while (lvl > LEAF_PARENT_LEVEL && mIterList.down(lvl)) --lvl; + } + mIterList.down(LEAF_PARENT_LEVEL); // initialize the leaf iterator + return true; +} + + +//////////////////////////////////////// + + +/// An IteratorRange wraps a tree or node iterator, giving the iterator TBB +/// splittable range semantics. +template +class IteratorRange +{ +public: + IteratorRange(const IterT& iter, size_t grainSize = 8): + mIter(iter), + mGrainSize(grainSize), + mSize(0) + { + mSize = this->size(); + } + IteratorRange(IteratorRange& other, tbb::split): + mIter(other.mIter), + mGrainSize(other.mGrainSize), + mSize(other.mSize >> 1) + { + other.increment(mSize); + } + + /// @brief Return a reference to this range's iterator. + /// @note The reference is const, because the iterator should not be + /// incremented directly. Use this range object's increment() instead. + const IterT& iterator() const { return mIter; } + + bool empty() const { return mSize == 0 || !mIter.test(); } + bool test() const { return !this->empty(); } + operator bool() const { return !this->empty(); } + + /// @brief Return @c true if this range is splittable (i.e., if the iterator + /// can be advanced more than mGrainSize times). + bool is_divisible() const { return mSize > mGrainSize; } + + /// Advance the iterator @a n times. + void increment(Index n = 1) { for ( ; n > 0 && mSize > 0; --n, --mSize, ++mIter) {} } + /// Advance the iterator to the next item. + IteratorRange& operator++() { this->increment(); return *this; } + /// @brief Advance the iterator to the next item. + /// @return @c true if the iterator is not yet exhausted. + bool next() { this->increment(); return this->test(); } + +private: + Index size() const { Index n = 0; for (IterT it(mIter); it.test(); ++n, ++it) {} return n; } + + IterT mIter; + size_t mGrainSize; + /// @note mSize is only an estimate of the number of times mIter can be incremented + /// before it is exhausted (because the topology of the underlying tree could change + /// during iteration). For the purpose of range splitting, though, that should be + /// sufficient, since the two halves need not be of exactly equal size. + Index mSize; +}; + + +//////////////////////////////////////// + + +/// @brief Base class for tree-traversal iterators over real and virtual voxel values +/// @todo class TreeVoxelIteratorBase; + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_TREEITERATOR_HAS_BEEN_INCLUDED diff --git a/openvdb/tree/ValueAccessor.h b/openvdb/tree/ValueAccessor.h new file mode 100644 index 00000000..cd3ffdc4 --- /dev/null +++ b/openvdb/tree/ValueAccessor.h @@ -0,0 +1,2634 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file tree/ValueAccessor.h +/// +/// When traversing a grid in a spatially coherent pattern (e.g., iterating +/// over neighboring voxels), request a @c ValueAccessor from the grid +/// (with Grid::getAccessor()) and use the accessor's @c getValue() and +/// @c setValue() methods. These will typically be significantly faster +/// than accessing voxels directly in the grid's tree. +/// +/// @par Example: +/// +/// @code +/// FloatGrid grid; +/// FloatGrid::Accessor acc = grid.getAccessor(); +/// // First access is slow: +/// acc.setValue(Coord(0, 0, 0), 100); +/// // Subsequent nearby accesses are fast, since the accessor now holds pointers +/// // to nodes that contain (0, 0, 0) along the path from the root of the grid's +/// // tree to the leaf: +/// acc.setValue(Coord(0, 0, 1), 100); +/// acc.getValue(Coord(0, 2, 0), 100); +/// // Slow, because the accessor must be repopulated: +/// acc.getValue(Coord(-1, -1, -1)); +/// // Fast: +/// acc.getValue(Coord(-1, -1, -2)); +/// acc.setValue(Coord(-1, -2, 0), -100); +/// @endcode + +#ifndef OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +// Forward declarations of local classes that are not intended for general use +// The IsSafe template parameter is explained in the warning below. +template +class ValueAccessor0; +template +class ValueAccessor1; +template +class ValueAccessor2; +template +class ValueAccessor3; +template class CacheItem; + + +/// @brief This base class for ValueAccessors manages registration of an accessor +/// with a tree so that the tree can automatically clear the accessor whenever +/// one of its nodes is deleted. +/// +/// @internal A base class is needed because ValueAccessor is templated on both +/// a Tree type and a mutex type. The various instantiations of the template +/// are distinct, unrelated types, so they can't easily be stored in a container +/// such as the Tree's CacheRegistry. This base class, in contrast, is templated +/// only on the Tree type, so for any given Tree, only two distinct instantiations +/// are possible, ValueAccessorBase and ValueAccessorBase. +/// +/// @warning If IsSafe = false then the ValueAccessor will not register itself +/// with the tree from which it is constructed. While in some rare cases this can +/// lead to better performance (since it avoids the small overhead of insertion +/// on creation and deletion on destruction) it is also unsafe if the tree is +/// modified. So unless you're an expert it is highly recommended to set +/// IsSafe = true, which is the default in all derived ValueAccessors defined +/// below. However if you know that the tree is no being modifed for the lifespan +/// of the ValueAccessor AND the work performed per ValueAccessor is small relative +/// to overhead of registering it you should consider setting IsSafe = false. If +/// this turns out to improve performance you should really rewrite your code so as +/// to better amortize the construction of the ValueAccessor, i.e. reuse it as much +/// as possible! +template +class ValueAccessorBase +{ +public: + static const bool IsConstTree = std::is_const::value; + + /// @brief Return true if this accessor is safe, i.e. registered + /// by the tree from which it is constructed. Un-registered + /// accessors can in rare cases be faster because it avoids the + /// (small) overhead of registration, but they are unsafe if the + /// tree is modified. So unless you're an expert it is highly + /// recommended to set IsSafe = true (which is the default). + static bool isSafe() { return IsSafe; } + + ValueAccessorBase(TreeType& tree): mTree(&tree) + { + if (IsSafe) tree.attachAccessor(*this); + } + + virtual ~ValueAccessorBase() { if (IsSafe && mTree) mTree->releaseAccessor(*this); } + + /// @brief Return a pointer to the tree associated with this accessor. + /// @details The pointer will be null only if the tree from which this accessor + /// was constructed was subsequently deleted (which generally leaves the + /// accessor in an unsafe state). + TreeType* getTree() const { return mTree; } + /// Return a reference to the tree associated with this accessor. + TreeType& tree() const { assert(mTree); return *mTree; } + + ValueAccessorBase(const ValueAccessorBase& other): mTree(other.mTree) + { + if (IsSafe && mTree) mTree->attachAccessor(*this); + } + + ValueAccessorBase& operator=(const ValueAccessorBase& other) + { + if (&other != this) { + if (IsSafe && mTree) mTree->releaseAccessor(*this); + mTree = other.mTree; + if (IsSafe && mTree) mTree->attachAccessor(*this); + } + return *this; + } + + virtual void clear() = 0; + +protected: + // Allow trees to deregister themselves. + template friend class Tree; + + virtual void release() { mTree = nullptr; } + + TreeType* mTree; +}; // class ValueAccessorBase + + +//////////////////////////////////////// + + +/// When traversing a grid in a spatially coherent pattern (e.g., iterating +/// over neighboring voxels), request a @c ValueAccessor from the grid +/// (with Grid::getAccessor()) and use the accessor's @c getValue() and +/// @c setValue() methods. These will typically be significantly faster +/// than accessing voxels directly in the grid's tree. +/// +/// A ValueAccessor caches pointers to tree nodes along the path to a voxel (x, y, z). +/// A subsequent access to voxel (x', y', z') starts from the cached leaf node and +/// moves up until a cached node that encloses (x', y', z') is found, then traverses +/// down the tree from that node to a leaf, updating the cache with the new path. +/// This leads to significant acceleration of spatially-coherent accesses. +/// +/// @param _TreeType the type of the tree to be accessed [required] +/// @param IsSafe if IsSafe = false then the ValueAccessor will +/// not register itself with the tree from which +/// it is consturcted (see warning). +/// @param CacheLevels the number of nodes to be cached, starting from the leaf level +/// and not including the root (i.e., CacheLevels < DEPTH), +/// and defaulting to all non-root nodes +/// @param MutexType the type of mutex to use (see note) +/// +/// @warning If IsSafe = false then the ValueAccessor will not register itself +/// with the tree from which it is constructed. While in some rare cases this can +/// lead to better performance (since it avoids the small overhead of insertion +/// on creation and deletion on destruction) it is also unsafe if the tree is +/// modified. So unless you're an expert it is highly recommended to set +/// IsSafe = true, which is the default. However if you know that the tree is no +/// being modifed for the lifespan of the ValueAccessor AND the work performed +/// per ValueAccessor is small relative to overhead of registering it you should +/// consider setting IsSafe = false. If this improves performance you should +/// really rewrite your code so as to better amortize the construction of the +/// ValueAccessor, i.e. reuse it as much as possible! +/// +/// @note If @c MutexType is a TBB-compatible mutex, then multiple threads may +/// safely access a single, shared accessor. However, it is highly recommended +/// that, instead, each thread be assigned its own, non-mutex-protected accessor. +template +class ValueAccessor: public ValueAccessorBase<_TreeType, IsSafe> +{ +public: + static_assert(CacheLevels < _TreeType::DEPTH, "cache size exceeds tree depth"); + + using TreeType = _TreeType; + using RootNodeT = typename TreeType::RootNodeType; + using LeafNodeT = typename TreeType::LeafNodeType; + using ValueType = typename RootNodeT::ValueType; + using BaseT = ValueAccessorBase; + using LockT = typename MutexType::scoped_lock; + using BaseT::IsConstTree; + + ValueAccessor(TreeType& tree): BaseT(tree), mCache(*this) + { + mCache.insert(Coord(), &tree.root()); + } + + ValueAccessor(const ValueAccessor& other): BaseT(other), mCache(*this, other.mCache) {} + + ValueAccessor& operator=(const ValueAccessor& other) + { + if (&other != this) { + this->BaseT::operator=(other); + mCache.copy(*this, other.mCache); + } + return *this; + } + ~ValueAccessor() override = default; + + /// Return the number of cache levels employed by this accessor. + static Index numCacheLevels() { return CacheLevels; } + + /// Return @c true if nodes along the path to the given voxel have been cached. + bool isCached(const Coord& xyz) const { LockT lock(mMutex); return mCache.isCached(xyz); } + + /// Return the value of the voxel at the given coordinates. + const ValueType& getValue(const Coord& xyz) const + { + LockT lock(mMutex); + return mCache.getValue(xyz); + } + + /// Return the active state of the voxel at the given coordinates. + bool isValueOn(const Coord& xyz) const { LockT lock(mMutex); return mCache.isValueOn(xyz); } + + /// Return the active state of the voxel as well as its value + bool probeValue(const Coord& xyz, ValueType& value) const + { + LockT lock(mMutex); + return mCache.probeValue(xyz,value); + } + + /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, + /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is + /// implicitly a background voxel). + int getValueDepth(const Coord& xyz) const + { + LockT lock(mMutex); + return mCache.getValueDepth(xyz); + } + + /// Return @c true if the value of voxel (x, y, z) resides at the leaf level + /// of the tree, i.e., if it is not a tile value. + bool isVoxel(const Coord& xyz) const { LockT lock(mMutex); return mCache.isVoxel(xyz); } + + //@{ + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, const ValueType& value) + { + LockT lock(mMutex); + mCache.setValue(xyz, value); + } + void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } + //@} + + /// Set the value of the voxel at the given coordinate but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value) + { + LockT lock(mMutex); + mCache.setValueOnly(xyz, value); + } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value) + { + LockT lock(mMutex); + mCache.setValueOff(xyz, value); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + LockT lock(mMutex); + mCache.modifyValue(xyz, op); + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + LockT lock(mMutex); + mCache.modifyValueAndActiveState(xyz, op); + } + + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on = true) + { + LockT lock(mMutex); + mCache.setActiveState(xyz, on); + } + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } + + /// Return the cached node of type @a NodeType. [Mainly for internal use] + template + NodeType* getNode() + { + LockT lock(mMutex); + NodeType* node = nullptr; + mCache.getNode(node); + return node; + } + + /// Cache the given node, which should lie along the path from the root node to + /// the node containing voxel (x, y, z). [Mainly for internal use] + template + void insertNode(const Coord& xyz, NodeType& node) + { + LockT lock(mMutex); + mCache.insert(xyz, &node); + } + + /// If a node of the given type exists in the cache, remove it, so that + /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in + /// that node. [Mainly for internal use] + template + void eraseNode() { LockT lock(mMutex); NodeType* node = nullptr; mCache.erase(node); } + + /// @brief Add the specified leaf to this tree, possibly creating a child branch + /// in the process. If the leaf node already exists, replace it. + void addLeaf(LeafNodeT* leaf) + { + LockT lock(mMutex); + mCache.addLeaf(leaf); + } + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) + { + LockT lock(mMutex); + mCache.addTile(level, xyz, value, state); + } + + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, create one, but preserve the values and + /// active states of all voxels. + /// @details Use this method to preallocate a static tree topology + /// over which to safely perform multithreaded processing. + LeafNodeT* touchLeaf(const Coord& xyz) + { + LockT lock(mMutex); + return mCache.touchLeaf(xyz); + } + + //@{ + /// @brief Return a pointer to the node of the specified type that contains + /// voxel (x, y, z), or @c nullptr if no such node exists. + template + NodeT* probeNode(const Coord& xyz) + { + LockT lock(mMutex); + return mCache.template probeNode(xyz); + } + template + const NodeT* probeConstNode(const Coord& xyz) const + { + LockT lock(mMutex); + return mCache.template probeConstNode(xyz); + } + template + const NodeT* probeNode(const Coord& xyz) const + { + return this->template probeConstNode(xyz); + } + //@} + + //@{ + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z), + /// or @c nullptr if no such node exists. + LeafNodeT* probeLeaf(const Coord& xyz) + { + LockT lock(mMutex); + return mCache.probeLeaf(xyz); + } + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + LockT lock(mMutex); + return mCache.probeConstLeaf(xyz); + } + const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + //@} + + /// Remove all nodes from this cache, then reinsert the root node. + void clear() override + { + LockT lock(mMutex); + mCache.clear(); + if (this->mTree) mCache.insert(Coord(), &(this->mTree->root())); + } + +private: + // Allow nodes to insert themselves into the cache. + template friend class RootNode; + template friend class InternalNode; + template friend class LeafNode; + // Allow trees to deregister themselves. + template friend class Tree; + + /// Prevent this accessor from calling Tree::releaseCache() on a tree that + /// no longer exists. (Called by mTree when it is destroyed.) + void release() override + { + LockT lock(mMutex); + this->BaseT::release(); + mCache.clear(); + } + + /// Cache the given node, which should lie along the path from the root node to + /// the node containing voxel (x, y, z). + /// @note This operation is not mutex-protected and is intended to be called + /// only by nodes and only in the context of a getValue() or setValue() call. + template + void insert(const Coord& xyz, NodeType* node) { mCache.insert(xyz, node); } + + // Define a list of all tree node types from LeafNode to RootNode + using InvTreeT = typename RootNodeT::NodeChainType; + // Remove all tree node types that are excluded from the cache + using BeginT = typename boost::mpl::begin::type; + using FirstT = typename boost::mpl::advance>::type; + using LastT = typename boost::mpl::find::type; + using SubtreeT = typename boost::mpl::erase::type; + using CacheItemT = CacheItem::value==1>; + + // Private member data + mutable CacheItemT mCache; + mutable MutexType mMutex; + +}; // class ValueAccessor + + +/// @brief Template specialization of the ValueAccessor with no mutex and no cache levels +/// @details This specialization is provided mainly for benchmarking. +/// Accessors with caching will almost always be faster. +template +class ValueAccessor + : public ValueAccessor0 +{ +public: + ValueAccessor(TreeType& tree): ValueAccessor0(tree) {} + ValueAccessor(const ValueAccessor& other): ValueAccessor0(other) {} + ~ValueAccessor() override = default; +}; + + +/// Template specialization of the ValueAccessor with no mutex and one cache level +template +class ValueAccessor + : public ValueAccessor1 +{ +public: + ValueAccessor(TreeType& tree): ValueAccessor1(tree) {} + ValueAccessor(const ValueAccessor& other): ValueAccessor1(other) {} + ~ValueAccessor() override = default; +}; + + +/// Template specialization of the ValueAccessor with no mutex and two cache levels +template +class ValueAccessor + : public ValueAccessor2 +{ +public: + ValueAccessor(TreeType& tree): ValueAccessor2(tree) {} + ValueAccessor(const ValueAccessor& other): ValueAccessor2(other) {} + ~ValueAccessor() override = default; +}; + + +/// Template specialization of the ValueAccessor with no mutex and three cache levels +template +class ValueAccessor: public ValueAccessor3 +{ +public: + ValueAccessor(TreeType& tree): ValueAccessor3(tree) {} + ValueAccessor(const ValueAccessor&) = default; + ValueAccessor& operator=(const ValueAccessor&) = default; + ~ValueAccessor() override = default; +}; + + +//////////////////////////////////////// + + +/// @brief This accessor is thread-safe (at the cost of speed) for both reading and +/// writing to a tree. That is, multiple threads may safely access a single, +/// shared ValueAccessorRW. +/// +/// @warning Since the mutex-locking employed by the ValueAccessorRW +/// can seriously impair performance of multithreaded applications, it +/// is recommended that, instead, each thread be assigned its own +/// (non-mutex protected) accessor. +template +class ValueAccessorRW: public ValueAccessor +{ +public: + ValueAccessorRW(TreeType& tree) + : ValueAccessor(tree) + { + } +}; + + +//////////////////////////////////////// + + +// +// The classes below are for internal use and should rarely be used directly. +// + +// An element of a compile-time linked list of node pointers, ordered from LeafNode to RootNode +template +class CacheItem +{ +public: + using NodeType = typename boost::mpl::front::type; + using ValueType = typename NodeType::ValueType; + using LeafNodeType = typename NodeType::LeafNodeType; + using CoordLimits = std::numeric_limits; + + CacheItem(TreeCacheT& parent): + mParent(&parent), + mHash(CoordLimits::max()), + mNode(nullptr), + mNext(parent) + { + } + + //@{ + /// Copy another CacheItem's node pointers and hash keys, but not its parent pointer. + CacheItem(TreeCacheT& parent, const CacheItem& other): + mParent(&parent), + mHash(other.mHash), + mNode(other.mNode), + mNext(parent, other.mNext) + { + } + + CacheItem& copy(TreeCacheT& parent, const CacheItem& other) + { + mParent = &parent; + mHash = other.mHash; + mNode = other.mNode; + mNext.copy(parent, other.mNext); + return *this; + } + //@} + + bool isCached(const Coord& xyz) const + { + return (this->isHashed(xyz) || mNext.isCached(xyz)); + } + + /// Cache the given node at this level. + void insert(const Coord& xyz, const NodeType* node) + { + mHash = (node != nullptr) ? xyz & ~(NodeType::DIM-1) : Coord::max(); + mNode = node; + } + /// Forward the given node to another level of the cache. + template + void insert(const Coord& xyz, const OtherNodeType* node) { mNext.insert(xyz, node); } + + /// Erase the node at this level. + void erase(const NodeType*) { mHash = Coord::max(); mNode = nullptr; } + /// Erase the node at another level of the cache. + template + void erase(const OtherNodeType* node) { mNext.erase(node); } + + /// Erase the nodes at this and lower levels of the cache. + void clear() { mHash = Coord::max(); mNode = nullptr; mNext.clear(); } + + /// Return the cached node (if any) at this level. + void getNode(const NodeType*& node) const { node = mNode; } + void getNode(const NodeType*& node) { node = mNode; } + void getNode(NodeType*& node) + { + // This combination of a static assertion and a const_cast might not be elegant, + // but it is a lot simpler than specializing TreeCache for const Trees. + static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree"); + node = const_cast(mNode); + } + /// Forward the request to another level of the cache. + template + void getNode(OtherNodeType*& node) { mNext.getNode(node); } + + /// Return the value of the voxel at the given coordinates. + const ValueType& getValue(const Coord& xyz) + { + if (this->isHashed(xyz)) { + assert(mNode); + return mNode->getValueAndCache(xyz, *mParent); + } + return mNext.getValue(xyz); + } + + void addLeaf(LeafNodeType* leaf) + { + static_assert(!TreeCacheT::IsConstTree, "can't add a node to a const tree"); + if (NodeType::LEVEL == 0) return; + if (this->isHashed(leaf->origin())) { + assert(mNode); + return const_cast(mNode)->addLeafAndCache(leaf, *mParent); + } + mNext.addLeaf(leaf); + } + + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) + { + static_assert(!TreeCacheT::IsConstTree, "can't add a tile to a const tree"); + if (NodeType::LEVEL < level) return; + if (this->isHashed(xyz)) { + assert(mNode); + return const_cast(mNode)->addTileAndCache( + level, xyz, value, state, *mParent); + } + mNext.addTile(level, xyz, value, state); + } + + LeafNodeType* touchLeaf(const Coord& xyz) + { + static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree"); + if (this->isHashed(xyz)) { + assert(mNode); + return const_cast(mNode)->touchLeafAndCache(xyz, *mParent); + } + return mNext.touchLeaf(xyz); + } + + LeafNodeType* probeLeaf(const Coord& xyz) + { + static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree"); + if (this->isHashed(xyz)) { + assert(mNode); + return const_cast(mNode)->probeLeafAndCache(xyz, *mParent); + } + return mNext.probeLeaf(xyz); + } + + const LeafNodeType* probeConstLeaf(const Coord& xyz) + { + if (this->isHashed(xyz)) { + assert(mNode); + return mNode->probeConstLeafAndCache(xyz, *mParent); + } + return mNext.probeConstLeaf(xyz); + } + + template + NodeT* probeNode(const Coord& xyz) + { + static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree"); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (this->isHashed(xyz)) { + if ((std::is_same::value)) { + assert(mNode); + return reinterpret_cast(const_cast(mNode)); + } + return const_cast(mNode)->template probeNodeAndCache(xyz, *mParent); + } + return mNext.template probeNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + template + const NodeT* probeConstNode(const Coord& xyz) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (this->isHashed(xyz)) { + if ((std::is_same::value)) { + assert(mNode); + return reinterpret_cast(mNode); + } + return mNode->template probeConstNodeAndCache(xyz, *mParent); + } + return mNext.template probeConstNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + /// Return the active state of the voxel at the given coordinates. + bool isValueOn(const Coord& xyz) + { + if (this->isHashed(xyz)) { + assert(mNode); + return mNode->isValueOnAndCache(xyz, *mParent); + } + return mNext.isValueOn(xyz); + } + + /// Return the active state and value of the voxel at the given coordinates. + bool probeValue(const Coord& xyz, ValueType& value) + { + if (this->isHashed(xyz)) { + assert(mNode); + return mNode->probeValueAndCache(xyz, value, *mParent); + } + return mNext.probeValue(xyz, value); + } + + int getValueDepth(const Coord& xyz) + { + if (this->isHashed(xyz)) { + assert(mNode); + return static_cast(TreeCacheT::RootNodeT::LEVEL) - + static_cast(mNode->getValueLevelAndCache(xyz, *mParent)); + } else { + return mNext.getValueDepth(xyz); + } + } + + bool isVoxel(const Coord& xyz) + { + if (this->isHashed(xyz)) { + assert(mNode); + return mNode->getValueLevelAndCache(xyz, *mParent)==0; + } else { + return mNext.isVoxel(xyz); + } + } + + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, const ValueType& value) + { + if (this->isHashed(xyz)) { + assert(mNode); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mNode)->setValueAndCache(xyz, value, *mParent); + } else { + mNext.setValue(xyz, value); + } + } + void setValueOnly(const Coord& xyz, const ValueType& value) + { + if (this->isHashed(xyz)) { + assert(mNode); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mNode)->setValueOnlyAndCache(xyz, value, *mParent); + } else { + mNext.setValueOnly(xyz, value); + } + } + void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + if (this->isHashed(xyz)) { + assert(mNode); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mNode)->modifyValueAndCache(xyz, op, *mParent); + } else { + mNext.modifyValue(xyz, op); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + if (this->isHashed(xyz)) { + assert(mNode); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mNode)->modifyValueAndActiveStateAndCache(xyz, op, *mParent); + } else { + mNext.modifyValueAndActiveState(xyz, op); + } + } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value) + { + if (this->isHashed(xyz)) { + assert(mNode); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mNode)->setValueOffAndCache(xyz, value, *mParent); + } else { + mNext.setValueOff(xyz, value); + } + } + + /// Set the active state of the voxel at the given coordinates. + void setActiveState(const Coord& xyz, bool on) + { + if (this->isHashed(xyz)) { + assert(mNode); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mNode)->setActiveStateAndCache(xyz, on, *mParent); + } else { + mNext.setActiveState(xyz, on); + } + } + +private: + CacheItem(const CacheItem&); + CacheItem& operator=(const CacheItem&); + + bool isHashed(const Coord& xyz) const + { + return (xyz[0] & ~Coord::ValueType(NodeType::DIM-1)) == mHash[0] + && (xyz[1] & ~Coord::ValueType(NodeType::DIM-1)) == mHash[1] + && (xyz[2] & ~Coord::ValueType(NodeType::DIM-1)) == mHash[2]; + } + + TreeCacheT* mParent; + Coord mHash; + const NodeType* mNode; + using RestT = typename boost::mpl::pop_front::type; // NodeVecT minus its first item + CacheItem::value == 1> mNext; +};// end of CacheItem + + +/// The tail of a compile-time list of cached node pointers, ordered from LeafNode to RootNode +template +class CacheItem +{ +public: + using RootNodeType = typename boost::mpl::front::type; + using ValueType = typename RootNodeType::ValueType; + using LeafNodeType = typename RootNodeType::LeafNodeType; + + CacheItem(TreeCacheT& parent): mParent(&parent), mRoot(nullptr) {} + CacheItem(TreeCacheT& parent, const CacheItem& other): mParent(&parent), mRoot(other.mRoot) {} + + CacheItem& copy(TreeCacheT& parent, const CacheItem& other) + { + mParent = &parent; + mRoot = other.mRoot; + return *this; + } + + bool isCached(const Coord& xyz) const { return this->isHashed(xyz); } + + void insert(const Coord&, const RootNodeType* root) { mRoot = root; } + + // Needed for node types that are not cached + template + void insert(const Coord&, const OtherNodeType*) {} + + void erase(const RootNodeType*) { mRoot = nullptr; } + + void clear() { mRoot = nullptr; } + + void getNode(RootNodeType*& node) + { + static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree"); + node = const_cast(mRoot); + } + void getNode(const RootNodeType*& node) const { node = mRoot; } + + void addLeaf(LeafNodeType* leaf) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't add a node to a const tree"); + const_cast(mRoot)->addLeafAndCache(leaf, *mParent); + } + + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't add a tile to a const tree"); + const_cast(mRoot)->addTileAndCache(level, xyz, value, state, *mParent); + } + + LeafNodeType* touchLeaf(const Coord& xyz) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree"); + return const_cast(mRoot)->touchLeafAndCache(xyz, *mParent); + } + + LeafNodeType* probeLeaf(const Coord& xyz) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree"); + return const_cast(mRoot)->probeLeafAndCache(xyz, *mParent); + } + + const LeafNodeType* probeConstLeaf(const Coord& xyz) + { + assert(mRoot); + return mRoot->probeConstLeafAndCache(xyz, *mParent); + } + + template + NodeType* probeNode(const Coord& xyz) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't get a non-const node from a const tree"); + return const_cast(mRoot)-> + template probeNodeAndCache(xyz, *mParent); + } + + template + const NodeType* probeConstNode(const Coord& xyz) + { + assert(mRoot); + return mRoot->template probeConstNodeAndCache(xyz, *mParent); + } + + int getValueDepth(const Coord& xyz) + { + assert(mRoot); + return mRoot->getValueDepthAndCache(xyz, *mParent); + } + bool isValueOn(const Coord& xyz) + { + assert(mRoot); + return mRoot->isValueOnAndCache(xyz, *mParent); + } + + bool probeValue(const Coord& xyz, ValueType& value) + { + assert(mRoot); + return mRoot->probeValueAndCache(xyz, value, *mParent); + } + bool isVoxel(const Coord& xyz) + { + assert(mRoot); + return mRoot->getValueDepthAndCache(xyz, *mParent) == + static_cast(RootNodeType::LEVEL); + } + const ValueType& getValue(const Coord& xyz) + { + assert(mRoot); + return mRoot->getValueAndCache(xyz, *mParent); + } + + void setValue(const Coord& xyz, const ValueType& value) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mRoot)->setValueAndCache(xyz, value, *mParent); + } + void setValueOnly(const Coord& xyz, const ValueType& value) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mRoot)->setValueOnlyAndCache(xyz, value, *mParent); + } + void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } + + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mRoot)->modifyValueAndCache(xyz, op, *mParent); + } + + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mRoot)->modifyValueAndActiveStateAndCache(xyz, op, *mParent); + } + + void setValueOff(const Coord& xyz, const ValueType& value) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mRoot)->setValueOffAndCache(xyz, value, *mParent); + } + + void setActiveState(const Coord& xyz, bool on) + { + assert(mRoot); + static_assert(!TreeCacheT::IsConstTree, "can't modify a const tree's values"); + const_cast(mRoot)->setActiveStateAndCache(xyz, on, *mParent); + } + +private: + CacheItem(const CacheItem&); + CacheItem& operator=(const CacheItem&); + + bool isHashed(const Coord&) const { return false; } + + TreeCacheT* mParent; + const RootNodeType* mRoot; +};// end of CacheItem specialized for RootNode + + +//////////////////////////////////////// + + +/// @brief ValueAccessor with no mutex and no node caching. +/// @details This specialization is provided mainly for benchmarking. +/// Accessors with caching will almost always be faster. +template +class ValueAccessor0: public ValueAccessorBase<_TreeType, IsSafe> +{ +public: + using TreeType = _TreeType; + using ValueType = typename TreeType::ValueType; + using RootNodeT = typename TreeType::RootNodeType; + using LeafNodeT = typename TreeType::LeafNodeType; + using BaseT = ValueAccessorBase; + + ValueAccessor0(TreeType& tree): BaseT(tree) {} + + ValueAccessor0(const ValueAccessor0& other): BaseT(other) {} + + /// Return the number of cache levels employed by this accessor. + static Index numCacheLevels() { return 0; } + + ValueAccessor0& operator=(const ValueAccessor0& other) + { + if (&other != this) this->BaseT::operator=(other); + return *this; + } + + ~ValueAccessor0() override = default; + + /// Return @c true if nodes along the path to the given voxel have been cached. + bool isCached(const Coord&) const { return false; } + + /// Return the value of the voxel at the given coordinates. + const ValueType& getValue(const Coord& xyz) const + { + assert(BaseT::mTree); + return BaseT::mTree->getValue(xyz); + } + + /// Return the active state of the voxel at the given coordinates. + bool isValueOn(const Coord& xyz) const + { + assert(BaseT::mTree); + return BaseT::mTree->isValueOn(xyz); + } + + /// Return the active state and, in @a value, the value of the voxel at the given coordinates. + bool probeValue(const Coord& xyz, ValueType& value) const + { + assert(BaseT::mTree); + return BaseT::mTree->probeValue(xyz, value); + } + + /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, + /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is + /// implicitly a background voxel). + int getValueDepth(const Coord& xyz) const + { + assert(BaseT::mTree); + return BaseT::mTree->getValueDepth(xyz); + } + + /// Return @c true if the value of voxel (x, y, z) resides at the leaf level + /// of the tree, i.e., if it is not a tile value. + bool isVoxel(const Coord& xyz) const + { + assert(BaseT::mTree); + return BaseT::mTree->getValueDepth(xyz) == static_cast(RootNodeT::LEVEL); + } + + //@{ + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + BaseT::mTree->setValue(xyz, value); + } + void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } + //@} + + /// Set the value of the voxel at the given coordinate but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + BaseT::mTree->setValueOnly(xyz, value); + } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + BaseT::mTree->root().setValueOff(xyz, value); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + BaseT::mTree->modifyValue(xyz, op); + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + BaseT::mTree->modifyValueAndActiveState(xyz, op); + } + + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on = true) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + BaseT::mTree->setActiveState(xyz, on); + } + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } + + /// Return the cached node of type @a NodeType. [Mainly for internal use] + template NodeT* getNode() { return nullptr; } + + /// Cache the given node, which should lie along the path from the root node to + /// the node containing voxel (x, y, z). [Mainly for internal use] + template void insertNode(const Coord&, NodeT&) {} + + /// @brief Add the specified leaf to this tree, possibly creating a child branch + /// in the process. If the leaf node already exists, replace it. + void addLeaf(LeafNodeT* leaf) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't add a node to a const tree"); + BaseT::mTree->root().addLeaf(leaf); + } + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree"); + BaseT::mTree->root().addTile(level, xyz, value, state); + } + + /// If a node of the given type exists in the cache, remove it, so that + /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in + /// that node. [Mainly for internal use] + template void eraseNode() {} + + LeafNodeT* touchLeaf(const Coord& xyz) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree"); + return BaseT::mTree->touchLeaf(xyz); + } + + template + NodeT* probeNode(const Coord& xyz) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree"); + return BaseT::mTree->template probeNode(xyz); + } + + template + const NodeT* probeConstNode(const Coord& xyz) const + { + assert(BaseT::mTree); + return BaseT::mTree->template probeConstNode(xyz); + } + + LeafNodeT* probeLeaf(const Coord& xyz) + { + return this->template probeNode(xyz); + } + + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + return this->template probeConstNode(xyz); + } + + const LeafNodeT* probeLeaf(const Coord& xyz) const + { + return this->probeConstLeaf(xyz); + } + + /// Remove all nodes from this cache, then reinsert the root node. + void clear() override {} + +private: + // Allow trees to deregister themselves. + template friend class Tree; + + /// Prevent this accessor from calling Tree::releaseCache() on a tree that + /// no longer exists. (Called by mTree when it is destroyed.) + void release() override { this->BaseT::release(); } + +}; // ValueAccessor0 + + +/// @brief Value accessor with one level of node caching. +/// @details The node cache level is specified by L0 with the default value 0 +/// (defined in the forward declaration) corresponding to a LeafNode. +/// +/// @note This class is for experts only and should rarely be used +/// directly. Instead use ValueAccessor with its default template arguments. +template +class ValueAccessor1 : public ValueAccessorBase<_TreeType, IsSafe> +{ +public: + static_assert(_TreeType::DEPTH >= 2, "cache size exceeds tree depth"); + static_assert(L0 < _TreeType::RootNodeType::LEVEL, "invalid cache level"); + using TreeType = _TreeType; + using ValueType = typename TreeType::ValueType; + using RootNodeT = typename TreeType::RootNodeType; + using LeafNodeT = typename TreeType::LeafNodeType; + using BaseT = ValueAccessorBase; + using InvTreeT = typename RootNodeT::NodeChainType; + using NodeT0 = typename boost::mpl::at >::type; + + /// Constructor from a tree + ValueAccessor1(TreeType& tree) : BaseT(tree), mKey0(Coord::max()), mNode0(nullptr) + { + } + + /// Copy constructor + ValueAccessor1(const ValueAccessor1& other) : BaseT(other) { this->copy(other); } + + /// Return the number of cache levels employed by this ValueAccessor + static Index numCacheLevels() { return 1; } + + /// Asignment operator + ValueAccessor1& operator=(const ValueAccessor1& other) + { + if (&other != this) { + this->BaseT::operator=(other); + this->copy(other); + } + return *this; + } + + /// Virtual destructor + ~ValueAccessor1() override = default; + + /// Return @c true if any of the nodes along the path to the given + /// voxel have been cached. + bool isCached(const Coord& xyz) const + { + assert(BaseT::mTree); + return this->isHashed(xyz); + } + + /// Return the value of the voxel at the given coordinates. + const ValueType& getValue(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed(xyz)) { + assert(mNode0); + return mNode0->getValueAndCache(xyz, this->self()); + } + return BaseT::mTree->root().getValueAndCache(xyz, this->self()); + } + + /// Return the active state of the voxel at the given coordinates. + bool isValueOn(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed(xyz)) { + assert(mNode0); + return mNode0->isValueOnAndCache(xyz, this->self()); + } + return BaseT::mTree->root().isValueOnAndCache(xyz, this->self()); + } + + /// Return the active state of the voxel as well as its value + bool probeValue(const Coord& xyz, ValueType& value) const + { + assert(BaseT::mTree); + if (this->isHashed(xyz)) { + assert(mNode0); + return mNode0->probeValueAndCache(xyz, value, this->self()); + } + return BaseT::mTree->root().probeValueAndCache(xyz, value, this->self()); + } + + /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, + /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is + /// implicitly a background voxel). + int getValueDepth(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed(xyz)) { + assert(mNode0); + return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self()); + } + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()); + } + + /// Return @c true if the value of voxel (x, y, z) resides at the leaf level + /// of the tree, i.e., if it is not a tile value. + bool isVoxel(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed(xyz)) { + assert(mNode0); + return mNode0->getValueLevelAndCache(xyz, this->self()) == 0; + } + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()) == + static_cast(RootNodeT::LEVEL); + } + + //@{ + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed(xyz)) { + assert(mNode0); + const_cast(mNode0)->setValueAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueAndCache(xyz, value, *this); + } + } + void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } + //@} + + /// Set the value of the voxel at the given coordinate but preserves its active state. + void setValueOnly(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed(xyz)) { + assert(mNode0); + const_cast(mNode0)->setValueOnlyAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueOnlyAndCache(xyz, value, *this); + } + } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed(xyz)) { + assert(mNode0); + const_cast(mNode0)->setValueOffAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueOffAndCache(xyz, value, *this); + } + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndCache(xyz, op, *this); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this); + } + } + + /// Set the active state of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on = true) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed(xyz)) { + assert(mNode0); + const_cast(mNode0)->setActiveStateAndCache(xyz, on, *this); + } else { + BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this); + } + } + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } + + /// Return the cached node of type @a NodeType. [Mainly for internal use] + template + NodeT* getNode() + { + const NodeT* node = nullptr; + this->getNode(node); + return const_cast(node); + } + + /// Cache the given node, which should lie along the path from the root node to + /// the node containing voxel (x, y, z). [Mainly for internal use] + template + void insertNode(const Coord& xyz, NodeT& node) { this->insert(xyz, &node); } + + /// If a node of the given type exists in the cache, remove it, so that + /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in + /// that node. [Mainly for internal use] + template + void eraseNode() + { + const NodeT* node = nullptr; + this->eraseNode(node); + } + + /// @brief Add the specified leaf to this tree, possibly creating a child branch + /// in the process. If the leaf node already exists, replace it. + void addLeaf(LeafNodeT* leaf) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't add a node to a const tree"); + BaseT::mTree->root().addLeaf(leaf); + } + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree"); + BaseT::mTree->root().addTile(level, xyz, value, state); + } + + /// @brief @return the leaf node that contains voxel (x, y, z) and + /// if it doesn't exist, create it, but preserve the values and + /// active states of all voxels. + /// + /// Use this method to preallocate a static tree topology over which to + /// safely perform multithreaded processing. + LeafNodeT* touchLeaf(const Coord& xyz) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree"); + if (this->isHashed(xyz)) { + assert(mNode0); + return const_cast(mNode0)->touchLeafAndCache(xyz, *this); + } + return BaseT::mTree->root().touchLeafAndCache(xyz, *this); + } + + /// @brief @return a pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + template + NodeT* probeNode(const Coord& xyz) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree"); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((std::is_same::value)) { + if (this->isHashed(xyz)) { + assert(mNode0); + return reinterpret_cast(const_cast(mNode0)); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } + return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + LeafNodeT* probeLeaf(const Coord& xyz) + { + return this->template probeNode(xyz); + } + + /// @brief @return a const pointer to the nodeof the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + template + const NodeT* probeConstNode(const Coord& xyz) const + { + assert(BaseT::mTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((std::is_same::value)) { + if (this->isHashed(xyz)) { + assert(mNode0); + return reinterpret_cast(mNode0); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } + return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + return this->template probeConstNode(xyz); + } + const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + + /// Remove all the cached nodes and invalidate the corresponding hash-keys. + void clear() override + { + mKey0 = Coord::max(); + mNode0 = nullptr; + } + +private: + // Allow nodes to insert themselves into the cache. + template friend class RootNode; + template friend class InternalNode; + template friend class LeafNode; + // Allow trees to deregister themselves. + template friend class Tree; + + // This private method is merely for convenience. + inline ValueAccessor1& self() const { return const_cast(*this); } + + void getNode(const NodeT0*& node) { node = mNode0; } + void getNode(const RootNodeT*& node) + { + node = (BaseT::mTree ? &BaseT::mTree->root() : nullptr); + } + template void getNode(const OtherNodeType*& node) { node = nullptr; } + void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = nullptr; } + template void eraseNode(const OtherNodeType*) {} + + /// Private copy method + inline void copy(const ValueAccessor1& other) + { + mKey0 = other.mKey0; + mNode0 = other.mNode0; + } + + /// Prevent this accessor from calling Tree::releaseCache() on a tree that + /// no longer exists. (Called by mTree when it is destroyed.) + void release() override + { + this->BaseT::release(); + this->clear(); + } + /// Cache the given node, which should lie along the path from the root node to + /// the node containing voxel (x, y, z). + /// @note This operation is not mutex-protected and is intended to be called + /// only by nodes and only in the context of a getValue() or setValue() call. + inline void insert(const Coord& xyz, const NodeT0* node) + { + assert(node); + mKey0 = xyz & ~(NodeT0::DIM-1); + mNode0 = node; + } + + /// No-op in case a tree traversal attemps to insert a node that + /// is not cached by the ValueAccessor + template inline void insert(const Coord&, const OtherNodeType*) {} + + inline bool isHashed(const Coord& xyz) const + { + return (xyz[0] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[0] + && (xyz[1] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[1] + && (xyz[2] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[2]; + } + mutable Coord mKey0; + mutable const NodeT0* mNode0; +}; // ValueAccessor1 + + +/// @brief Value accessor with two levels of node caching. +/// @details The node cache levels are specified by L0 and L1 +/// with the default values 0 and 1 (defined in the forward declaration) +/// corresponding to a LeafNode and its parent InternalNode. +/// +/// @note This class is for experts only and should rarely be used directly. +/// Instead use ValueAccessor with its default template arguments. +template +class ValueAccessor2 : public ValueAccessorBase<_TreeType, IsSafe> +{ +public: + static_assert(_TreeType::DEPTH >= 3, "cache size exceeds tree depth"); + static_assert(L0 < L1, "invalid cache level"); + static_assert(L1 < _TreeType::RootNodeType::LEVEL, "invalid cache level"); + + using TreeType = _TreeType; + using ValueType = typename TreeType::ValueType; + using RootNodeT = typename TreeType::RootNodeType; + using LeafNodeT = typename TreeType::LeafNodeType; + using BaseT = ValueAccessorBase; + using InvTreeT = typename RootNodeT::NodeChainType; + using NodeT0 = typename boost::mpl::at>::type; + using NodeT1 = typename boost::mpl::at>::type; + + /// Constructor from a tree + ValueAccessor2(TreeType& tree) : BaseT(tree), + mKey0(Coord::max()), mNode0(nullptr), + mKey1(Coord::max()), mNode1(nullptr) {} + + /// Copy constructor + ValueAccessor2(const ValueAccessor2& other) : BaseT(other) { this->copy(other); } + + /// Return the number of cache levels employed by this ValueAccessor + static Index numCacheLevels() { return 2; } + + /// Asignment operator + ValueAccessor2& operator=(const ValueAccessor2& other) + { + if (&other != this) { + this->BaseT::operator=(other); + this->copy(other); + } + return *this; + } + + /// Virtual destructor + ~ValueAccessor2() override = default; + + /// Return @c true if any of the nodes along the path to the given + /// voxel have been cached. + bool isCached(const Coord& xyz) const + { + assert(BaseT::mTree); + return this->isHashed1(xyz) || this->isHashed0(xyz); + } + + /// Return the value of the voxel at the given coordinates. + const ValueType& getValue(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return mNode0->getValueAndCache(xyz, this->self()); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->getValueAndCache(xyz, this->self()); + } + return BaseT::mTree->root().getValueAndCache(xyz, this->self()); + } + + /// Return the active state of the voxel at the given coordinates. + bool isValueOn(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return mNode0->isValueOnAndCache(xyz, this->self()); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->isValueOnAndCache(xyz, this->self()); + } + return BaseT::mTree->root().isValueOnAndCache(xyz, this->self()); + } + + /// Return the active state of the voxel as well as its value + bool probeValue(const Coord& xyz, ValueType& value) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return mNode0->probeValueAndCache(xyz, value, this->self()); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->probeValueAndCache(xyz, value, this->self()); + } + return BaseT::mTree->root().probeValueAndCache(xyz, value, this->self()); + } + + /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, + /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is + /// implicitly a background voxel). + int getValueDepth(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self()); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return RootNodeT::LEVEL - mNode1->getValueLevelAndCache(xyz, this->self()); + } + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()); + } + + /// Return @c true if the value of voxel (x, y, z) resides at the leaf level + /// of the tree, i.e., if it is not a tile value. + bool isVoxel(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return mNode0->getValueLevelAndCache(xyz, this->self())==0; + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->getValueLevelAndCache(xyz, this->self())==0; + } + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()) == + static_cast(RootNodeT::LEVEL); + } + + //@{ + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->setValueAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueAndCache(xyz, value, *this); + } + } + void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } + //@} + + /// Set the value of the voxel at the given coordinate but preserves its active state. + void setValueOnly(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->setValueOnlyAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueOnlyAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueOnlyAndCache(xyz, value, *this); + } + } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->setValueOffAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueOffAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueOffAndCache(xyz, value, *this); + } + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->modifyValueAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndCache(xyz, op, *this); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this); + } + } + + /// Set the active state of the voxel at the given coordinates without changing its value. + void setActiveState(const Coord& xyz, bool on = true) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->setActiveStateAndCache(xyz, on, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setActiveStateAndCache(xyz, on, *this); + } else { + BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this); + } + } + /// Mark the voxel at the given coordinates as active without changing its value. + void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } + /// Mark the voxel at the given coordinates as inactive without changing its value. + void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } + + /// Return the cached node of type @a NodeType. [Mainly for internal use] + template + NodeT* getNode() + { + const NodeT* node = nullptr; + this->getNode(node); + return const_cast(node); + } + + /// Cache the given node, which should lie along the path from the root node to + /// the node containing voxel (x, y, z). [Mainly for internal use] + template + void insertNode(const Coord& xyz, NodeT& node) { this->insert(xyz, &node); } + + /// If a node of the given type exists in the cache, remove it, so that + /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in + /// that node. [Mainly for internal use] + template + void eraseNode() + { + const NodeT* node = nullptr; + this->eraseNode(node); + } + + /// @brief Add the specified leaf to this tree, possibly creating a child branch + /// in the process. If the leaf node already exists, replace it. + void addLeaf(LeafNodeT* leaf) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't add a node to a const tree"); + if (this->isHashed1(leaf->origin())) { + assert(mNode1); + return const_cast(mNode1)->addLeafAndCache(leaf, *this); + } + BaseT::mTree->root().addLeafAndCache(leaf, *this); + } + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree"); + if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->addTileAndCache(level, xyz, value, state, *this); + } + BaseT::mTree->root().addTileAndCache(level, xyz, value, state, *this); + } + + /// @brief @return the leaf node that contains voxel (x, y, z) and + /// if it doesn't exist, create it, but preserve the values and + /// active states of all voxels. + /// + /// Use this method to preallocate a static tree topology over which to + /// safely perform multithreaded processing. + LeafNodeT* touchLeaf(const Coord& xyz) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree"); + if (this->isHashed0(xyz)) { + assert(mNode0); + return const_cast(mNode0)->touchLeafAndCache(xyz, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->touchLeafAndCache(xyz, *this); + } + return BaseT::mTree->root().touchLeafAndCache(xyz, *this); + } + /// @brief @return a pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + template + NodeT* probeNode(const Coord& xyz) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree"); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((std::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(const_cast(mNode0)); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->template probeNodeAndCache(xyz, *this); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } else if ((std::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(const_cast(mNode1)); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } + return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// @brief @return a pointer to the leaf node that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + LeafNodeT* probeLeaf(const Coord& xyz) { return this->template probeNode(xyz); } + + /// @brief @return a const pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + template + const NodeT* probeConstLeaf(const Coord& xyz) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((std::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->template probeConstNodeAndCache(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } else if ((std::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(mNode1); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } + return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// @brief @return a const pointer to the leaf node that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + return this->template probeConstNode(xyz); + } + const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + + /// @brief @return a const pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + template + const NodeT* probeConstNode(const Coord& xyz) const + { + assert(BaseT::mTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((std::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->template probeConstNodeAndCache(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } else if ((std::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(mNode1); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } + return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + /// Remove all the cached nodes and invalidate the corresponding hash-keys. + void clear() override + { + mKey0 = Coord::max(); + mNode0 = nullptr; + mKey1 = Coord::max(); + mNode1 = nullptr; + } + +private: + // Allow nodes to insert themselves into the cache. + template friend class RootNode; + template friend class InternalNode; + template friend class LeafNode; + // Allow trees to deregister themselves. + template friend class Tree; + + // This private method is merely for convenience. + inline ValueAccessor2& self() const { return const_cast(*this); } + + void getNode(const NodeT0*& node) { node = mNode0; } + void getNode(const NodeT1*& node) { node = mNode1; } + void getNode(const RootNodeT*& node) + { + node = (BaseT::mTree ? &BaseT::mTree->root() : nullptr); + } + template void getNode(const OtherNodeType*& node) { node = nullptr; } + + void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = nullptr; } + void eraseNode(const NodeT1*) { mKey1 = Coord::max(); mNode1 = nullptr; } + template void eraseNode(const OtherNodeType*) {} + + /// Private copy method + inline void copy(const ValueAccessor2& other) + { + mKey0 = other.mKey0; + mNode0 = other.mNode0; + mKey1 = other.mKey1; + mNode1 = other.mNode1; + } + + /// Prevent this accessor from calling Tree::releaseCache() on a tree that + /// no longer exists. (Called by mTree when it is destroyed.) + void release() override + { + this->BaseT::release(); + this->clear(); + } + + /// Cache the given node, which should lie along the path from the root node to + /// the node containing voxel (x, y, z). + /// @note This operation is not mutex-protected and is intended to be called + /// only by nodes and only in the context of a getValue() or setValue() call. + inline void insert(const Coord& xyz, const NodeT0* node) + { + assert(node); + mKey0 = xyz & ~(NodeT0::DIM-1); + mNode0 = node; + } + inline void insert(const Coord& xyz, const NodeT1* node) + { + assert(node); + mKey1 = xyz & ~(NodeT1::DIM-1); + mNode1 = node; + } + /// No-op in case a tree traversal attemps to insert a node that + /// is not cached by the ValueAccessor + template inline void insert(const Coord&, const NodeT*) {} + + inline bool isHashed0(const Coord& xyz) const + { + return (xyz[0] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[0] + && (xyz[1] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[1] + && (xyz[2] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[2]; + } + inline bool isHashed1(const Coord& xyz) const + { + return (xyz[0] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[0] + && (xyz[1] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[1] + && (xyz[2] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[2]; + } + mutable Coord mKey0; + mutable const NodeT0* mNode0; + mutable Coord mKey1; + mutable const NodeT1* mNode1; +}; // ValueAccessor2 + + +/// @brief Value accessor with three levels of node caching. +/// @details The node cache levels are specified by L0, L1, and L2 +/// with the default values 0, 1 and 2 (defined in the forward declaration) +/// corresponding to a LeafNode, its parent InternalNode, and its parent InternalNode. +/// Since the default configuration of all typed trees and grids, e.g., +/// FloatTree or FloatGrid, has a depth of four, this value accessor is the one +/// used by default. +/// +/// @note This class is for experts only and should rarely be used +/// directly. Instead use ValueAccessor with its default template arguments +template +class ValueAccessor3 : public ValueAccessorBase<_TreeType, IsSafe> +{ +public: + static_assert(_TreeType::DEPTH >= 4, "cache size exceeds tree depth"); + static_assert(L0 < L1, "invalid cache level"); + static_assert(L1 < L2, "invalid cache level"); + static_assert(L2 < _TreeType::RootNodeType::LEVEL, "invalid cache level"); + + using TreeType = _TreeType; + using ValueType = typename TreeType::ValueType; + using RootNodeT = typename TreeType::RootNodeType; + using LeafNodeT = typename TreeType::LeafNodeType; + using BaseT = ValueAccessorBase; + using InvTreeT = typename RootNodeT::NodeChainType; + using NodeT0 = typename boost::mpl::at >::type; + using NodeT1 = typename boost::mpl::at >::type; + using NodeT2 = typename boost::mpl::at >::type; + + /// Constructor from a tree + ValueAccessor3(TreeType& tree) : BaseT(tree), + mKey0(Coord::max()), mNode0(nullptr), + mKey1(Coord::max()), mNode1(nullptr), + mKey2(Coord::max()), mNode2(nullptr) {} + + /// Copy constructor + ValueAccessor3(const ValueAccessor3& other) : BaseT(other) { this->copy(other); } + + /// Asignment operator + ValueAccessor3& operator=(const ValueAccessor3& other) + { + if (&other != this) { + this->BaseT::operator=(other); + this->copy(other); + } + return *this; + } + + /// Return the number of cache levels employed by this ValueAccessor + static Index numCacheLevels() { return 3; } + + /// Virtual destructor + ~ValueAccessor3() override = default; + + /// Return @c true if any of the nodes along the path to the given + /// voxel have been cached. + bool isCached(const Coord& xyz) const + { + assert(BaseT::mTree); + return this->isHashed2(xyz) || this->isHashed1(xyz) || this->isHashed0(xyz); + } + + /// Return the value of the voxel at the given coordinates. + const ValueType& getValue(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return mNode0->getValueAndCache(xyz, this->self()); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->getValueAndCache(xyz, this->self()); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->getValueAndCache(xyz, this->self()); + } + return BaseT::mTree->root().getValueAndCache(xyz, this->self()); + } + + /// Return the active state of the voxel at the given coordinates. + bool isValueOn(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return mNode0->isValueOnAndCache(xyz, this->self()); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->isValueOnAndCache(xyz, this->self()); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->isValueOnAndCache(xyz, this->self()); + } + return BaseT::mTree->root().isValueOnAndCache(xyz, this->self()); + } + + /// Return the active state of the voxel as well as its value + bool probeValue(const Coord& xyz, ValueType& value) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return mNode0->probeValueAndCache(xyz, value, this->self()); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->probeValueAndCache(xyz, value, this->self()); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->probeValueAndCache(xyz, value, this->self()); + } + return BaseT::mTree->root().probeValueAndCache(xyz, value, this->self()); + } + + /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, + /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is + /// implicitly a background voxel). + int getValueDepth(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self()); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return RootNodeT::LEVEL - mNode1->getValueLevelAndCache(xyz, this->self()); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return RootNodeT::LEVEL - mNode2->getValueLevelAndCache(xyz, this->self()); + } + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()); + } + + /// Return @c true if the value of voxel (x, y, z) resides at the leaf level + /// of the tree, i.e., if it is not a tile value. + bool isVoxel(const Coord& xyz) const + { + assert(BaseT::mTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + return mNode0->getValueLevelAndCache(xyz, this->self())==0; + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->getValueLevelAndCache(xyz, this->self())==0; + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->getValueLevelAndCache(xyz, this->self())==0; + } + return BaseT::mTree->root().getValueDepthAndCache(xyz, this->self()) == + static_cast(RootNodeT::LEVEL); + } + + //@{ + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->setValueAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueAndCache(xyz, value, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->setValueAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueAndCache(xyz, value, *this); + } + } + void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } + //@} + + /// Set the value of the voxel at the given coordinate but preserves its active state. + void setValueOnly(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->setValueOnlyAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueOnlyAndCache(xyz, value, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->setValueOnlyAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueOnlyAndCache(xyz, value, *this); + } + } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->setValueOffAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueOffAndCache(xyz, value, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->setValueOffAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueOffAndCache(xyz, value, *this); + } + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->modifyValueAndCache(xyz, op, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->modifyValueAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndCache(xyz, op, *this); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this); + } + } + + /// Set the active state of the voxel at the given coordinates without changing its value. + void setActiveState(const Coord& xyz, bool on = true) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't modify a const tree's values"); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->setActiveStateAndCache(xyz, on, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setActiveStateAndCache(xyz, on, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->setActiveStateAndCache(xyz, on, *this); + } else { + BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this); + } + } + /// Mark the voxel at the given coordinates as active without changing its value. + void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } + /// Mark the voxel at the given coordinates as inactive without changing its value. + void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } + + /// Return the cached node of type @a NodeType. [Mainly for internal use] + template + NodeT* getNode() + { + const NodeT* node = nullptr; + this->getNode(node); + return const_cast(node); + } + + /// Cache the given node, which should lie along the path from the root node to + /// the node containing voxel (x, y, z). [Mainly for internal use] + template + void insertNode(const Coord& xyz, NodeT& node) { this->insert(xyz, &node); } + + /// If a node of the given type exists in the cache, remove it, so that + /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in + /// that node. [Mainly for internal use] + template + void eraseNode() + { + const NodeT* node = nullptr; + this->eraseNode(node); + } + + /// @brief Add the specified leaf to this tree, possibly creating a child branch + /// in the process. If the leaf node already exists, replace it. + void addLeaf(LeafNodeT* leaf) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't add a node to a const tree"); + if (this->isHashed1(leaf->origin())) { + assert(mNode1); + return const_cast(mNode1)->addLeafAndCache(leaf, *this); + } else if (this->isHashed2(leaf->origin())) { + assert(mNode2); + return const_cast(mNode2)->addLeafAndCache(leaf, *this); + } + BaseT::mTree->root().addLeafAndCache(leaf, *this); + } + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree"); + if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->addTileAndCache(level, xyz, value, state, *this); + } if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast(mNode2)->addTileAndCache(level, xyz, value, state, *this); + } + BaseT::mTree->root().addTileAndCache(level, xyz, value, state, *this); + } + + /// @brief @return the leaf node that contains voxel (x, y, z) and + /// if it doesn't exist, create it, but preserve the values and + /// active states of all voxels. + /// + /// Use this method to preallocate a static tree topology over which to + /// safely perform multithreaded processing. + LeafNodeT* touchLeaf(const Coord& xyz) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree"); + if (this->isHashed0(xyz)) { + assert(mNode0); + return const_cast(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->touchLeafAndCache(xyz, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast(mNode2)->touchLeafAndCache(xyz, *this); + } + return BaseT::mTree->root().touchLeafAndCache(xyz, *this); + } + /// @brief @return a pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + template + NodeT* probeNode(const Coord& xyz) + { + assert(BaseT::mTree); + static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree"); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((std::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(const_cast(mNode0)); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->template probeNodeAndCache(xyz, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast(mNode2)->template probeNodeAndCache(xyz, *this); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } else if ((std::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(const_cast(mNode1)); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast(mNode2)->template probeNodeAndCache(xyz, *this); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } else if ((std::is_same::value)) { + if (this->isHashed2(xyz)) { + assert(mNode2); + return reinterpret_cast(const_cast(mNode2)); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } + return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// @brief @return a pointer to the leaf node that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + LeafNodeT* probeLeaf(const Coord& xyz) { return this->template probeNode(xyz); } + + /// @brief @return a const pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + template + const NodeT* probeConstNode(const Coord& xyz) const + { + assert(BaseT::mTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((std::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->template probeConstNodeAndCache(xyz, this->self()); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->template probeConstNodeAndCache(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } else if ((std::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(mNode1); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->template probeConstNodeAndCache(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } else if ((std::is_same::value)) { + if (this->isHashed2(xyz)) { + assert(mNode2); + return reinterpret_cast(mNode2); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } + return nullptr; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// @brief @return a const pointer to the leaf node that contains + /// voxel (x, y, z) and if it doesn't exist, return @c nullptr. + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + return this->template probeConstNode(xyz); + } + const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + + /// Remove all the cached nodes and invalidate the corresponding hash-keys. + void clear() override + { + mKey0 = Coord::max(); + mNode0 = nullptr; + mKey1 = Coord::max(); + mNode1 = nullptr; + mKey2 = Coord::max(); + mNode2 = nullptr; + } + +private: + // Allow nodes to insert themselves into the cache. + template friend class RootNode; + template friend class InternalNode; + template friend class LeafNode; + // Allow trees to deregister themselves. + template friend class Tree; + + // This private method is merely for convenience. + inline ValueAccessor3& self() const { return const_cast(*this); } + + /// Private copy method + inline void copy(const ValueAccessor3& other) + { + mKey0 = other.mKey0; + mNode0 = other.mNode0; + mKey1 = other.mKey1; + mNode1 = other.mNode1; + mKey2 = other.mKey2; + mNode2 = other.mNode2; + } + + /// Prevent this accessor from calling Tree::releaseCache() on a tree that + /// no longer exists. (Called by mTree when it is destroyed.) + void release() override + { + this->BaseT::release(); + this->clear(); + } + void getNode(const NodeT0*& node) { node = mNode0; } + void getNode(const NodeT1*& node) { node = mNode1; } + void getNode(const NodeT2*& node) { node = mNode2; } + void getNode(const RootNodeT*& node) + { + node = (BaseT::mTree ? &BaseT::mTree->root() : nullptr); + } + template void getNode(const OtherNodeType*& node) { node = nullptr; } + + void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = nullptr; } + void eraseNode(const NodeT1*) { mKey1 = Coord::max(); mNode1 = nullptr; } + void eraseNode(const NodeT2*) { mKey2 = Coord::max(); mNode2 = nullptr; } + template void eraseNode(const OtherNodeType*) {} + + /// Cache the given node, which should lie along the path from the root node to + /// the node containing voxel (x, y, z). + /// @note This operation is not mutex-protected and is intended to be called + /// only by nodes and only in the context of a getValue() or setValue() call. + inline void insert(const Coord& xyz, const NodeT0* node) + { + assert(node); + mKey0 = xyz & ~(NodeT0::DIM-1); + mNode0 = node; + } + inline void insert(const Coord& xyz, const NodeT1* node) + { + assert(node); + mKey1 = xyz & ~(NodeT1::DIM-1); + mNode1 = node; + } + inline void insert(const Coord& xyz, const NodeT2* node) + { + assert(node); + mKey2 = xyz & ~(NodeT2::DIM-1); + mNode2 = node; + } + /// No-op in case a tree traversal attemps to insert a node that + /// is not cached by the ValueAccessor + template + inline void insert(const Coord&, const OtherNodeType*) + { + } + inline bool isHashed0(const Coord& xyz) const + { + return (xyz[0] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[0] + && (xyz[1] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[1] + && (xyz[2] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[2]; + } + inline bool isHashed1(const Coord& xyz) const + { + return (xyz[0] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[0] + && (xyz[1] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[1] + && (xyz[2] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[2]; + } + inline bool isHashed2(const Coord& xyz) const + { + return (xyz[0] & ~Coord::ValueType(NodeT2::DIM-1)) == mKey2[0] + && (xyz[1] & ~Coord::ValueType(NodeT2::DIM-1)) == mKey2[1] + && (xyz[2] & ~Coord::ValueType(NodeT2::DIM-1)) == mKey2[2]; + } + mutable Coord mKey0; + mutable const NodeT0* mNode0; + mutable Coord mKey1; + mutable const NodeT1* mNode1; + mutable Coord mKey2; + mutable const NodeT2* mNode2; +}; // ValueAccessor3 + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED diff --git a/openvdb/unittest/CMakeLists.txt b/openvdb/unittest/CMakeLists.txt new file mode 100644 index 00000000..2695492f --- /dev/null +++ b/openvdb/unittest/CMakeLists.txt @@ -0,0 +1,198 @@ +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: MPL-2.0 +# +#[=======================================================================[ + + CMake Configuration for OpenVDB Unit Tests + +#]=======================================================================] + +project(OpenVDBUnitTests) +cmake_minimum_required(VERSION 3.3) +# Monitoring _ROOT variables +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif() + +########################################################################## + +message(STATUS "----------------------------------------------------") +message(STATUS "----------- Configuring OpenVDBUnitTests -----------") +message(STATUS "----------------------------------------------------") + +########################################################################## + +# Collect lib dependencies + +if(NOT OPENVDB_BUILD_CORE) + set(OPENVDB_LIB OpenVDB::openvdb) +else() + set(OPENVDB_LIB openvdb) +endif() + +find_package(CppUnit ${MINIMUM_CPPUNIT_VERSION} REQUIRED) + +set(OPENVDB_TEST_DEPENDENT_LIBS + ${OPENVDB_LIB} + CppUnit::cppunit +) + +########################################################################## + +if(WIN32 AND OPENVDB_DISABLE_BOOST_IMPLICIT_LINKING) + add_definitions(-DBOOST_ALL_NO_LIB) +endif() + +if(WIN32) + # Because of implicit linking! + link_directories(${Boost_LIBRARY_DIR}) +endif() + +if(WIN32) + add_definitions(-D_WIN32 -DNOMINMAX -DOPENVDB_DLL) +endif() + +##### VDB unit tests + +set(UNITTEST_SOURCE_FILES + main.cc + TestAttributeArray.cc + TestAttributeArrayString.cc + TestAttributeGroup.cc + TestAttributeSet.cc + TestBBox.cc + TestClip.cc + TestConjGradient.cc + TestCoord.cc + TestCpt.cc + TestCurl.cc + TestDelayedLoadMetadata.cc + TestDense.cc + TestDenseSparseTools.cc + TestDiagnostics.cc + TestDivergence.cc + TestDoubleMetadata.cc + TestExceptions.cc + TestFile.cc + TestFindActiveValues.cc + TestFloatMetadata.cc + TestGradient.cc + TestGrid.cc + TestGridBbox.cc + TestGridDescriptor.cc + TestGridIO.cc + TestGridTransformer.cc + TestIndexFilter.cc + TestIndexIterator.cc + TestInit.cc + TestInt32Metadata.cc + TestInt64Metadata.cc + TestInternalOrigin.cc + TestLaplacian.cc + TestLeaf.cc + TestLeafBool.cc + TestLeafIO.cc + TestLeafManager.cc + TestLeafMask.cc + TestLeafOrigin.cc + TestLevelSetRayIntersector.cc + TestLevelSetUtil.cc + TestLinearInterp.cc + TestMaps.cc + TestMat4Metadata.cc + TestMath.cc + TestMeanCurvature.cc + TestMeshToVolume.cc + TestMetadata.cc + TestMetadataIO.cc + TestMetaMap.cc + TestMultiResGrid.cc + TestName.cc + TestNodeIterator.cc + TestNodeManager.cc + TestNodeMask.cc + TestParticleAtlas.cc + TestParticlesToLevelSet.cc + TestPointAdvect.cc + TestPointAttribute.cc + TestPointConversion.cc + TestPointCount.cc + TestPointDataLeaf.cc + TestPointDelete.cc + TestPointGroup.cc + TestPointIndexGrid.cc + TestPointMask.cc + TestPointMove.cc + TestPointPartitioner.cc + TestPointSample.cc + TestPointScatter.cc + TestPointsToMask.cc + TestPoissonSolver.cc + TestPotentialFlow.cc + TestPrePostAPI.cc + TestQuadraticInterp.cc + TestQuantizedUnitVec.cc + TestQuat.cc + TestRay.cc + TestStats.cc + TestStream.cc + TestStreamCompression.cc + TestStringMetadata.cc + TestTools.cc + TestTopologyToLevelSet.cc + TestTransform.cc + TestTree.cc + TestTreeCombine.cc + TestTreeGetSetValues.cc + TestTreeIterators.cc + TestTreeVisitor.cc + TestTypes.cc + TestUtil.cc + TestValueAccessor.cc + TestVec2Metadata.cc + TestVec3Metadata.cc + TestVolumeRayIntersector.cc + TestVolumeToMesh.cc + TestVolumeToSpheres.cc +) + +add_executable(vdb_test ${UNITTEST_SOURCE_FILES}) + +if(USE_BLOSC OR OpenVDB_USES_BLOSC) + # Blosc is a hidden dependency for the core library (not exposed in headers) + # so we need to manually link it in here to provide header access for the + # blosc relevant unit tests + find_package(Blosc ${MINIMUM_BLOSC_VERSION} REQUIRED) + list(APPEND OPENVDB_TEST_DEPENDENT_LIBS Blosc::blosc) + target_compile_definitions(vdb_test PRIVATE "-DOPENVDB_USE_BLOSC") +endif() + +target_link_libraries(vdb_test + ${OPENVDB_TEST_DEPENDENT_LIBS} +) + +if(OPENVDB_ENABLE_RPATH) + set(RPATHS "") + # @todo There is probably a better way to do this for imported targets + list(APPEND RPATHS + ${Boost_LIBRARY_DIRS} + ${IlmBase_LIBRARY_DIRS} + ${Log4cplus_LIBRARY_DIRS} + ${Blosc_LIBRARY_DIRS} + ${Tbb_LIBRARY_DIRS} + ${CppUnit_LIBRARY_DIRS} + ) + if(OPENVDB_BUILD_CORE) + list(APPEND RPATHS ${CMAKE_INSTALL_PREFIX}/lib) + else() + list(APPEND RPATHS ${OpenVDB_LIBRARY_DIRS}) + endif() + + list(REMOVE_DUPLICATES RPATHS) + + set_target_properties(vdb_test + PROPERTIES INSTALL_RPATH "${RPATHS}" + ) +endif() + +add_test(vdb_unit_test vdb_test -v) diff --git a/openvdb/unittest/TestAttributeArray.cc b/openvdb/unittest/TestAttributeArray.cc new file mode 100644 index 00000000..b23c4d71 --- /dev/null +++ b/openvdb/unittest/TestAttributeArray.cc @@ -0,0 +1,2453 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include + +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-macros" +#endif +// Boost.Interprocess uses a header-only portion of Boost.DateTime +#define BOOST_DATE_TIME_NO_LIB +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif +#include +#include +#include +#include + +#include // for std::remove() +#include +#include +#include + +#ifdef _MSC_VER +#include // open_existing_file(), close_file() +// boost::interprocess::detail was renamed to boost::interprocess::ipcdetail in Boost 1.48. +// Ensure that both namespaces exist. +namespace boost { namespace interprocess { namespace detail {} namespace ipcdetail {} } } +#include +#else +#include // for struct stat +#include // for stat() +#endif + + +/// @brief io::MappedFile has a private constructor, so declare a class that acts as the friend +class TestMappedFile +{ +public: + static openvdb::io::MappedFile::Ptr create(const std::string& filename) + { + return openvdb::SharedPtr(new openvdb::io::MappedFile(filename)); + } +}; + + +/// @brief Functionality similar to openvdb::util::CpuTimer except with prefix padding and no decimals. +/// +/// @code +/// ProfileTimer timer("algorithm 1"); +/// // code to be timed goes here +/// timer.stop(); +/// @endcode +class ProfileTimer +{ +public: + /// @brief Prints message and starts timer. + /// + /// @note Should normally be followed by a call to stop() + ProfileTimer(const std::string& msg) + { + (void)msg; +#ifdef PROFILE + // padd string to 50 characters + std::string newMsg(msg); + if (newMsg.size() < 50) newMsg.insert(newMsg.end(), 50 - newMsg.size(), ' '); + std::cerr << newMsg << " ... "; +#endif + mT0 = tbb::tick_count::now(); + } + + ~ProfileTimer() { this->stop(); } + + /// Return Time diference in milliseconds since construction or start was called. + inline double delta() const + { + tbb::tick_count::interval_t dt = tbb::tick_count::now() - mT0; + return 1000.0*dt.seconds(); + } + + /// @brief Print time in milliseconds since construction or start was called. + inline void stop() const + { +#ifdef PROFILE + std::stringstream ss; + ss << std::setw(6) << ::round(this->delta()); + std::cerr << "completed in " << ss.str() << " ms\n"; +#endif + } + +private: + tbb::tick_count mT0; +};// ProfileTimer + + +struct ScopedFile +{ + explicit ScopedFile(const std::string& s): pathname(s) {} + ~ScopedFile() { if (!pathname.empty()) std::remove(pathname.c_str()); } + const std::string pathname; +}; + + +using namespace openvdb; +using namespace openvdb::points; + +class TestAttributeArray: public CppUnit::TestCase +{ +public: + void setUp() override { AttributeArray::clearRegistry(); } + void tearDown() override { AttributeArray::clearRegistry(); } + + CPPUNIT_TEST_SUITE(TestAttributeArray); + CPPUNIT_TEST(testFixedPointConversion); + CPPUNIT_TEST(testRegistry); + CPPUNIT_TEST(testAttributeArray); + CPPUNIT_TEST(testAttributeArrayCopy); + CPPUNIT_TEST(testAccessorEval); + CPPUNIT_TEST(testAttributeHandle); + CPPUNIT_TEST(testStrided); + CPPUNIT_TEST(testDelayedLoad); + CPPUNIT_TEST(testQuaternions); + CPPUNIT_TEST(testMatrices); + CPPUNIT_TEST(testProfile); + + CPPUNIT_TEST_SUITE_END(); + + void testFixedPointConversion(); + void testRegistry(); + void testAttributeArray(); + void testAttributeArrayCopy(); + void testAccessorEval(); + void testAttributeHandle(); + void testStrided(); + void testDelayedLoad(); + void testQuaternions(); + void testMatrices(); + void testProfile(); +}; // class TestAttributeArray + +CPPUNIT_TEST_SUITE_REGISTRATION(TestAttributeArray); + + +//////////////////////////////////////// + + +namespace { + +bool +matchingNamePairs(const openvdb::NamePair& lhs, + const openvdb::NamePair& rhs) +{ + if (lhs.first != rhs.first) return false; + if (lhs.second != rhs.second) return false; + + return true; +} + +} // namespace + + +//////////////////////////////////////// + + +void +TestAttributeArray::testFixedPointConversion() +{ + openvdb::math::Transform::Ptr transform(openvdb::math::Transform::createLinearTransform(/*voxelSize=*/0.1)); + + const float value = 33.5688040469035f; + + { + // convert to fixed-point value + + const openvdb::Vec3f worldSpaceValue(value); + const openvdb::Vec3f indexSpaceValue = transform->worldToIndex(worldSpaceValue); + const float voxelSpaceValue = indexSpaceValue.x() - math::Round(indexSpaceValue.x()) + 0.5f; + const uint32_t intValue = floatingPointToFixedPoint(voxelSpaceValue); + + // convert back to floating-point value + + const float newVoxelSpaceValue = fixedPointToFloatingPoint(intValue); + const openvdb::Vec3f newIndexSpaceValue(newVoxelSpaceValue + math::Round(indexSpaceValue.x()) - 0.5f); + const openvdb::Vec3f newWorldSpaceValue = transform->indexToWorld(newIndexSpaceValue); + + const float newValue = newWorldSpaceValue.x(); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(value, newValue, /*tolerance=*/1e-6); + } + + { + // convert to fixed-point value (vector) + + const openvdb::Vec3f worldSpaceValue(value, value+1, value+2); + const openvdb::Vec3f indexSpaceValue = transform->worldToIndex(worldSpaceValue); + const float voxelSpaceValueX = indexSpaceValue.x() - math::Round(indexSpaceValue.x()) + 0.5f; + const float voxelSpaceValueY = indexSpaceValue.y() - math::Round(indexSpaceValue.y()) + 0.5f; + const float voxelSpaceValueZ = indexSpaceValue.z() - math::Round(indexSpaceValue.z()) + 0.5f; + const openvdb::Vec3f voxelSpaceValue(voxelSpaceValueX, voxelSpaceValueY, voxelSpaceValueZ); + const openvdb::math::Vec3 intValue = floatingPointToFixedPoint>(voxelSpaceValue); + + // convert back to floating-point value (vector) + + const openvdb::Vec3f newVoxelSpaceValue = fixedPointToFloatingPoint(intValue); + const float newIndexSpaceValueX = newVoxelSpaceValue.x() + math::Round(indexSpaceValue.x()) - 0.5f; + const float newIndexSpaceValueY = newVoxelSpaceValue.y() + math::Round(indexSpaceValue.y()) - 0.5f; + const float newIndexSpaceValueZ = newVoxelSpaceValue.z() + math::Round(indexSpaceValue.z()) - 0.5f; + const openvdb::Vec3f newIndexSpaceValue(newIndexSpaceValueX, newIndexSpaceValueY, newIndexSpaceValueZ); + const openvdb::Vec3f newWorldSpaceValue = transform->indexToWorld(newIndexSpaceValue); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(worldSpaceValue.x(), newWorldSpaceValue.x(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(worldSpaceValue.y(), newWorldSpaceValue.y(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(worldSpaceValue.z(), newWorldSpaceValue.z(), /*tolerance=*/1e-6); + + } +} + +namespace +{ +// use a dummy factory as TypedAttributeArray::factory is private +static AttributeArray::Ptr factoryInt(Index n, Index strideOrTotalSize, bool constantStride) +{ + return TypedAttributeArray::create(n, strideOrTotalSize, constantStride); +} +} // namespace + +void +TestAttributeArray::testRegistry() +{ + using AttributeF = TypedAttributeArray; + using AttributeFTrnc = TypedAttributeArray; + + AttributeArray::clearRegistry(); + + { // cannot create AttributeArray that is not registered + CPPUNIT_ASSERT(!AttributeArray::isRegistered(AttributeF::attributeType())); + CPPUNIT_ASSERT_THROW(AttributeArray::create(AttributeF::attributeType(), Index(5)), LookupError); + } + + { // throw when attempting to register a float type with an integer factory + CPPUNIT_ASSERT_THROW(AttributeArray::registerType( + AttributeF::attributeType(), factoryInt), KeyError); + } + + // register the attribute array + + AttributeF::registerType(); + + { // can register an AttributeArray with the same value type but different codec + CPPUNIT_ASSERT_NO_THROW(AttributeFTrnc::registerType()); + CPPUNIT_ASSERT(AttributeArray::isRegistered(AttributeF::attributeType())); + CPPUNIT_ASSERT(AttributeArray::isRegistered(AttributeFTrnc::attributeType())); + } + + { // un-registering + AttributeArray::unregisterType(AttributeF::attributeType()); + CPPUNIT_ASSERT(!AttributeArray::isRegistered(AttributeF::attributeType())); + CPPUNIT_ASSERT(AttributeArray::isRegistered(AttributeFTrnc::attributeType())); + } + + { // clearing registry + AttributeF::registerType(); + AttributeArray::clearRegistry(); + CPPUNIT_ASSERT(!AttributeArray::isRegistered(AttributeF::attributeType())); + } +} + +void +TestAttributeArray::testAttributeArray() +{ + using AttributeArrayF = TypedAttributeArray; + using AttributeArrayD = TypedAttributeArray; + + { + AttributeArray::Ptr attr(new AttributeArrayD(50)); + + CPPUNIT_ASSERT_EQUAL(Index(50), attr->size()); + } + + { + AttributeArray::Ptr attr(new AttributeArrayD(50)); + + CPPUNIT_ASSERT_EQUAL(Index(50), attr->size()); + + AttributeArrayD& typedAttr = static_cast(*attr); + + typedAttr.set(0, 0.5); + + double value = 0.0; + typedAttr.get(0, value); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.5), value, /*tolerance=*/double(0.0)); + + // test unsafe methods for get() and set() + + typedAttr.setUnsafe(0, 1.5); + typedAttr.getUnsafe(0, value); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.5), value, /*tolerance=*/double(0.0)); + + // out-of-range get() and set() + CPPUNIT_ASSERT_THROW(typedAttr.set(100, 0.5), openvdb::IndexError); + CPPUNIT_ASSERT_THROW(typedAttr.set(100, 1), openvdb::IndexError); + CPPUNIT_ASSERT_THROW(typedAttr.get(100, value), openvdb::IndexError); + CPPUNIT_ASSERT_THROW(typedAttr.get(100), openvdb::IndexError); + } + +#ifdef NDEBUG + { // test setUnsafe and getUnsafe on uniform arrays + AttributeArrayD::Ptr attr(new AttributeArrayD(50)); + + CPPUNIT_ASSERT_EQUAL(Index(50), attr->size()); + attr->collapse(5.0); + CPPUNIT_ASSERT(attr->isUniform()); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(attr->getUnsafe(10), 5.0, /*tolerance=*/double(0.0)); + CPPUNIT_ASSERT(attr->isUniform()); + + // this is expected behaviour because for performance reasons, array is not implicitly expanded + + attr->setUnsafe(10, 15.0); + CPPUNIT_ASSERT(attr->isUniform()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(attr->getUnsafe(5), 15.0, /*tolerance=*/double(0.0)); + + attr->expand(); + CPPUNIT_ASSERT(!attr->isUniform()); + attr->setUnsafe(10, 25.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(attr->getUnsafe(5), 15.0, /*tolerance=*/double(0.0)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(attr->getUnsafe(10), 25.0, /*tolerance=*/double(0.0)); + } +#endif + + using AttributeArrayC = TypedAttributeArray>; + + { // test hasValueType() + AttributeArray::Ptr attrC(new AttributeArrayC(50)); + AttributeArray::Ptr attrD(new AttributeArrayD(50)); + AttributeArray::Ptr attrF(new AttributeArrayF(50)); + + CPPUNIT_ASSERT(attrD->hasValueType()); + CPPUNIT_ASSERT(attrC->hasValueType()); + CPPUNIT_ASSERT(!attrF->hasValueType()); + + CPPUNIT_ASSERT(!attrD->hasValueType()); + CPPUNIT_ASSERT(!attrC->hasValueType()); + CPPUNIT_ASSERT(attrF->hasValueType()); + } + + { // lots of type checking +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + Index size(50); + { + TypedAttributeArray typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("bool"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(1), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), attr.storageTypeSize()); + CPPUNIT_ASSERT(!attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(!attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("int8"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(1), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), attr.storageTypeSize()); + CPPUNIT_ASSERT(!attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(!attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("int16"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(2), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), attr.storageTypeSize()); + CPPUNIT_ASSERT(!attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(!attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("int32"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(4), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(4), attr.storageTypeSize()); + CPPUNIT_ASSERT(!attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(!attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("int64"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(8), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(8), attr.storageTypeSize()); + CPPUNIT_ASSERT(!attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(!attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + // half is not registered by default, but for complete-ness + TypedAttributeArray typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("half"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(2), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), attr.storageTypeSize()); + CPPUNIT_ASSERT(attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(!attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("float"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(4), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(4), attr.storageTypeSize()); + CPPUNIT_ASSERT(attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(!attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("double"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(8), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(8), attr.storageTypeSize()); + CPPUNIT_ASSERT(attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(!attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray> typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("vec3i"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(12), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(12), attr.storageTypeSize()); + CPPUNIT_ASSERT(!attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(attr.valueTypeIsClass()); + CPPUNIT_ASSERT(attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray> typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("vec3d"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(24), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(24), attr.storageTypeSize()); + CPPUNIT_ASSERT(attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(attr.valueTypeIsClass()); + CPPUNIT_ASSERT(attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray> typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("mat3s"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(36), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(36), attr.storageTypeSize()); + CPPUNIT_ASSERT(attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray> typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("mat4d"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(128), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(128), attr.storageTypeSize()); + CPPUNIT_ASSERT(attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray> typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("quats"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("null"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(16), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(16), attr.storageTypeSize()); + CPPUNIT_ASSERT(attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } + { + TypedAttributeArray typedAttr(size); + AttributeArray& attr(typedAttr); + CPPUNIT_ASSERT_EQUAL(Name("float"), attr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("trnc"), attr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(4), attr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), attr.storageTypeSize()); + CPPUNIT_ASSERT(attr.valueTypeIsFloatingPoint()); + CPPUNIT_ASSERT(!attr.valueTypeIsClass()); + CPPUNIT_ASSERT(!attr.valueTypeIsVector()); + CPPUNIT_ASSERT(!attr.valueTypeIsQuaternion()); + CPPUNIT_ASSERT(!attr.valueTypeIsMatrix()); + } +#endif + } + + { + AttributeArray::Ptr attr(new AttributeArrayC(50)); + + AttributeArrayC& typedAttr = static_cast(*attr); + + typedAttr.set(0, 0.5); + + double value = 0.0; + typedAttr.get(0, value); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.5), value, /*tolerance=*/double(0.0001)); + + // test unsafe methods for get() and set() + + double value2 = 0.0; + typedAttr.setUnsafe(0, double(0.2)); + typedAttr.getUnsafe(0, value2); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.2), value2, /*tolerance=*/double(0.0001)); + } + + using AttributeArrayI = TypedAttributeArray; + + { // Base class API + + AttributeArray::Ptr attr(new AttributeArrayI(50)); + + CPPUNIT_ASSERT_EQUAL(Index(50), attr->size()); + + CPPUNIT_ASSERT_EQUAL((sizeof(AttributeArrayI) + sizeof(int)), attr->memUsage()); + + CPPUNIT_ASSERT(attr->isType()); + CPPUNIT_ASSERT(!attr->isType()); + + CPPUNIT_ASSERT(*attr == *attr); + } + + { // Typed class API + + const Index count = 50; + const size_t uniformMemUsage = sizeof(AttributeArrayI) + sizeof(int); + const size_t expandedMemUsage = sizeof(AttributeArrayI) + count * sizeof(int); + + AttributeArrayI attr(count); + + CPPUNIT_ASSERT_EQUAL(Index(count), attr.size()); + + CPPUNIT_ASSERT_EQUAL(0, attr.get(0)); + CPPUNIT_ASSERT_EQUAL(0, attr.get(10)); + + CPPUNIT_ASSERT(attr.isUniform()); + CPPUNIT_ASSERT_EQUAL(uniformMemUsage, attr.memUsage()); + + attr.set(0, 10); + CPPUNIT_ASSERT(!attr.isUniform()); + CPPUNIT_ASSERT_EQUAL(expandedMemUsage, attr.memUsage()); + + AttributeArrayI attr2(count); + attr2.set(0, 10); + + CPPUNIT_ASSERT(attr == attr2); + + attr.set(1, 5); + + CPPUNIT_ASSERT(!attr.compact()); + CPPUNIT_ASSERT(!attr.isUniform()); + + CPPUNIT_ASSERT_EQUAL(10, attr.get(0)); + CPPUNIT_ASSERT_EQUAL(5, attr.get(1)); + CPPUNIT_ASSERT_EQUAL(0, attr.get(2)); + + attr.collapse(5); + CPPUNIT_ASSERT(attr.isUniform()); + CPPUNIT_ASSERT_EQUAL(uniformMemUsage, attr.memUsage()); + + CPPUNIT_ASSERT_EQUAL(5, attr.get(0)); + CPPUNIT_ASSERT_EQUAL(5, attr.get(20)); + CPPUNIT_ASSERT_EQUAL(5, attr.getUnsafe(20)); + + attr.expand(/*fill=*/false); + CPPUNIT_ASSERT(!attr.isUniform()); + CPPUNIT_ASSERT_EQUAL(expandedMemUsage, attr.memUsage()); + + attr.collapse(5); + + CPPUNIT_ASSERT(attr.isUniform()); + + attr.expand(); + + CPPUNIT_ASSERT(!attr.isUniform()); + CPPUNIT_ASSERT_EQUAL(expandedMemUsage, attr.memUsage()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(5, attr.get(i)); + } + + CPPUNIT_ASSERT(attr.compact()); + CPPUNIT_ASSERT(attr.isUniform()); + CPPUNIT_ASSERT(attr.compact()); + + attr.expand(); + + attr.fill(10); + CPPUNIT_ASSERT(!attr.isUniform()); + CPPUNIT_ASSERT_EQUAL(expandedMemUsage, attr.memUsage()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(10, attr.get(i)); + } + + attr.collapse(7); + CPPUNIT_ASSERT(attr.isUniform()); + CPPUNIT_ASSERT_EQUAL(uniformMemUsage, attr.memUsage()); + + CPPUNIT_ASSERT_EQUAL(7, attr.get(0)); + CPPUNIT_ASSERT_EQUAL(7, attr.get(20)); + + attr.fill(5); + CPPUNIT_ASSERT(attr.isUniform()); + CPPUNIT_ASSERT_EQUAL(uniformMemUsage, attr.memUsage()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(5, attr.get(i)); + } + + CPPUNIT_ASSERT(!attr.isTransient()); + CPPUNIT_ASSERT(!attr.isHidden()); + + attr.setTransient(true); + CPPUNIT_ASSERT(attr.isTransient()); + CPPUNIT_ASSERT(!attr.isHidden()); + + attr.setHidden(true); + CPPUNIT_ASSERT(attr.isTransient()); + CPPUNIT_ASSERT(attr.isHidden()); + + attr.setTransient(false); + CPPUNIT_ASSERT(!attr.isTransient()); + CPPUNIT_ASSERT(attr.isHidden()); + + attr.setHidden(false); + CPPUNIT_ASSERT(!attr.isTransient()); + CPPUNIT_ASSERT(!attr.isHidden()); + + attr.setHidden(true); + + { // test copy construction + AttributeArrayI attrB(attr); + CPPUNIT_ASSERT(matchingNamePairs(attr.type(), attrB.type())); + CPPUNIT_ASSERT_EQUAL(attr.size(), attrB.size()); + CPPUNIT_ASSERT_EQUAL(attr.memUsage(), attrB.memUsage()); + CPPUNIT_ASSERT_EQUAL(attr.isUniform(), attrB.isUniform()); + CPPUNIT_ASSERT_EQUAL(attr.isTransient(), attrB.isTransient()); + CPPUNIT_ASSERT_EQUAL(attr.isHidden(), attrB.isHidden()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attr.get(i), attrB.get(i)); + CPPUNIT_ASSERT_EQUAL(attr.get(i), attrB.getUnsafe(i)); + CPPUNIT_ASSERT_EQUAL(attr.getUnsafe(i), attrB.getUnsafe(i)); + } + } + + { // Equality using an unregistered attribute type + TypedAttributeArray attr1(50); + TypedAttributeArray attr2(50); + + CPPUNIT_ASSERT(attr1 == attr2); + } + + // attribute array must not be uniform for compression + + attr.set(1, 7); + attr.set(2, 8); + attr.set(6, 100); + } + + { // Fixed codec (position range) + AttributeArray::Ptr attr1(new AttributeArrayC(50)); + + AttributeArrayC& fixedPoint = static_cast(*attr1); + + // position range is -0.5 => 0.5 + + fixedPoint.set(0, -0.6); + fixedPoint.set(1, -0.4); + fixedPoint.set(2, 0.4); + fixedPoint.set(3, 0.6); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(-0.5), fixedPoint.get(0), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(-0.4), fixedPoint.get(1), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.4), fixedPoint.get(2), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.5), fixedPoint.get(3), /*tolerance=*/double(0.0001)); + } + + using UnitFixedPointCodec8 = FixedPointCodec; + using AttributeArrayUFxpt8 = TypedAttributeArray; + + { // 8-bit fixed codec (unit range) + AttributeArray::Ptr attr1(new AttributeArrayUFxpt8(50)); + + AttributeArrayUFxpt8& fixedPoint = static_cast(*attr1); + + // unit range is 0.0 => 1.0 + + fixedPoint.set(0, -0.2); + fixedPoint.set(1, 0.3); + fixedPoint.set(2, 0.6); + fixedPoint.set(3, 1.1); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.0), fixedPoint.get(0), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.3), fixedPoint.get(1), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.6), fixedPoint.get(2), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), fixedPoint.get(3), /*tolerance=*/double(0.0001)); + } + + using UnitFixedPointCodec16 = FixedPointCodec; + using AttributeArrayUFxpt16 = TypedAttributeArray; + + { // 16-bit fixed codec (unit range) + AttributeArray::Ptr attr1(new AttributeArrayUFxpt16(50)); + + AttributeArrayUFxpt16& fixedPoint = static_cast(*attr1); + + // unit range is 0.0 => 1.0 + + fixedPoint.set(0, -0.2); + fixedPoint.set(1, 0.3); + fixedPoint.set(2, 0.6); + fixedPoint.set(3, 1.1); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.0), fixedPoint.get(0), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.3), fixedPoint.get(1), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.6), fixedPoint.get(2), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), fixedPoint.get(3), /*tolerance=*/double(0.0001)); + } + + using AttributeArrayU = TypedAttributeArray; + + { // UnitVec codec test + AttributeArray::Ptr attr1(new AttributeArrayU(50)); + + AttributeArrayU& unitVec = static_cast(*attr1); + + // all vectors must be unit length + + const openvdb::Vec3f vec1(1.0, 0.0, 0.0); + const openvdb::Vec3f vec2(openvdb::Vec3f(1.0, 2.0, 3.0).unit()); + const openvdb::Vec3f vec3(openvdb::Vec3f(1.0, 2.0, 300000.0).unit()); + + unitVec.set(0, vec1); + unitVec.set(1, vec2); + unitVec.set(2, vec3); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(vec1.x()), unitVec.get(0).x(), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(vec1.y()), unitVec.get(0).y(), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(vec1.z()), unitVec.get(0).z(), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(vec2.x()), unitVec.get(1).x(), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(vec2.y()), unitVec.get(1).y(), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(vec2.z()), unitVec.get(1).z(), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(vec3.x()), unitVec.get(2).x(), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(vec3.y()), unitVec.get(2).y(), /*tolerance=*/double(0.0001)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(vec3.z()), unitVec.get(2).z(), /*tolerance=*/double(0.0001)); + } + + { // IO + const Index count = 50; + AttributeArrayI attrA(count); + + for (unsigned i = 0; i < unsigned(count); ++i) { + attrA.set(i, int(i)); + } + + attrA.setHidden(true); + + std::ostringstream ostr(std::ios_base::binary); + io::setDataCompression(ostr, io::COMPRESS_BLOSC); + + attrA.write(ostr); + + AttributeArrayI attrB; + + std::istringstream istr(ostr.str(), std::ios_base::binary); + attrB.read(istr); + + CPPUNIT_ASSERT(attrA == attrB); + + AttributeArrayI attrC(count, 3); + attrC.setTransient(true); + + std::ostringstream ostrC(std::ios_base::binary); + attrC.write(ostrC); + + CPPUNIT_ASSERT(ostrC.str().empty()); + + std::ostringstream ostrD(std::ios_base::binary); + attrC.write(ostrD, /*transient=*/true); + + CPPUNIT_ASSERT(!ostrD.str().empty()); + } + + // Registry + AttributeArrayI::registerType(); + + AttributeArray::Ptr attr = + AttributeArray::create( + AttributeArrayI::attributeType(), 34); + + { // Casting + AttributeArray::Ptr array = TypedAttributeArray::create(0); + CPPUNIT_ASSERT_NO_THROW(TypedAttributeArray::cast(*array)); + CPPUNIT_ASSERT_THROW(TypedAttributeArray::cast(*array), TypeError); + + AttributeArray::ConstPtr constArray = array; + CPPUNIT_ASSERT_NO_THROW(TypedAttributeArray::cast(*constArray)); + CPPUNIT_ASSERT_THROW(TypedAttributeArray::cast(*constArray), TypeError); + } +} + +struct VectorWrapper +{ + using T = std::vector>; + + VectorWrapper(const T& _data) : data(_data) { } + operator bool() const { return index < data.size(); } + VectorWrapper& operator++() { index++; return *this; } + Index sourceIndex() const { assert(*this); return data[index].first; } + Index targetIndex() const { assert(*this); return data[index].second; } + +private: + const T& data; + T::size_type index = 0; +}; // struct VectorWrapper + +void +TestAttributeArray::testAttributeArrayCopy() +{ + using AttributeArrayD = TypedAttributeArray; + + Index size(50); + + // initialize some test data + + AttributeArrayD sourceTypedAttr(size); + AttributeArray& sourceAttr(sourceTypedAttr); + CPPUNIT_ASSERT_EQUAL(size, sourceAttr.size()); + + sourceAttr.expand(); + for (Index i = 0; i < size; i++) { + sourceTypedAttr.set(i, double(i)/2); + } + + // initialize source -> target pairs that reverse the order + + std::vector> indexPairs; + for (Index i = 0; i < size; i++) { + indexPairs.push_back(std::make_pair(i, size-i-1)); + } + + // create a new index pair wrapper + + VectorWrapper wrapper(indexPairs); + + // build a target attribute array + + AttributeArrayD targetTypedAttr(size); + AttributeArray& targetAttr(targetTypedAttr); + for (const auto& pair : indexPairs) { + targetTypedAttr.set(pair.second, sourceTypedAttr.get(pair.first)); + } + +#if OPENVDB_ABI_VERSION_NUMBER < 6 + { // verify behaviour with slow virtual function (ABI<6) + AttributeArrayD typedAttr(size); + AttributeArray& attr(typedAttr); + + for (const auto& pair : indexPairs) { + attr.set(pair.second, sourceAttr, pair.first); + } + + CPPUNIT_ASSERT(targetAttr == attr); + } +#else + using AttributeArrayF = TypedAttributeArray; + + { // use std::vector>::begin() as iterator to AttributeArray::copy() + AttributeArrayD typedAttr(size); + AttributeArray& attr(typedAttr); + + attr.copyValues(sourceAttr, wrapper); + + CPPUNIT_ASSERT(targetAttr == attr); + } + + { // attempt to copy values between attribute arrays with different storage sizes + AttributeArrayF typedAttr(size); + AttributeArray& attr(typedAttr); + + CPPUNIT_ASSERT_THROW(attr.copyValues(sourceAttr, wrapper), TypeError); + } + + { // attempt to copy values between integer and float attribute arrays + AttributeArrayF typedAttr(size); + AttributeArray& attr(typedAttr); + + CPPUNIT_ASSERT_THROW(attr.copyValues(sourceAttr, wrapper), TypeError); + } + + { // copy values between attribute arrays with different value types, but the same storage type + // target half array + TypedAttributeArray targetTypedAttr1(size); + AttributeArray& targetAttr1(targetTypedAttr1); + for (Index i = 0; i < size; i++) { + targetTypedAttr1.set(i, + io::RealToHalf::convert(sourceTypedAttr.get(i))); + } + + // truncated float array + TypedAttributeArray targetTypedAttr2(size); + AttributeArray& targetAttr2(targetTypedAttr2); + + targetAttr2.copyValues(targetAttr1, wrapper); + + // equality fails as attribute types are not the same + CPPUNIT_ASSERT(targetAttr2 != targetAttr); + CPPUNIT_ASSERT(targetAttr2.type() != targetAttr.type()); + // however testing value equality succeeds + for (Index i = 0; i < size; i++) { + CPPUNIT_ASSERT(targetTypedAttr2.get(i) == targetTypedAttr.get(i)); + } + } + + { // out-of-range checking + AttributeArrayD typedAttr(size); + AttributeArray& attr(typedAttr); + + decltype(indexPairs) rangeIndexPairs(indexPairs); + + rangeIndexPairs[10].first = size+1; + + VectorWrapper rangeWrapper(rangeIndexPairs); + + CPPUNIT_ASSERT_THROW(attr.copyValues(sourceAttr, rangeWrapper), IndexError); + + rangeIndexPairs[10].first = 0; + + CPPUNIT_ASSERT_NO_THROW(attr.copyValues(sourceAttr, rangeWrapper)); + + rangeIndexPairs[10].second = size+1; + + CPPUNIT_ASSERT_THROW(attr.copyValues(sourceAttr, rangeWrapper), IndexError); + } + + { // source attribute array is uniform + AttributeArrayD uniformTypedAttr(size); + AttributeArray& uniformAttr(uniformTypedAttr); + + uniformTypedAttr.collapse(5.3); + + CPPUNIT_ASSERT(uniformAttr.isUniform()); + + AttributeArrayD typedAttr(size); + AttributeArray& attr(typedAttr); + + CPPUNIT_ASSERT(attr.isUniform()); + + attr.copyValues(uniformAttr, wrapper); + + CPPUNIT_ASSERT(attr.isUniform()); + + attr.copyValues(uniformAttr, wrapper, /*preserveUniformity=*/false); + + CPPUNIT_ASSERT(!attr.isUniform()); + + typedAttr.collapse(1.4); + + CPPUNIT_ASSERT(attr.isUniform()); + + // resize the vector to be smaller than the size of the array + + decltype(indexPairs) subsetIndexPairs(indexPairs); + subsetIndexPairs.resize(size-1); + + decltype(wrapper) subsetWrapper(subsetIndexPairs); + + // now copy the values attempting to preserve uniformity + + attr.copyValues(uniformAttr, subsetWrapper, /*preserveUniformity=*/true); + + // verify that the array cannot be kept uniform + + CPPUNIT_ASSERT(!attr.isUniform()); + } + + { // target attribute array is uniform + AttributeArrayD uniformTypedAttr(size); + AttributeArray& uniformAttr(uniformTypedAttr); + + uniformTypedAttr.collapse(5.3); + + CPPUNIT_ASSERT(uniformAttr.isUniform()); + + AttributeArrayD typedAttr(size); + AttributeArray& attr(typedAttr); + + typedAttr.set(5, 1.2); + typedAttr.set(10, 3.1); + + CPPUNIT_ASSERT(!attr.isUniform()); + + std::vector> uniformIndexPairs; + uniformIndexPairs.push_back(std::make_pair(10, 0)); + uniformIndexPairs.push_back(std::make_pair(5, 0)); + VectorWrapper uniformWrapper(uniformIndexPairs); + + // note that calling copyValues() will implicitly expand the uniform target + + CPPUNIT_ASSERT_NO_THROW(uniformAttr.copyValuesUnsafe(attr, uniformWrapper)); + + CPPUNIT_ASSERT(uniformAttr.isUniform()); + CPPUNIT_ASSERT(uniformTypedAttr.get(0) == typedAttr.get(5)); + } +#endif +} + + +void +TestAttributeArray::testAccessorEval() +{ + using AttributeF = TypedAttributeArray; + + struct TestAccessor + { + static float getterError(const AttributeArray* /*array*/, const Index /*n*/) { + OPENVDB_THROW(NotImplementedError, ""); + } + static void setterError [[noreturn]] (AttributeArray* /*array*/, + const Index /*n*/, const float& /*value*/) + { + OPENVDB_THROW(NotImplementedError, ""); + } + + //static float testGetter(const AttributeArray* array, const Index n) { + // return AccessorEval::get(&getterError, array, n); + //} + //static void testSetter(AttributeArray* array, const Index n, const float& value) { + // AccessorEval::set(&setterError, array, n, value); + //} + }; + + { // test get and set (NullCodec) + AttributeF::Ptr attr = AttributeF::create(10); + attr->collapse(5.0f); + attr->expand(); + + AttributeArray& array = *attr; + + // explicit codec is used here so getter and setter are not called + + AttributeWriteHandle writeHandle(array); + + writeHandle.mSetter = TestAccessor::setterError; + + writeHandle.set(4, 15.0f); + + AttributeHandle handle(array); + + const AttributeArray& constArray(array); + CPPUNIT_ASSERT_EQUAL(&constArray, &handle.array()); + + handle.mGetter = TestAccessor::getterError; + + const float result1 = handle.get(4); + const float result2 = handle.get(6); + + CPPUNIT_ASSERT_EQUAL(15.0f, result1); + CPPUNIT_ASSERT_EQUAL(5.0f, result2); + } + + { // test get and set (UnknownCodec) + AttributeF::Ptr attr = AttributeF::create(10); + attr->collapse(5.0f); + attr->expand(); + + AttributeArray& array = *attr; + + // unknown codec is used here so getter and setter are called + + AttributeWriteHandle writeHandle(array); + + CPPUNIT_ASSERT_EQUAL(&array, &writeHandle.array()); + + writeHandle.mSetter = TestAccessor::setterError; + + CPPUNIT_ASSERT_THROW(writeHandle.set(4, 15.0f), NotImplementedError); + + AttributeHandle handle(array); + + handle.mGetter = TestAccessor::getterError; + + CPPUNIT_ASSERT_THROW(handle.get(4), NotImplementedError); + } +} + + +void +TestAttributeArray::testAttributeHandle() +{ + using namespace openvdb::math; + + using AttributeI = TypedAttributeArray; + using AttributeFH = TypedAttributeArray; + using AttributeVec3f = TypedAttributeArray; + + using AttributeHandleRWI = AttributeWriteHandle; + + AttributeI::registerType(); + AttributeFH::registerType(); + AttributeVec3f::registerType(); + + // create a Descriptor and AttributeSet + + using Descriptor = AttributeSet::Descriptor; + Descriptor::Ptr descr = Descriptor::create(AttributeVec3f::attributeType()); + + unsigned count = 500; + AttributeSet attrSet(descr, /*arrayLength=*/count); + + attrSet.appendAttribute("truncate", AttributeFH::attributeType()); + attrSet.appendAttribute("int", AttributeI::attributeType()); + + // check uniform value implementation + + { + AttributeArray* array = attrSet.get(2); + + AttributeHandleRWI nonExpandingHandle(*array, /*expand=*/false); + CPPUNIT_ASSERT(nonExpandingHandle.isUniform()); + + AttributeHandleRWI handle(*array); + CPPUNIT_ASSERT(!handle.isUniform()); + + CPPUNIT_ASSERT_EQUAL(array->size(), handle.size()); + + CPPUNIT_ASSERT_EQUAL(0, handle.get(0)); + CPPUNIT_ASSERT_EQUAL(0, handle.get(10)); + + handle.set(0, 10); + CPPUNIT_ASSERT(!handle.isUniform()); + + handle.collapse(5); + CPPUNIT_ASSERT(handle.isUniform()); + + CPPUNIT_ASSERT_EQUAL(5, handle.get(0)); + CPPUNIT_ASSERT_EQUAL(5, handle.get(20)); + + handle.expand(); + CPPUNIT_ASSERT(!handle.isUniform()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(5, handle.get(i)); + } + + CPPUNIT_ASSERT(handle.compact()); + CPPUNIT_ASSERT(handle.isUniform()); + + handle.expand(); + + handle.fill(10); + CPPUNIT_ASSERT(!handle.isUniform()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(10, handle.get(i)); + } + + handle.collapse(7); + CPPUNIT_ASSERT(handle.isUniform()); + + CPPUNIT_ASSERT_EQUAL(7, handle.get(0)); + CPPUNIT_ASSERT_EQUAL(7, handle.get(20)); + + handle.fill(5); + CPPUNIT_ASSERT(handle.isUniform()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(5, handle.get(i)); + } + + CPPUNIT_ASSERT(handle.isUniform()); + } + + { + AttributeArray* array = attrSet.get(0); + + AttributeWriteHandle handle(*array); + + handle.set(5, Vec3f(10)); + + CPPUNIT_ASSERT_EQUAL(Vec3f(10), handle.get(5)); + } + + { + AttributeArray* array = attrSet.get(1); + + AttributeWriteHandle handle(*array); + + handle.set(6, float(11)); + + CPPUNIT_ASSERT_EQUAL(float(11), handle.get(6)); + + { + AttributeHandle handleRO(*array); + + CPPUNIT_ASSERT_EQUAL(float(11), handleRO.get(6)); + } + } + + // check values have been correctly set without using handles + + { + AttributeVec3f* array = static_cast(attrSet.get(0)); + + CPPUNIT_ASSERT(array); + + CPPUNIT_ASSERT_EQUAL(Vec3f(10), array->get(5)); + } + + { + AttributeFH* array = static_cast(attrSet.get(1)); + + CPPUNIT_ASSERT(array); + + CPPUNIT_ASSERT_EQUAL(float(11), array->get(6)); + } +} + +void +TestAttributeArray::testStrided() +{ + using AttributeArrayI = TypedAttributeArray; + using StridedHandle = AttributeHandle; + using StridedWriteHandle = AttributeWriteHandle; + + { // non-strided array + AttributeArrayI::Ptr array = AttributeArrayI::create(/*n=*/2, /*stride=*/1); + CPPUNIT_ASSERT(array->hasConstantStride()); + CPPUNIT_ASSERT_EQUAL(Index(1), array->stride()); + CPPUNIT_ASSERT_EQUAL(Index(2), array->size()); + CPPUNIT_ASSERT_EQUAL(Index(2), array->dataSize()); + } + + { // strided array + AttributeArrayI::Ptr array = AttributeArrayI::create(/*n=*/2, /*stride=*/3); + + CPPUNIT_ASSERT(array->hasConstantStride()); + + CPPUNIT_ASSERT_EQUAL(Index(3), array->stride()); + CPPUNIT_ASSERT_EQUAL(Index(2), array->size()); + CPPUNIT_ASSERT_EQUAL(Index(6), array->dataSize()); + CPPUNIT_ASSERT(array->isUniform()); + + CPPUNIT_ASSERT_EQUAL(0, array->get(0)); + CPPUNIT_ASSERT_EQUAL(0, array->get(5)); + CPPUNIT_ASSERT_THROW(array->get(6), IndexError); // out-of-range + + CPPUNIT_ASSERT_NO_THROW(StridedHandle::create(*array)); + CPPUNIT_ASSERT_NO_THROW(StridedWriteHandle::create(*array)); + + array->collapse(10); + + CPPUNIT_ASSERT_EQUAL(int(10), array->get(0)); + CPPUNIT_ASSERT_EQUAL(int(10), array->get(5)); + + array->expand(); + + CPPUNIT_ASSERT_EQUAL(int(10), array->get(0)); + CPPUNIT_ASSERT_EQUAL(int(10), array->get(5)); + + array->collapse(0); + + CPPUNIT_ASSERT_EQUAL(int(0), array->get(0)); + CPPUNIT_ASSERT_EQUAL(int(0), array->get(5)); + + StridedWriteHandle writeHandle(*array); + + writeHandle.set(0, 2, 5); + writeHandle.set(1, 1, 10); + + CPPUNIT_ASSERT_EQUAL(Index(3), writeHandle.stride()); + CPPUNIT_ASSERT_EQUAL(Index(2), writeHandle.size()); + + // non-interleaved: 0 0 5 0 10 0 + + CPPUNIT_ASSERT_EQUAL(5, array->get(2)); + CPPUNIT_ASSERT_EQUAL(10, array->get(4)); + + CPPUNIT_ASSERT_EQUAL(5, writeHandle.get(0, 2)); + CPPUNIT_ASSERT_EQUAL(10, writeHandle.get(1, 1)); + + StridedHandle handle(*array); + CPPUNIT_ASSERT(handle.hasConstantStride()); + + CPPUNIT_ASSERT_EQUAL(5, handle.get(0, 2)); + CPPUNIT_ASSERT_EQUAL(10, handle.get(1, 1)); + + CPPUNIT_ASSERT_EQUAL(Index(3), handle.stride()); + CPPUNIT_ASSERT_EQUAL(Index(2), handle.size()); + +// as of ABI=6, the base memory requirements of an AttributeArray have been lowered +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + size_t arrayMem = 40; +#else + size_t arrayMem = 64; +#endif + CPPUNIT_ASSERT_EQUAL(sizeof(int) * /*size*/3 * /*stride*/2 + arrayMem, array->memUsage()); + } + + { // dynamic stride + AttributeArrayI::Ptr array = AttributeArrayI::create( + /*n=*/2, /*stride=*/7, /*constantStride=*/false); + + CPPUNIT_ASSERT(!array->hasConstantStride()); + + // zero indicates dynamic striding + CPPUNIT_ASSERT_EQUAL(Index(0), array->stride()); + CPPUNIT_ASSERT_EQUAL(Index(2), array->size()); + // the actual array size + CPPUNIT_ASSERT_EQUAL(Index(7), array->dataSize()); + CPPUNIT_ASSERT(array->isUniform()); + + CPPUNIT_ASSERT_EQUAL(0, array->get(0)); + CPPUNIT_ASSERT_EQUAL(0, array->get(6)); + CPPUNIT_ASSERT_THROW(array->get(7), IndexError); // out-of-range + + CPPUNIT_ASSERT_NO_THROW(StridedHandle::create(*array)); + CPPUNIT_ASSERT_NO_THROW(StridedWriteHandle::create(*array)); + + // handle is bound as if a linear array with stride 1 + StridedHandle handle(*array); + CPPUNIT_ASSERT(!handle.hasConstantStride()); + CPPUNIT_ASSERT_EQUAL(Index(1), handle.stride()); + CPPUNIT_ASSERT_EQUAL(array->dataSize(), handle.size()); + } + + { // IO + const Index count = 50, total = 100; + AttributeArrayI attrA(count, total, /*constantStride=*/false); + + for (unsigned i = 0; i < unsigned(total); ++i) { + attrA.set(i, int(i)); + } + + std::ostringstream ostr(std::ios_base::binary); + io::setDataCompression(ostr, io::COMPRESS_BLOSC); + attrA.write(ostr); + + AttributeArrayI attrB; + std::istringstream istr(ostr.str(), std::ios_base::binary); + attrB.read(istr); + + CPPUNIT_ASSERT(attrA == attrB); + } +} + +void +TestAttributeArray::testDelayedLoad() +{ + using AttributeArrayI = TypedAttributeArray; + using AttributeArrayF = TypedAttributeArray; + + AttributeArrayI::registerType(); + AttributeArrayF::registerType(); + + SharedPtr mappedFile; + + io::StreamMetadata::Ptr streamMetadata(new io::StreamMetadata); + + std::string tempDir; + if (const char* dir = std::getenv("TMPDIR")) tempDir = dir; +#ifdef _MSC_VER + if (tempDir.empty()) { + char tempDirBuffer[MAX_PATH+1]; + int tempDirLen = GetTempPath(MAX_PATH+1, tempDirBuffer); + CPPUNIT_ASSERT(tempDirLen > 0 && tempDirLen <= MAX_PATH); + tempDir = tempDirBuffer; + } +#else + if (tempDir.empty()) tempDir = P_tmpdir; +#endif + + { // IO + const Index count = 50; + AttributeArrayI attrA(count); + + for (unsigned i = 0; i < unsigned(count); ++i) { + attrA.set(i, int(i)); + } + + AttributeArrayF attrA2(count); + + std::string filename; + + // write out attribute array to a temp file + { + filename = tempDir + "/openvdb_delayed1"; + std::ofstream fileout(filename.c_str(), std::ios_base::binary); + io::setStreamMetadataPtr(fileout, streamMetadata); + io::setDataCompression(fileout, io::COMPRESS_BLOSC); + + attrA.writeMetadata(fileout, false, /*paged=*/true); + compression::PagedOutputStream outputStreamSize(fileout); + outputStreamSize.setSizeOnly(true); + attrA.writePagedBuffers(outputStreamSize, false); + outputStreamSize.flush(); + compression::PagedOutputStream outputStream(fileout); + outputStream.setSizeOnly(false); + attrA.writePagedBuffers(outputStream, false); + outputStream.flush(); + + attrA2.writeMetadata(fileout, false, /*paged=*/true); + compression::PagedOutputStream outputStreamSize2(fileout); + outputStreamSize2.setSizeOnly(true); + attrA2.writePagedBuffers(outputStreamSize2, false); + outputStreamSize2.flush(); + compression::PagedOutputStream outputStream2(fileout); + outputStream2.setSizeOnly(false); + attrA2.writePagedBuffers(outputStream2, false); + outputStream2.flush(); + + fileout.close(); + } + + mappedFile = TestMappedFile::create(filename); + + // read in using delayed load and check manual loading of data + { + AttributeArrayI attrB; + AttributeArrayF attrB2; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(matchingNamePairs(attrA.type(), attrB.type())); + CPPUNIT_ASSERT_EQUAL(attrA.size(), attrB.size()); + CPPUNIT_ASSERT_EQUAL(attrA.isUniform(), attrB.isUniform()); + CPPUNIT_ASSERT_EQUAL(attrA.isTransient(), attrB.isTransient()); + CPPUNIT_ASSERT_EQUAL(attrA.isHidden(), attrB.isHidden()); + + AttributeArrayI attrBcopy(attrB); + AttributeArrayI attrBequal = attrB; + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + CPPUNIT_ASSERT(attrBcopy.isOutOfCore()); + CPPUNIT_ASSERT(attrBequal.isOutOfCore()); + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + CPPUNIT_ASSERT(!static_cast(attrB).isDataLoaded()); + CPPUNIT_ASSERT(!static_cast(attrBcopy).isDataLoaded()); + CPPUNIT_ASSERT(!static_cast(attrBequal).isDataLoaded()); +#endif + + attrB.loadData(); + attrBcopy.loadData(); + attrBequal.loadData(); + + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + CPPUNIT_ASSERT(!attrBcopy.isOutOfCore()); + CPPUNIT_ASSERT(!attrBequal.isOutOfCore()); + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + CPPUNIT_ASSERT(static_cast(attrB).isDataLoaded()); + CPPUNIT_ASSERT(static_cast(attrBcopy).isDataLoaded()); + CPPUNIT_ASSERT(static_cast(attrBequal).isDataLoaded()); +#endif + + CPPUNIT_ASSERT_EQUAL(attrA.memUsage(), attrB.memUsage()); + CPPUNIT_ASSERT_EQUAL(attrA.memUsage(), attrBcopy.memUsage()); + CPPUNIT_ASSERT_EQUAL(attrA.memUsage(), attrBequal.memUsage()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrB.get(i)); + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrBcopy.get(i)); + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrBequal.get(i)); + } + + attrB2.readMetadata(filein); + compression::PagedInputStream inputStream2(filein); + inputStream2.setSizeOnly(true); + attrB2.readPagedBuffers(inputStream2); + inputStream2.setSizeOnly(false); + attrB2.readPagedBuffers(inputStream2); + + CPPUNIT_ASSERT(matchingNamePairs(attrA2.type(), attrB2.type())); + CPPUNIT_ASSERT_EQUAL(attrA2.size(), attrB2.size()); + CPPUNIT_ASSERT_EQUAL(attrA2.isUniform(), attrB2.isUniform()); + CPPUNIT_ASSERT_EQUAL(attrA2.isTransient(), attrB2.isTransient()); + CPPUNIT_ASSERT_EQUAL(attrA2.isHidden(), attrB2.isHidden()); + + AttributeArrayF attrB2copy(attrB2); + AttributeArrayF attrB2equal = attrB2; + + CPPUNIT_ASSERT(attrB2.isOutOfCore()); + CPPUNIT_ASSERT(attrB2copy.isOutOfCore()); + CPPUNIT_ASSERT(attrB2equal.isOutOfCore()); + attrB2.loadData(); + attrB2copy.loadData(); + attrB2equal.loadData(); + + CPPUNIT_ASSERT(!attrB2.isOutOfCore()); + CPPUNIT_ASSERT(!attrB2copy.isOutOfCore()); + CPPUNIT_ASSERT(!attrB2equal.isOutOfCore()); + + CPPUNIT_ASSERT_EQUAL(attrA2.memUsage(), attrB2.memUsage()); + CPPUNIT_ASSERT_EQUAL(attrA2.memUsage(), attrB2copy.memUsage()); + CPPUNIT_ASSERT_EQUAL(attrA2.memUsage(), attrB2equal.memUsage()); + + CPPUNIT_ASSERT_EQUAL(attrA2.get(0), attrB2.get(0)); + CPPUNIT_ASSERT_EQUAL(attrA2.get(0), attrB2copy.get(0)); + CPPUNIT_ASSERT_EQUAL(attrA2.get(0), attrB2equal.get(0)); + } + + // read in using delayed load and check fill() + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + CPPUNIT_ASSERT(!attrB.isUniform()); + + attrB.fill(5); + + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(5, attrB.get(i)); + } + } + + // read in using delayed load and check streaming (write handle) + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + CPPUNIT_ASSERT(!attrB.isUniform()); + + attrB.setStreaming(true); + + { + AttributeWriteHandle handle(attrB); + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + CPPUNIT_ASSERT(!attrB.isUniform()); + } + + CPPUNIT_ASSERT(!attrB.isUniform()); + } + + // read in using delayed load and check streaming (read handle) + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + CPPUNIT_ASSERT(!attrB.isUniform()); + + attrB.setStreaming(true); + + { + AttributeHandle handle(attrB); + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + CPPUNIT_ASSERT(!attrB.isUniform()); + } + + CPPUNIT_ASSERT(attrB.isUniform()); + } + + // read in using delayed load and check implicit load through get() + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + attrB.get(0); + + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrB.get(i)); + } + } + + // read in using delayed load and check implicit load through compress() + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + } + + // read in using delayed load and check copy and assignment constructors + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + AttributeArrayI attrC(attrB); + AttributeArrayI attrD = attrB; + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + CPPUNIT_ASSERT(attrC.isOutOfCore()); + CPPUNIT_ASSERT(attrD.isOutOfCore()); + + attrB.loadData(); + attrC.loadData(); + attrD.loadData(); + + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + CPPUNIT_ASSERT(!attrC.isOutOfCore()); + CPPUNIT_ASSERT(!attrD.isOutOfCore()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrB.get(i)); + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrC.get(i)); + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrD.get(i)); + } + } + + // read in using delayed load and check implicit load through AttributeHandle + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + AttributeHandle handle(attrB); + + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + } + + // read in using delayed load and check detaching of file (using collapse()) + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + CPPUNIT_ASSERT(!attrB.isUniform()); + + attrB.collapse(); + + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + CPPUNIT_ASSERT(attrB.isUniform()); + + CPPUNIT_ASSERT_EQUAL(0, attrB.get(0)); + } + + // read in and write out using delayed load to check writing out-of-core attributes + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + std::string filename2 = tempDir + "/openvdb_delayed5"; + std::ofstream fileout2(filename2.c_str(), std::ios_base::binary); + io::setStreamMetadataPtr(fileout2, streamMetadata); + io::setDataCompression(fileout2, io::COMPRESS_BLOSC); + + attrB.writeMetadata(fileout2, false, /*paged=*/true); + compression::PagedOutputStream outputStreamSize(fileout2); + outputStreamSize.setSizeOnly(true); + attrB.writePagedBuffers(outputStreamSize, false); + outputStreamSize.flush(); + compression::PagedOutputStream outputStream(fileout2); + outputStream.setSizeOnly(false); + attrB.writePagedBuffers(outputStream, false); + outputStream.flush(); + + fileout2.close(); + + AttributeArrayI attrB2; + + std::ifstream filein2(filename2.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein2, streamMetadata); + io::setMappedFilePtr(filein2, mappedFile); + + attrB2.readMetadata(filein2); + compression::PagedInputStream inputStream2(filein2); + inputStream2.setSizeOnly(true); + attrB2.readPagedBuffers(inputStream2); + inputStream2.setSizeOnly(false); + attrB2.readPagedBuffers(inputStream2); + + CPPUNIT_ASSERT(attrB2.isOutOfCore()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrB.get(i), attrB2.get(i)); + } + + filein2.close(); + } + + // Clean up temp files. + std::remove(mappedFile->filename().c_str()); + std::remove(filename.c_str()); + + AttributeArrayI attrUniform(count); + + // write out uniform attribute array to a temp file + { + filename = tempDir + "/openvdb_delayed2"; + std::ofstream fileout(filename.c_str(), std::ios_base::binary); + io::setStreamMetadataPtr(fileout, streamMetadata); + io::setDataCompression(fileout, io::COMPRESS_BLOSC); + + attrUniform.writeMetadata(fileout, false, /*paged=*/true); + + compression::PagedOutputStream outputStreamSize(fileout); + outputStreamSize.setSizeOnly(true); + attrUniform.writePagedBuffers(outputStreamSize, false); + outputStreamSize.flush(); + compression::PagedOutputStream outputStream(fileout); + outputStream.setSizeOnly(false); + attrUniform.writePagedBuffers(outputStream, false); + outputStream.flush(); + + fileout.close(); + } + + mappedFile = TestMappedFile::create(filename); + + // read in using delayed load and check fill() + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isUniform()); + + attrB.fill(5); + + CPPUNIT_ASSERT(attrB.isUniform()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(5, attrB.get(i)); + } + } + + AttributeArrayI attrStrided(count, /*stride=*/3); + + CPPUNIT_ASSERT_EQUAL(Index(3), attrStrided.stride()); + + // Clean up temp files. + std::remove(mappedFile->filename().c_str()); + std::remove(filename.c_str()); + + // write out strided attribute array to a temp file + { + filename = tempDir + "/openvdb_delayed3"; + std::ofstream fileout(filename.c_str(), std::ios_base::binary); + io::setStreamMetadataPtr(fileout, streamMetadata); + io::setDataCompression(fileout, io::COMPRESS_BLOSC); + + attrStrided.writeMetadata(fileout, false, /*paged=*/true); + + compression::PagedOutputStream outputStreamSize(fileout); + outputStreamSize.setSizeOnly(true); + attrStrided.writePagedBuffers(outputStreamSize, false); + outputStreamSize.flush(); + compression::PagedOutputStream outputStream(fileout); + outputStream.setSizeOnly(false); + attrStrided.writePagedBuffers(outputStream, false); + outputStream.flush(); + + fileout.close(); + } + + mappedFile = TestMappedFile::create(filename); + + // read in using delayed load and check fill() + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT_EQUAL(Index(3), attrB.stride()); + } + + // Clean up temp files. + std::remove(mappedFile->filename().c_str()); + std::remove(filename.c_str()); + + // write out compressed attribute array to a temp file + { + filename = tempDir + "/openvdb_delayed4"; + std::ofstream fileout(filename.c_str(), std::ios_base::binary); + io::setStreamMetadataPtr(fileout, streamMetadata); + io::setDataCompression(fileout, io::COMPRESS_BLOSC); + + attrA.writeMetadata(fileout, false, /*paged=*/true); + + compression::PagedOutputStream outputStreamSize(fileout); + outputStreamSize.setSizeOnly(true); + attrA.writePagedBuffers(outputStreamSize, false); + outputStreamSize.flush(); + compression::PagedOutputStream outputStream(fileout); + outputStream.setSizeOnly(false); + attrA.writePagedBuffers(outputStream, false); + outputStream.flush(); + + fileout.close(); + } + + mappedFile = TestMappedFile::create(filename); + + // read in using delayed load and check manual loading of data + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + attrB.loadData(); + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + + CPPUNIT_ASSERT_EQUAL(attrA.memUsage(), attrB.memUsage()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrB.get(i)); + } + } + + // read in using delayed load and check partial read state + { + std::unique_ptr attrB(new AttributeArrayI); + + CPPUNIT_ASSERT(!(attrB->flags() & AttributeArray::PARTIALREAD)); + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB->readMetadata(filein); + + // PARTIALREAD flag should now be set + CPPUNIT_ASSERT(attrB->flags() & AttributeArray::PARTIALREAD); + + // copy-construct and assign AttributeArray + AttributeArrayI attrC(*attrB); + CPPUNIT_ASSERT(attrC.flags() & AttributeArray::PARTIALREAD); + AttributeArrayI attrD = *attrB; + CPPUNIT_ASSERT(attrD.flags() & AttributeArray::PARTIALREAD); + + // verify deleting attrB is safe + attrB.reset(); + + // verify data is not valid + CPPUNIT_ASSERT(!attrC.validData()); + + { // attempting to write a partially-read AttributeArray throws + std::string filename = tempDir + "/openvdb_partial1"; + ScopedFile f(filename); + std::ofstream fileout(filename.c_str(), std::ios_base::binary); + io::setStreamMetadataPtr(fileout, streamMetadata); + io::setDataCompression(fileout, io::COMPRESS_BLOSC); + + CPPUNIT_ASSERT_THROW(attrC.writeMetadata(fileout, false, /*paged=*/true), IoError); + } + + // continue loading with copy-constructed AttributeArray + + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrC.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrC.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrC.isOutOfCore()); + attrC.loadData(); + CPPUNIT_ASSERT(!attrC.isOutOfCore()); + + // verify data is now valid + CPPUNIT_ASSERT(attrC.validData()); + + CPPUNIT_ASSERT_EQUAL(attrA.memUsage(), attrC.memUsage()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrC.get(i)); + } + } + + // read in using delayed load and check implicit load through get() + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + attrB.get(0); + + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrB.get(i)); + } + } + +#ifdef OPENVDB_USE_BLOSC + // read in using delayed load and check copy and assignment constructors + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + AttributeArrayI attrC(attrB); + AttributeArrayI attrD = attrB; + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + CPPUNIT_ASSERT(attrC.isOutOfCore()); + CPPUNIT_ASSERT(attrD.isOutOfCore()); + + attrB.loadData(); + attrC.loadData(); + attrD.loadData(); + + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + CPPUNIT_ASSERT(!attrC.isOutOfCore()); + CPPUNIT_ASSERT(!attrD.isOutOfCore()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrB.get(i)); + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrC.get(i)); + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrD.get(i)); + } + } + + // read in using delayed load and check implicit load through AttributeHandle + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + attrB.readMetadata(filein); + compression::PagedInputStream inputStream(filein); + inputStream.setSizeOnly(true); + attrB.readPagedBuffers(inputStream); + inputStream.setSizeOnly(false); + attrB.readPagedBuffers(inputStream); + + CPPUNIT_ASSERT(attrB.isOutOfCore()); + + AttributeHandle handle(attrB); + + CPPUNIT_ASSERT(!attrB.isOutOfCore()); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), handle.get(i)); + } + } +#endif + + // Clean up temp files. + std::remove(mappedFile->filename().c_str()); + std::remove(filename.c_str()); + + // write out invalid serialization flags as metadata to a temp file + { + filename = tempDir + "/openvdb_delayed5"; + std::ofstream fileout(filename.c_str(), std::ios_base::binary); + io::setStreamMetadataPtr(fileout, streamMetadata); + io::setDataCompression(fileout, io::COMPRESS_BLOSC); + + // write out unknown serialization flags to check forwards-compatibility + + Index64 bytes(0); + uint8_t flags(0); + uint8_t serializationFlags(Int16(0x10)); + Index size(0); + + fileout.write(reinterpret_cast(&bytes), sizeof(Index64)); + fileout.write(reinterpret_cast(&flags), sizeof(uint8_t)); + fileout.write(reinterpret_cast(&serializationFlags), sizeof(uint8_t)); + fileout.write(reinterpret_cast(&size), sizeof(Index)); + + fileout.close(); + } + + mappedFile = TestMappedFile::create(filename); + + // read in using delayed load and check metadata fail due to serialization flags + { + AttributeArrayI attrB; + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + CPPUNIT_ASSERT_THROW(attrB.readMetadata(filein), openvdb::IoError); + } + + // cleanup temp files + + std::remove(mappedFile->filename().c_str()); + std::remove(filename.c_str()); + } +} + + +void +TestAttributeArray::testQuaternions() +{ + using AttributeQF = TypedAttributeArray>; + using AttributeQD = TypedAttributeArray; + + AttributeQF::registerType(); + AttributeQD::registerType(); + + CPPUNIT_ASSERT(AttributeQF::attributeType().first == "quats"); + CPPUNIT_ASSERT(AttributeQD::attributeType().first == "quatd"); + + AttributeQF test(/*size=*/5); + + AttributeQD orient(/*size=*/10); + + { // set some quaternion values + AttributeWriteHandle orientHandle(orient); + + orientHandle.set(4, QuatR(1, 2, 3, 4)); + orientHandle.set(7, QuatR::identity()); + } + + { // get some quaternion values + AttributeHandle orientHandle(orient); + + CPPUNIT_ASSERT_EQUAL(QuatR::zero(), orientHandle.get(3)); + CPPUNIT_ASSERT_EQUAL(QuatR(1, 2, 3, 4), orientHandle.get(4)); + CPPUNIT_ASSERT_EQUAL(QuatR::identity(), orientHandle.get(7)); + } + + { // create a quaternion array with a zero uniform value + AttributeQD zero(/*size=*/10, /*stride=*/1, /*constantStride=*/true, QuatR::zero()); + + CPPUNIT_ASSERT_EQUAL(QuatR::zero(), zero.get(5)); + } +} + + +void +TestAttributeArray::testMatrices() +{ + typedef TypedAttributeArray AttributeM; + + AttributeM::registerType(); + + CPPUNIT_ASSERT(AttributeM::attributeType().first == "mat4d"); + + AttributeM matrix(/*size=*/10); + + Mat4d testMatrix(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + + { // set some matrix values + AttributeWriteHandle matrixHandle(matrix); + + matrixHandle.set(4, testMatrix); + matrixHandle.set(7, Mat4d::zero()); + } + + { // get some matrix values + AttributeHandle matrixHandle(matrix); + + CPPUNIT_ASSERT_EQUAL(Mat4d::zero(), matrixHandle.get(3)); + CPPUNIT_ASSERT_EQUAL(testMatrix, matrixHandle.get(4)); + CPPUNIT_ASSERT_EQUAL(Mat4d::zero(), matrixHandle.get(7)); + } + + { // create a matrix array with a zero uniform value + AttributeM zero(/*size=*/10, /*stride=*/1, /*constantStride=*/true, Mat4d::zero()); + + CPPUNIT_ASSERT_EQUAL(Mat4d::zero(), zero.get(5)); + } +} + + +namespace profile { + +template +void expand(const Name& prefix, AttrT& attr) +{ + ProfileTimer timer(prefix + ": expand"); + attr.expand(); +} + +template +void set(const Name& prefix, AttrT& attr) +{ + ProfileTimer timer(prefix + ": set"); + const Index size = attr.size(); + for (Index i = 0; i < size; i++) { + attr.setUnsafe(i, typename AttrT::ValueType(i)); + } +} + +template +void setH(const Name& prefix, AttrT& attr) +{ + using ValueType = typename AttrT::ValueType; + ProfileTimer timer(prefix + ": setHandle"); + AttributeWriteHandle handle(attr); + const Index size = attr.size(); + for (Index i = 0; i < size; i++) { + handle.set(i, ValueType(i)); + } +} + +template +void sum(const Name& prefix, const AttrT& attr) +{ + ProfileTimer timer(prefix + ": sum"); + using ValueType = typename AttrT::ValueType; + ValueType sum = 0; + const Index size = attr.size(); + for (Index i = 0; i < size; i++) { + sum += attr.getUnsafe(i); + } + // prevent compiler optimisations removing computation + CPPUNIT_ASSERT(sum!=ValueType()); +} + +template +void sumH(const Name& prefix, const AttrT& attr) +{ + ProfileTimer timer(prefix + ": sumHandle"); + using ValueType = typename AttrT::ValueType; + ValueType sum = 0; + AttributeHandle handle(attr); + for (Index i = 0; i < attr.size(); i++) { + sum += handle.get(i); + } + // prevent compiler optimisations removing computation + CPPUNIT_ASSERT(sum!=ValueType()); +} + +} // namespace profile + +void +TestAttributeArray::testProfile() +{ + using namespace openvdb::util; + using namespace openvdb::math; + + using AttributeArrayF = TypedAttributeArray; + using AttributeArrayF16 = TypedAttributeArray>; + using AttributeArrayF8 = TypedAttributeArray>; + + /////////////////////////////////////////////////// + +#ifdef PROFILE + const size_t elements(1000 * 1000 * 1000); + + std::cerr << std::endl; +#else + const size_t elements(10 * 1000 * 1000); +#endif + + // std::vector + + { + std::vector values; + { + ProfileTimer timer("Vector: resize"); + values.resize(elements); + } + { + ProfileTimer timer("Vector: set"); + for (size_t i = 0; i < elements; i++) { + values[i] = float(i); + } + } + { + ProfileTimer timer("Vector: sum"); + float sum = 0; + for (size_t i = 0; i < elements; i++) { + sum += float(values[i]); + } + // to prevent optimisation clean up + CPPUNIT_ASSERT(sum!=0.0f); + } + } + + // AttributeArray + + { + AttributeArrayF attr(elements); + profile::expand("AttributeArray", attr); + profile::set("AttributeArray", attr); + profile::sum("AttributeArray", attr); + } + + { + AttributeArrayF16 attr(elements); + profile::expand("AttributeArray", attr); + profile::set("AttributeArray", attr); + profile::sum("AttributeArray", attr); + } + + { + AttributeArrayF8 attr(elements); + profile::expand("AttributeArray", attr); + profile::set("AttributeArray", attr); + profile::sum("AttributeArray", attr); + } + + // AttributeHandle (UnknownCodec) + + { + AttributeArrayF attr(elements); + profile::expand("AttributeHandle", attr); + profile::setH("AttributeHandle", attr); + profile::sumH("AttributeHandle", attr); + } + + { + AttributeArrayF16 attr(elements); + profile::expand("AttributeHandle", attr); + profile::setH("AttributeHandle", attr); + profile::sumH("AttributeHandle", attr); + } + + { + AttributeArrayF8 attr(elements); + profile::expand("AttributeHandle", attr); + profile::setH("AttributeHandle", attr); + profile::sumH("AttributeHandle", attr); + } + + // AttributeHandle (explicit codec) + + { + AttributeArrayF attr(elements); + profile::expand("AttributeHandle", attr); + profile::setH("AttributeHandle", attr); + profile::sumH("AttributeHandle", attr); + } + + { + AttributeArrayF16 attr(elements); + profile::expand("AttributeHandle", attr); + profile::setH>("AttributeHandle", attr); + profile::sumH>("AttributeHandle", attr); + } + + { + AttributeArrayF8 attr(elements); + profile::expand("AttributeHandle", attr); + profile::setH>("AttributeHandle", attr); + profile::sumH>("AttributeHandle", attr); + } +} diff --git a/openvdb/unittest/TestAttributeArrayString.cc b/openvdb/unittest/TestAttributeArrayString.cc new file mode 100644 index 00000000..8f3f4657 --- /dev/null +++ b/openvdb/unittest/TestAttributeArrayString.cc @@ -0,0 +1,528 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + +#include + +#include + +using namespace openvdb; +using namespace openvdb::points; + +class TestAttributeArrayString: public CppUnit::TestCase +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestAttributeArrayString); + CPPUNIT_TEST(testStringMetaInserter); + CPPUNIT_TEST(testStringAttribute); + CPPUNIT_TEST(testStringAttributeHandle); + CPPUNIT_TEST(testStringAttributeWriteHandle); + CPPUNIT_TEST(testProfile); + + CPPUNIT_TEST_SUITE_END(); + + void testStringMetaInserter(); + void testStringAttribute(); + void testStringAttributeHandle(); + void testStringAttributeWriteHandle(); + void testProfile(); + +}; // class TestAttributeArrayString + +CPPUNIT_TEST_SUITE_REGISTRATION(TestAttributeArrayString); + + +//////////////////////////////////////// + + +namespace { + +bool +matchingNamePairs(const openvdb::NamePair& lhs, + const openvdb::NamePair& rhs) +{ + if (lhs.first != rhs.first) return false; + if (lhs.second != rhs.second) return false; + + return true; +} + +} // namespace + + +//////////////////////////////////////// + + +void +TestAttributeArrayString::testStringMetaInserter() +{ + MetaMap metadata; + + StringMetaInserter inserter(metadata); + + { // insert one value + inserter.insert("test"); + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(1)); + StringMetadata::Ptr meta = metadata.getMetadata("string:0"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test")); + } + + { // insert another value + inserter.insert("test2"); + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(2)); + StringMetadata::Ptr meta = metadata.getMetadata("string:0"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test")); + meta = metadata.getMetadata("string:1"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test2")); + } + + // remove a value and reset the cache + + metadata.removeMeta("string:1"); + inserter.resetCache(); + + { // re-insert value + inserter.insert("test3"); + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(2)); + StringMetadata::Ptr meta = metadata.getMetadata("string:0"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test")); + meta = metadata.getMetadata("string:1"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test3")); + } + + { // insert and remove to create a gap + inserter.insert("test4"); + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(3)); + metadata.removeMeta("string:1"); + inserter.resetCache(); + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(2)); + StringMetadata::Ptr meta = metadata.getMetadata("string:0"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test")); + meta = metadata.getMetadata("string:2"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test4")); + } + + { // insert to fill gap + inserter.insert("test10"); + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(3)); + StringMetadata::Ptr meta = metadata.getMetadata("string:0"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test")); + meta = metadata.getMetadata("string:1"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test10")); + meta = metadata.getMetadata("string:2"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test4")); + } + + { // insert existing value + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(3)); + inserter.insert("test10"); + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(3)); + } + + metadata.removeMeta("string:0"); + metadata.removeMeta("string:2"); + inserter.resetCache(); + + { // insert other value and string metadata + metadata.insertMeta("int:1", Int32Metadata(5)); + metadata.insertMeta("irrelevant", StringMetadata("irrelevant")); + inserter.resetCache(); + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(3)); + inserter.insert("test15"); + CPPUNIT_ASSERT_EQUAL(metadata.metaCount(), size_t(4)); + StringMetadata::Ptr meta = metadata.getMetadata("string:0"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test15")); + meta = metadata.getMetadata("string:1"); + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT_EQUAL(meta->value(), openvdb::Name("test10")); + } +} + + +//////////////////////////////////////// + + +void +TestAttributeArrayString::testStringAttribute() +{ + { // Typed class API + + const Index count = 50; + StringAttributeArray attr(count); + + CPPUNIT_ASSERT(!attr.isTransient()); + CPPUNIT_ASSERT(!attr.isHidden()); + CPPUNIT_ASSERT(isString(attr)); + + attr.setTransient(true); + CPPUNIT_ASSERT(attr.isTransient()); + CPPUNIT_ASSERT(!attr.isHidden()); + CPPUNIT_ASSERT(isString(attr)); + + attr.setHidden(true); + CPPUNIT_ASSERT(attr.isTransient()); + CPPUNIT_ASSERT(attr.isHidden()); + CPPUNIT_ASSERT(isString(attr)); + + attr.setTransient(false); + CPPUNIT_ASSERT(!attr.isTransient()); + CPPUNIT_ASSERT(attr.isHidden()); + CPPUNIT_ASSERT(isString(attr)); + + StringAttributeArray attrB(attr); + + CPPUNIT_ASSERT(matchingNamePairs(attr.type(), attrB.type())); + CPPUNIT_ASSERT_EQUAL(attr.size(), attrB.size()); + CPPUNIT_ASSERT_EQUAL(attr.memUsage(), attrB.memUsage()); + CPPUNIT_ASSERT_EQUAL(attr.isUniform(), attrB.isUniform()); + CPPUNIT_ASSERT_EQUAL(attr.isTransient(), attrB.isTransient()); + CPPUNIT_ASSERT_EQUAL(attr.isHidden(), attrB.isHidden()); + CPPUNIT_ASSERT_EQUAL(isString(attr), isString(attrB)); + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + AttributeArray& baseAttr(attr); + CPPUNIT_ASSERT_EQUAL(Name(typeNameAsString()), baseAttr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("str"), baseAttr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(4), baseAttr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(4), baseAttr.storageTypeSize()); + CPPUNIT_ASSERT(!baseAttr.valueTypeIsFloatingPoint()); +#endif + } + + { // IO + const Index count = 50; + StringAttributeArray attrA(count); + + for (unsigned i = 0; i < unsigned(count); ++i) { + attrA.set(i, int(i)); + } + + attrA.setHidden(true); + + std::ostringstream ostr(std::ios_base::binary); + attrA.write(ostr); + + StringAttributeArray attrB; + + std::istringstream istr(ostr.str(), std::ios_base::binary); + attrB.read(istr); + + CPPUNIT_ASSERT(matchingNamePairs(attrA.type(), attrB.type())); + CPPUNIT_ASSERT_EQUAL(attrA.size(), attrB.size()); + CPPUNIT_ASSERT_EQUAL(attrA.memUsage(), attrB.memUsage()); + CPPUNIT_ASSERT_EQUAL(attrA.isUniform(), attrB.isUniform()); + CPPUNIT_ASSERT_EQUAL(attrA.isTransient(), attrB.isTransient()); + CPPUNIT_ASSERT_EQUAL(attrA.isHidden(), attrB.isHidden()); + CPPUNIT_ASSERT_EQUAL(isString(attrA), isString(attrB)); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrB.get(i)); + } + } +} + + +void +TestAttributeArrayString::testStringAttributeHandle() +{ + MetaMap metadata; + + StringAttributeArray attr(4); + StringAttributeHandle handle(attr, metadata); + + CPPUNIT_ASSERT_EQUAL(handle.size(), Index(4)); + CPPUNIT_ASSERT_EQUAL(handle.size(), attr.size()); + CPPUNIT_ASSERT_EQUAL(Index(1), handle.stride()); + CPPUNIT_ASSERT(handle.hasConstantStride()); + + { // index 0 should always be an empty string + Name value = handle.get(0); + + CPPUNIT_ASSERT_EQUAL(value, Name("")); + } + + // set first element to 101 + + CPPUNIT_ASSERT(handle.isUniform()); + + attr.set(2, 102); + + CPPUNIT_ASSERT(!handle.isUniform()); + + { // index 101 does not exist as metadata is empty + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("")); + CPPUNIT_ASSERT_THROW(handle.get(2), LookupError); + } + + { // add an element to the metadata for 101 + metadata.insertMeta("string:101", StringMetadata("test101")); + + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("")); + + CPPUNIT_ASSERT_NO_THROW(handle.get(2)); + CPPUNIT_ASSERT_EQUAL(handle.get(2), Name("test101")); + + Name name; + handle.get(name, 2); + + CPPUNIT_ASSERT_EQUAL(name, Name("test101")); + } + + { // add a second element to the metadata + metadata.insertMeta("string:102", StringMetadata("test102")); + + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("")); + + CPPUNIT_ASSERT_NO_THROW(handle.get(2)); + CPPUNIT_ASSERT_EQUAL(handle.get(2), Name("test101")); + + Name name; + handle.get(name, 2); + + CPPUNIT_ASSERT_EQUAL(name, Name("test101")); + } + + { // set two more values in the array + attr.set(0, 103); + attr.set(1, 103); + + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("test102")); + CPPUNIT_ASSERT_EQUAL(handle.get(1), Name("test102")); + CPPUNIT_ASSERT_EQUAL(handle.get(2), Name("test101")); + CPPUNIT_ASSERT_EQUAL(handle.get(3), Name("")); + } + + { // change a value + attr.set(1, 102); + + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("test102")); + CPPUNIT_ASSERT_EQUAL(handle.get(1), Name("test101")); + CPPUNIT_ASSERT_EQUAL(handle.get(2), Name("test101")); + CPPUNIT_ASSERT_EQUAL(handle.get(3), Name("")); + } + + { // cannot use a StringAttributeHandle with a non-string attribute + TypedAttributeArray invalidAttr(50); + CPPUNIT_ASSERT_THROW(StringAttributeHandle(invalidAttr, metadata), TypeError); + } + + // Test stride and hasConstantStride methods for string handles + + { + StringAttributeArray attr(3, 2, true); + StringAttributeHandle handle(attr, metadata); + + CPPUNIT_ASSERT_EQUAL(Index(3), handle.size()); + CPPUNIT_ASSERT_EQUAL(handle.size(), attr.size()); + CPPUNIT_ASSERT_EQUAL(Index(2), handle.stride()); + CPPUNIT_ASSERT(handle.hasConstantStride()); + } + + { + StringAttributeArray attr(4, 10, false); + StringAttributeHandle handle(attr, metadata); + + CPPUNIT_ASSERT_EQUAL(Index(10), handle.size()); + CPPUNIT_ASSERT_EQUAL(Index(4), attr.size()); + CPPUNIT_ASSERT_EQUAL(Index(1), handle.stride()); + CPPUNIT_ASSERT(!handle.hasConstantStride()); + } +} + + +void +TestAttributeArrayString::testStringAttributeWriteHandle() +{ + MetaMap metadata; + + StringAttributeArray attr(4); + StringAttributeWriteHandle handle(attr, metadata); + + { // add some values to metadata + metadata.insertMeta("string:45", StringMetadata("testA")); + metadata.insertMeta("string:90", StringMetadata("testB")); + metadata.insertMeta("string:1000", StringMetadata("testC")); + } + + { // no string values set + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("")); + CPPUNIT_ASSERT_EQUAL(handle.get(1), Name("")); + CPPUNIT_ASSERT_EQUAL(handle.get(2), Name("")); + CPPUNIT_ASSERT_EQUAL(handle.get(3), Name("")); + } + + { // cache not reset since metadata changed + CPPUNIT_ASSERT_THROW(handle.set(1, "testB"), LookupError); + } + + { // empty string always has index 0 + CPPUNIT_ASSERT(handle.contains("")); + } + + { // cache won't contain metadata until it has been reset + CPPUNIT_ASSERT(!handle.contains("testA")); + CPPUNIT_ASSERT(!handle.contains("testB")); + CPPUNIT_ASSERT(!handle.contains("testC")); + } + + handle.resetCache(); + + { // empty string always has index 0 regardless of cache reset + CPPUNIT_ASSERT(handle.contains("")); + } + + { // cache now reset + CPPUNIT_ASSERT(handle.contains("testA")); + CPPUNIT_ASSERT(handle.contains("testB")); + CPPUNIT_ASSERT(handle.contains("testC")); + + CPPUNIT_ASSERT_NO_THROW(handle.set(1, "testB")); + + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("")); + CPPUNIT_ASSERT_EQUAL(handle.get(1), Name("testB")); + CPPUNIT_ASSERT_EQUAL(handle.get(2), Name("")); + CPPUNIT_ASSERT_EQUAL(handle.get(3), Name("")); + } + + { // add another value + handle.set(2, "testC"); + + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("")); + CPPUNIT_ASSERT_EQUAL(handle.get(1), Name("testB")); + CPPUNIT_ASSERT_EQUAL(handle.get(2), Name("testC")); + CPPUNIT_ASSERT_EQUAL(handle.get(3), Name("")); + } + + handle.resetCache(); + + { // compact tests + CPPUNIT_ASSERT(!handle.compact()); + handle.set(0, "testA"); + handle.set(1, "testA"); + handle.set(2, "testA"); + handle.set(3, "testA"); + CPPUNIT_ASSERT(handle.compact()); + CPPUNIT_ASSERT(handle.isUniform()); + } + + { // expand tests + CPPUNIT_ASSERT(handle.isUniform()); + handle.expand(); + CPPUNIT_ASSERT(!handle.isUniform()); + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("testA")); + CPPUNIT_ASSERT_EQUAL(handle.get(1), Name("testA")); + CPPUNIT_ASSERT_EQUAL(handle.get(2), Name("testA")); + CPPUNIT_ASSERT_EQUAL(handle.get(3), Name("testA")); + } + + { // fill tests + CPPUNIT_ASSERT(!handle.isUniform()); + handle.set(3, "testB"); + handle.fill("testC"); + CPPUNIT_ASSERT(!handle.isUniform()); + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("testC")); + CPPUNIT_ASSERT_EQUAL(handle.get(1), Name("testC")); + CPPUNIT_ASSERT_EQUAL(handle.get(2), Name("testC")); + CPPUNIT_ASSERT_EQUAL(handle.get(3), Name("testC")); + } + + { // collapse tests + handle.set(2, "testB"); + handle.collapse("testA"); + CPPUNIT_ASSERT(handle.isUniform()); + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("testA")); + handle.expand(); + handle.set(2, "testB"); + CPPUNIT_ASSERT(!handle.isUniform()); + handle.collapse(); + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("")); + } + + { // empty string tests + handle.collapse(""); + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("")); + } +} + + +void +TestAttributeArrayString::testProfile() +{ + +#ifdef PROFILE + struct Timer : public openvdb::util::CpuTimer {}; + const size_t elements = 1000000; +#else + struct Timer { + void start(const std::string&) {} + void stop() {} + }; + const size_t elements = 10000; +#endif + + MetaMap metadata; + StringMetaInserter inserter(metadata); + + Timer timer; + timer.start("StringMetaInserter initialise"); + + for (size_t i = 0; i < elements; ++i) { + inserter.insert("test_string_" + std::to_string(i)); + } + + timer.stop(); + + for (size_t i = 0; i < elements/2; ++i) { + metadata.removeMeta("test_string_" + std::to_string(i*2)); + } + + timer.start("StringMetaInserter resetCache()"); + + inserter.resetCache(); + + timer.stop(); + timer.start("StringMetaInserter insert duplicates"); + + for (size_t i = 0; i < elements; ++i) { + inserter.insert("test_string_" + std::to_string(i)); + } + + timer.stop(); + + openvdb::points::StringAttributeArray attr(elements); + for (size_t i = 0; i < elements; ++i) { + attr.set(Index(i), Index(i)); + } + + timer.start("StringAttributeWriteHandle construction"); + + openvdb::points::StringAttributeWriteHandle handle(attr, metadata); + + timer.stop(); + timer.start("StringAttributeWriteHandle contains()"); + + // half the calls will miss caches + volatile bool result = false; + for (size_t i = 0; i < elements/2; ++i) { + result |= handle.contains("test_string_" + std::to_string(i*4)); + } + + timer.stop(); +} diff --git a/openvdb/unittest/TestAttributeGroup.cc b/openvdb/unittest/TestAttributeGroup.cc new file mode 100644 index 00000000..55d5a3c2 --- /dev/null +++ b/openvdb/unittest/TestAttributeGroup.cc @@ -0,0 +1,560 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +#include + +#include +#include + +using namespace openvdb; +using namespace openvdb::points; + +class TestAttributeGroup: public CppUnit::TestCase +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestAttributeGroup); + CPPUNIT_TEST(testAttributeGroup); + CPPUNIT_TEST(testAttributeGroupHandle); + CPPUNIT_TEST(testAttributeGroupFilter); + + CPPUNIT_TEST_SUITE_END(); + + void testAttributeGroup(); + void testAttributeGroupHandle(); + void testAttributeGroupFilter(); +}; // class TestAttributeGroup + +CPPUNIT_TEST_SUITE_REGISTRATION(TestAttributeGroup); + + +//////////////////////////////////////// + + +namespace { + +bool +matchingNamePairs(const openvdb::NamePair& lhs, + const openvdb::NamePair& rhs) +{ + if (lhs.first != rhs.first) return false; + if (lhs.second != rhs.second) return false; + + return true; +} + +} // namespace + + +//////////////////////////////////////// + + +void +TestAttributeGroup::testAttributeGroup() +{ + { // Typed class API + + const size_t count = 50; + GroupAttributeArray attr(count); + + CPPUNIT_ASSERT(!attr.isTransient()); + CPPUNIT_ASSERT(!attr.isHidden()); + CPPUNIT_ASSERT(isGroup(attr)); + + attr.setTransient(true); + CPPUNIT_ASSERT(attr.isTransient()); + CPPUNIT_ASSERT(!attr.isHidden()); + CPPUNIT_ASSERT(isGroup(attr)); + + attr.setHidden(true); + CPPUNIT_ASSERT(attr.isTransient()); + CPPUNIT_ASSERT(attr.isHidden()); + CPPUNIT_ASSERT(isGroup(attr)); + + attr.setTransient(false); + CPPUNIT_ASSERT(!attr.isTransient()); + CPPUNIT_ASSERT(attr.isHidden()); + CPPUNIT_ASSERT(isGroup(attr)); + + GroupAttributeArray attrB(attr); + + CPPUNIT_ASSERT(matchingNamePairs(attr.type(), attrB.type())); + CPPUNIT_ASSERT_EQUAL(attr.size(), attrB.size()); + CPPUNIT_ASSERT_EQUAL(attr.memUsage(), attrB.memUsage()); + CPPUNIT_ASSERT_EQUAL(attr.isUniform(), attrB.isUniform()); + CPPUNIT_ASSERT_EQUAL(attr.isTransient(), attrB.isTransient()); + CPPUNIT_ASSERT_EQUAL(attr.isHidden(), attrB.isHidden()); + CPPUNIT_ASSERT_EQUAL(isGroup(attr), isGroup(attrB)); + +#if OPENVDB_ABI_VERSION_NUMBER >= 6 + AttributeArray& baseAttr(attr); + CPPUNIT_ASSERT_EQUAL(Name(typeNameAsString()), baseAttr.valueType()); + CPPUNIT_ASSERT_EQUAL(Name("grp"), baseAttr.codecType()); + CPPUNIT_ASSERT_EQUAL(Index(1), baseAttr.valueTypeSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), baseAttr.storageTypeSize()); + CPPUNIT_ASSERT(!baseAttr.valueTypeIsFloatingPoint()); +#endif + } + + { // casting + TypedAttributeArray floatAttr(4); + AttributeArray& floatArray = floatAttr; + const AttributeArray& constFloatArray = floatAttr; + + CPPUNIT_ASSERT_THROW(GroupAttributeArray::cast(floatArray), TypeError); + CPPUNIT_ASSERT_THROW(GroupAttributeArray::cast(constFloatArray), TypeError); + + GroupAttributeArray groupAttr(4); + AttributeArray& groupArray = groupAttr; + const AttributeArray& constGroupArray = groupAttr; + + CPPUNIT_ASSERT_NO_THROW(GroupAttributeArray::cast(groupArray)); + CPPUNIT_ASSERT_NO_THROW(GroupAttributeArray::cast(constGroupArray)); + } + + { // IO + const size_t count = 50; + GroupAttributeArray attrA(count); + + for (unsigned i = 0; i < unsigned(count); ++i) { + attrA.set(i, int(i)); + } + + attrA.setHidden(true); + + std::ostringstream ostr(std::ios_base::binary); + attrA.write(ostr); + + GroupAttributeArray attrB; + + std::istringstream istr(ostr.str(), std::ios_base::binary); + attrB.read(istr); + + CPPUNIT_ASSERT(matchingNamePairs(attrA.type(), attrB.type())); + CPPUNIT_ASSERT_EQUAL(attrA.size(), attrB.size()); + CPPUNIT_ASSERT_EQUAL(attrA.memUsage(), attrB.memUsage()); + CPPUNIT_ASSERT_EQUAL(attrA.isUniform(), attrB.isUniform()); + CPPUNIT_ASSERT_EQUAL(attrA.isTransient(), attrB.isTransient()); + CPPUNIT_ASSERT_EQUAL(attrA.isHidden(), attrB.isHidden()); + CPPUNIT_ASSERT_EQUAL(isGroup(attrA), isGroup(attrB)); + + for (unsigned i = 0; i < unsigned(count); ++i) { + CPPUNIT_ASSERT_EQUAL(attrA.get(i), attrB.get(i)); + } + } +} + + +void +TestAttributeGroup::testAttributeGroupHandle() +{ + GroupAttributeArray attr(4); + GroupHandle handle(attr, 3); + + CPPUNIT_ASSERT_EQUAL(handle.size(), Index(4)); + CPPUNIT_ASSERT_EQUAL(handle.size(), attr.size()); + + // construct bitmasks + + const GroupType bitmask3 = GroupType(1) << 3; + const GroupType bitmask6 = GroupType(1) << 6; + const GroupType bitmask36 = GroupType(1) << 3 | GroupType(1) << 6; + + // enable attribute 1,2,3 for group permutations of 3 and 6 + attr.set(0, 0); + attr.set(1, bitmask3); + attr.set(2, bitmask6); + attr.set(3, bitmask36); + + CPPUNIT_ASSERT(attr.get(2) != bitmask36); + CPPUNIT_ASSERT_EQUAL(attr.get(3), bitmask36); + + { // group 3 valid for attributes 1 and 3 (using specific offset) + GroupHandle handle3(attr, 3); + + CPPUNIT_ASSERT(!handle3.get(0)); + CPPUNIT_ASSERT(handle3.get(1)); + CPPUNIT_ASSERT(!handle3.get(2)); + CPPUNIT_ASSERT(handle3.get(3)); + } + + { // test group 3 valid for attributes 1 and 3 (unsafe access) + GroupHandle handle3(attr, 3); + + CPPUNIT_ASSERT(!handle3.getUnsafe(0)); + CPPUNIT_ASSERT(handle3.getUnsafe(1)); + CPPUNIT_ASSERT(!handle3.getUnsafe(2)); + CPPUNIT_ASSERT(handle3.getUnsafe(3)); + } + + { // group 6 valid for attributes 2 and 3 (using specific offset) + GroupHandle handle6(attr, 6); + + CPPUNIT_ASSERT(!handle6.get(0)); + CPPUNIT_ASSERT(!handle6.get(1)); + CPPUNIT_ASSERT(handle6.get(2)); + CPPUNIT_ASSERT(handle6.get(3)); + } + + { // groups 3 and 6 only valid for attribute 3 (using bitmask) + GroupHandle handle36(attr, bitmask36, GroupHandle::BitMask()); + + CPPUNIT_ASSERT(!handle36.get(0)); + CPPUNIT_ASSERT(!handle36.get(1)); + CPPUNIT_ASSERT(!handle36.get(2)); + CPPUNIT_ASSERT(handle36.get(3)); + } + + // clear the array + + attr.fill(0); + + CPPUNIT_ASSERT_EQUAL(attr.get(1), GroupType(0)); + + // write handles + + GroupWriteHandle writeHandle3(attr, 3); + GroupWriteHandle writeHandle6(attr, 6); + + // test collapse + + CPPUNIT_ASSERT_EQUAL(writeHandle3.get(1), false); + CPPUNIT_ASSERT_EQUAL(writeHandle6.get(1), false); + + CPPUNIT_ASSERT(writeHandle6.compact()); + CPPUNIT_ASSERT(writeHandle6.isUniform()); + + attr.expand(); + + CPPUNIT_ASSERT(!writeHandle6.isUniform()); + + CPPUNIT_ASSERT(writeHandle3.collapse(true)); + + CPPUNIT_ASSERT(attr.isUniform()); + CPPUNIT_ASSERT(writeHandle3.isUniform()); + CPPUNIT_ASSERT(writeHandle6.isUniform()); + + CPPUNIT_ASSERT_EQUAL(writeHandle3.get(1), true); + CPPUNIT_ASSERT_EQUAL(writeHandle6.get(1), false); + + CPPUNIT_ASSERT(writeHandle3.collapse(false)); + + CPPUNIT_ASSERT(writeHandle3.isUniform()); + CPPUNIT_ASSERT_EQUAL(writeHandle3.get(1), false); + + attr.fill(0); + + writeHandle3.set(1, true); + + CPPUNIT_ASSERT(!attr.isUniform()); + CPPUNIT_ASSERT(!writeHandle3.isUniform()); + CPPUNIT_ASSERT(!writeHandle6.isUniform()); + + CPPUNIT_ASSERT(!writeHandle3.collapse(true)); + + CPPUNIT_ASSERT(!attr.isUniform()); + CPPUNIT_ASSERT(!writeHandle3.isUniform()); + CPPUNIT_ASSERT(!writeHandle6.isUniform()); + + CPPUNIT_ASSERT_EQUAL(writeHandle3.get(1), true); + CPPUNIT_ASSERT_EQUAL(writeHandle6.get(1), false); + + writeHandle6.set(2, true); + + CPPUNIT_ASSERT(!writeHandle3.collapse(false)); + + CPPUNIT_ASSERT(!writeHandle3.isUniform()); + + attr.fill(0); + + writeHandle3.set(1, true); + writeHandle6.set(2, true); + writeHandle3.set(3, true); + writeHandle6.set(3, true); + + { // group 3 valid for attributes 1 and 3 (using specific offset) + GroupHandle handle3(attr, 3); + + CPPUNIT_ASSERT(!handle3.get(0)); + CPPUNIT_ASSERT(handle3.get(1)); + CPPUNIT_ASSERT(!handle3.get(2)); + CPPUNIT_ASSERT(handle3.get(3)); + + CPPUNIT_ASSERT(!writeHandle3.get(0)); + CPPUNIT_ASSERT(writeHandle3.get(1)); + CPPUNIT_ASSERT(!writeHandle3.get(2)); + CPPUNIT_ASSERT(writeHandle3.get(3)); + } + + { // group 6 valid for attributes 2 and 3 (using specific offset) + GroupHandle handle6(attr, 6); + + CPPUNIT_ASSERT(!handle6.get(0)); + CPPUNIT_ASSERT(!handle6.get(1)); + CPPUNIT_ASSERT(handle6.get(2)); + CPPUNIT_ASSERT(handle6.get(3)); + + CPPUNIT_ASSERT(!writeHandle6.get(0)); + CPPUNIT_ASSERT(!writeHandle6.get(1)); + CPPUNIT_ASSERT(writeHandle6.get(2)); + CPPUNIT_ASSERT(writeHandle6.get(3)); + } + + writeHandle3.set(3, false); + + { // group 3 valid for attributes 1 and 3 (using specific offset) + GroupHandle handle3(attr, 3); + + CPPUNIT_ASSERT(!handle3.get(0)); + CPPUNIT_ASSERT(handle3.get(1)); + CPPUNIT_ASSERT(!handle3.get(2)); + CPPUNIT_ASSERT(!handle3.get(3)); + + CPPUNIT_ASSERT(!writeHandle3.get(0)); + CPPUNIT_ASSERT(writeHandle3.get(1)); + CPPUNIT_ASSERT(!writeHandle3.get(2)); + CPPUNIT_ASSERT(!writeHandle3.get(3)); + } + + { // group 6 valid for attributes 2 and 3 (using specific offset) + GroupHandle handle6(attr, 6); + + CPPUNIT_ASSERT(!handle6.get(0)); + CPPUNIT_ASSERT(!handle6.get(1)); + CPPUNIT_ASSERT(handle6.get(2)); + CPPUNIT_ASSERT(handle6.get(3)); + + CPPUNIT_ASSERT(!writeHandle6.get(0)); + CPPUNIT_ASSERT(!writeHandle6.get(1)); + CPPUNIT_ASSERT(writeHandle6.get(2)); + CPPUNIT_ASSERT(writeHandle6.get(3)); + } +} + + +class GroupNotFilter +{ +public: + explicit GroupNotFilter(const AttributeSet::Descriptor::GroupIndex& index) + : mFilter(index) { } + + inline bool initialized() const { return mFilter.initialized(); } + + template + void reset(const LeafT& leaf) { + mFilter.reset(leaf); + } + + template + bool valid(const IterT& iter) const { + return !mFilter.valid(iter); + } + +private: + GroupFilter mFilter; +}; // class GroupNotFilter + + +struct HandleWrapper +{ + HandleWrapper(const GroupHandle& handle) + : mHandle(handle) { } + + GroupHandle groupHandle(const AttributeSet::Descriptor::GroupIndex& /*index*/) const { + return mHandle; + } + +private: + const GroupHandle mHandle; +}; // struct HandleWrapper + + +void +TestAttributeGroup::testAttributeGroupFilter() +{ + using GroupIndex = AttributeSet::Descriptor::GroupIndex; + + GroupIndex zeroIndex; + + typedef IndexIter IndexGroupAllIter; + + GroupAttributeArray attrGroup(4); + const Index32 size = attrGroup.size(); + + { // group values all zero + ValueVoxelCIter indexIter(0, size); + GroupFilter filter(zeroIndex); + CPPUNIT_ASSERT(filter.state() == index::PARTIAL); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 0))); + IndexGroupAllIter iter(indexIter, filter); + + CPPUNIT_ASSERT(!iter); + } + + // enable attributes 0 and 2 for groups 3 and 6 + + const GroupType bitmask = GroupType(1) << 3 | GroupType(1) << 6; + + attrGroup.set(0, bitmask); + attrGroup.set(2, bitmask); + + // index iterator only valid in groups 3 and 6 + { + ValueVoxelCIter indexIter(0, size); + + GroupFilter filter(zeroIndex); + + filter.reset(HandleWrapper(GroupHandle(attrGroup, 0))); + CPPUNIT_ASSERT(!IndexGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 1))); + CPPUNIT_ASSERT(!IndexGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 2))); + CPPUNIT_ASSERT(!IndexGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 3))); + CPPUNIT_ASSERT(IndexGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 4))); + CPPUNIT_ASSERT(!IndexGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 5))); + CPPUNIT_ASSERT(!IndexGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 6))); + CPPUNIT_ASSERT(IndexGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 7))); + CPPUNIT_ASSERT(!IndexGroupAllIter(indexIter, filter)); + } + + attrGroup.set(1, bitmask); + attrGroup.set(3, bitmask); + + using IndexNotGroupAllIter = IndexIter; + + // index iterator only not valid in groups 3 and 6 + { + ValueVoxelCIter indexIter(0, size); + + GroupNotFilter filter(zeroIndex); + + filter.reset(HandleWrapper(GroupHandle(attrGroup, 0))); + CPPUNIT_ASSERT(IndexNotGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 1))); + CPPUNIT_ASSERT(IndexNotGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 2))); + CPPUNIT_ASSERT(IndexNotGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 3))); + CPPUNIT_ASSERT(!IndexNotGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 4))); + CPPUNIT_ASSERT(IndexNotGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 5))); + CPPUNIT_ASSERT(IndexNotGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 6))); + CPPUNIT_ASSERT(!IndexNotGroupAllIter(indexIter, filter)); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 7))); + CPPUNIT_ASSERT(IndexNotGroupAllIter(indexIter, filter)); + } + + // clear group membership for attributes 1 and 3 + + attrGroup.set(1, GroupType(0)); + attrGroup.set(3, GroupType(0)); + + { // index in group next + ValueVoxelCIter indexIter(0, size); + GroupFilter filter(zeroIndex); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 3))); + IndexGroupAllIter iter(indexIter, filter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(0)); + + CPPUNIT_ASSERT(iter.next()); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(2)); + + CPPUNIT_ASSERT(!iter.next()); + } + + { // index in group prefix ++ + ValueVoxelCIter indexIter(0, size); + GroupFilter filter(zeroIndex); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 3))); + IndexGroupAllIter iter(indexIter, filter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(0)); + + IndexGroupAllIter old = ++iter; + CPPUNIT_ASSERT_EQUAL(*old, Index32(2)); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(2)); + + CPPUNIT_ASSERT(!iter.next()); + } + + { // index in group postfix ++/-- + ValueVoxelCIter indexIter(0, size); + GroupFilter filter(zeroIndex); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 3))); + IndexGroupAllIter iter(indexIter, filter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(0)); + + IndexGroupAllIter old = iter++; + CPPUNIT_ASSERT_EQUAL(*old, Index32(0)); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(2)); + + CPPUNIT_ASSERT(!iter.next()); + } + + { // index not in group next + ValueVoxelCIter indexIter(0, size); + GroupNotFilter filter(zeroIndex); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 3))); + IndexNotGroupAllIter iter(indexIter, filter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(1)); + + CPPUNIT_ASSERT(iter.next()); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(3)); + + CPPUNIT_ASSERT(!iter.next()); + } + + { // index not in group prefix ++ + ValueVoxelCIter indexIter(0, size); + GroupNotFilter filter(zeroIndex); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 3))); + IndexNotGroupAllIter iter(indexIter, filter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(1)); + + IndexNotGroupAllIter old = ++iter; + CPPUNIT_ASSERT_EQUAL(*old, Index32(3)); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(3)); + + CPPUNIT_ASSERT(!iter.next()); + } + + { // index not in group postfix ++ + ValueVoxelCIter indexIter(0, size); + GroupNotFilter filter(zeroIndex); + filter.reset(HandleWrapper(GroupHandle(attrGroup, 3))); + IndexNotGroupAllIter iter(indexIter, filter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(1)); + + IndexNotGroupAllIter old = iter++; + CPPUNIT_ASSERT_EQUAL(*old, Index32(1)); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(3)); + + CPPUNIT_ASSERT(!iter.next()); + } +} diff --git a/openvdb/unittest/TestAttributeSet.cc b/openvdb/unittest/TestAttributeSet.cc new file mode 100644 index 00000000..638b6078 --- /dev/null +++ b/openvdb/unittest/TestAttributeSet.cc @@ -0,0 +1,1108 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include + +#include +#include + +class TestAttributeSet: public CppUnit::TestCase +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestAttributeSet); + CPPUNIT_TEST(testAttributeSetDescriptor); + CPPUNIT_TEST(testAttributeSet); + CPPUNIT_TEST(testAttributeSetGroups); + + CPPUNIT_TEST_SUITE_END(); + + void testAttributeSetDescriptor(); + void testAttributeSet(); + void testAttributeSetGroups(); +}; // class TestAttributeSet + +CPPUNIT_TEST_SUITE_REGISTRATION(TestAttributeSet); + + +//////////////////////////////////////// + + +using namespace openvdb; +using namespace openvdb::points; + +namespace { + +bool +matchingAttributeSets(const AttributeSet& lhs, + const AttributeSet& rhs) +{ + if (lhs.size() != rhs.size()) return false; + if (lhs.memUsage() != rhs.memUsage()) return false; + if (lhs.descriptor() != rhs.descriptor()) return false; + + for (size_t n = 0, N = lhs.size(); n < N; ++n) { + + const AttributeArray* a = lhs.getConst(n); + const AttributeArray* b = rhs.getConst(n); + + if (a->size() != b->size()) return false; + if (a->isUniform() != b->isUniform()) return false; + if (a->isHidden() != b->isHidden()) return false; + if (a->type() != b->type()) return false; + } + + return true; +} + +bool +attributeSetMatchesDescriptor( const AttributeSet& attrSet, + const AttributeSet::Descriptor& descriptor) +{ + if (descriptor.size() != attrSet.size()) return false; + + // check default metadata + + const openvdb::MetaMap& meta1 = descriptor.getMetadata(); + const openvdb::MetaMap& meta2 = attrSet.descriptor().getMetadata(); + + // build vector of all default keys + + std::vector defaultKeys; + + for (auto it = meta1.beginMeta(), itEnd = meta1.endMeta(); it != itEnd; ++it) + { + const openvdb::Name& name = it->first; + + if (name.compare(0, 8, "default:") == 0) { + defaultKeys.push_back(name); + } + } + + for (auto it = meta2.beginMeta(), itEnd = meta2.endMeta(); it != itEnd; ++it) + { + const openvdb::Name& name = it->first; + + if (name.compare(0, 8, "default:") == 0) { + if (std::find(defaultKeys.begin(), defaultKeys.end(), name) != defaultKeys.end()) { + defaultKeys.push_back(name); + } + } + } + + // compare metadata value from each metamap + + for (const openvdb::Name& name : defaultKeys) { + openvdb::Metadata::ConstPtr metaValue1 = meta1[name]; + openvdb::Metadata::ConstPtr metaValue2 = meta2[name]; + + if (!metaValue1) return false; + if (!metaValue2) return false; + + if (*metaValue1 != *metaValue2) return false; + } + + // ensure descriptor and attributes are still in sync + + for (const auto& namePos : attrSet.descriptor().map()) { + const size_t pos = descriptor.find(namePos.first); + + if (pos != size_t(namePos.second)) return false; + if (descriptor.type(pos) != attrSet.get(pos)->type()) return false; + } + + return true; +} + +bool testStringVector(std::vector& input) +{ + return input.empty(); +} + +bool testStringVector(std::vector& input, const std::string& name1) +{ + if (input.size() != 1) return false; + if (input[0] != name1) return false; + return true; +} + +bool testStringVector(std::vector& input, + const std::string& name1, const std::string& name2) +{ + if (input.size() != 2) return false; + if (input[0] != name1) return false; + if (input[1] != name2) return false; + return true; +} + +} //unnamed namespace + + +//////////////////////////////////////// + + +void +TestAttributeSet::testAttributeSetDescriptor() +{ + // Define and register some common attribute types + using AttributeVec3f = TypedAttributeArray; + using AttributeS = TypedAttributeArray; + using AttributeI = TypedAttributeArray; + + using Descriptor = AttributeSet::Descriptor; + + { // error on invalid construction + Descriptor::Ptr invalidDescr = Descriptor::create(AttributeVec3f::attributeType()); + CPPUNIT_ASSERT_THROW(invalidDescr->duplicateAppend("P", AttributeS::attributeType()), + openvdb::KeyError); + } + + Descriptor::Ptr descrA = Descriptor::create(AttributeVec3f::attributeType()); + + descrA = descrA->duplicateAppend("density", AttributeS::attributeType()); + descrA = descrA->duplicateAppend("id", AttributeI::attributeType()); + + Descriptor::Ptr descrB = Descriptor::create(AttributeVec3f::attributeType()); + + descrB = descrB->duplicateAppend("density", AttributeS::attributeType()); + descrB = descrB->duplicateAppend("id", AttributeI::attributeType()); + + CPPUNIT_ASSERT_EQUAL(descrA->size(), descrB->size()); + + CPPUNIT_ASSERT(*descrA == *descrB); + + descrB->setGroup("test", size_t(0)); + descrB->setGroup("test2", size_t(1)); + + Descriptor descrC(*descrB); + + CPPUNIT_ASSERT(descrB->hasSameAttributes(descrC)); + CPPUNIT_ASSERT(descrC.hasGroup("test")); + CPPUNIT_ASSERT(*descrB == descrC); + + descrC.dropGroup("test"); + descrC.dropGroup("test2"); + + CPPUNIT_ASSERT(!descrB->hasSameAttributes(descrC)); + CPPUNIT_ASSERT(!descrC.hasGroup("test")); + CPPUNIT_ASSERT(*descrB != descrC); + + descrC.setGroup("test2", size_t(1)); + descrC.setGroup("test3", size_t(0)); + + CPPUNIT_ASSERT(!descrB->hasSameAttributes(descrC)); + + descrC.dropGroup("test3"); + descrC.setGroup("test", size_t(0)); + + CPPUNIT_ASSERT(descrB->hasSameAttributes(descrC)); + + Descriptor::Inserter names; + names.add("P", AttributeVec3f::attributeType()); + names.add("density", AttributeS::attributeType()); + names.add("id", AttributeI::attributeType()); + + // rebuild NameAndTypeVec + + Descriptor::NameAndTypeVec rebuildNames; + descrA->appendTo(rebuildNames); + + CPPUNIT_ASSERT_EQUAL(rebuildNames.size(), names.vec.size()); + + for (auto itA = rebuildNames.cbegin(), itB = names.vec.cbegin(), + itEndA = rebuildNames.cend(), itEndB = names.vec.cend(); + itA != itEndA && itB != itEndB; ++itA, ++itB) { + CPPUNIT_ASSERT_EQUAL(itA->name, itB->name); + CPPUNIT_ASSERT_EQUAL(itA->type.first, itB->type.first); + CPPUNIT_ASSERT_EQUAL(itA->type.second, itB->type.second); + } + + Descriptor::NameToPosMap groupMap; + openvdb::MetaMap metadata; + + // hasSameAttributes (note: uses protected create methods) + { + Descriptor::Ptr descr1 = Descriptor::create(Descriptor::Inserter() + .add("P", AttributeVec3f::attributeType()) + .add("test", AttributeI::attributeType()) + .add("id", AttributeI::attributeType()) + .vec, groupMap, metadata); + + // test same names with different types, should be false + Descriptor::Ptr descr2 = Descriptor::create(Descriptor::Inserter() + .add("P", AttributeVec3f::attributeType()) + .add("test", AttributeS::attributeType()) + .add("id", AttributeI::attributeType()) + .vec, groupMap, metadata); + + CPPUNIT_ASSERT(!descr1->hasSameAttributes(*descr2)); + + // test different names, should be false + Descriptor::Ptr descr3 = Descriptor::create(Descriptor::Inserter() + .add("P", AttributeVec3f::attributeType()) + .add("test2", AttributeI::attributeType()) + .add("id", AttributeI::attributeType()) + .vec, groupMap, metadata); + + CPPUNIT_ASSERT(!descr1->hasSameAttributes(*descr3)); + + // test same names and types but different order, should be true + Descriptor::Ptr descr4 = Descriptor::create(Descriptor::Inserter() + .add("test", AttributeI::attributeType()) + .add("id", AttributeI::attributeType()) + .add("P", AttributeVec3f::attributeType()) + .vec, groupMap, metadata); + + CPPUNIT_ASSERT(descr1->hasSameAttributes(*descr4)); + } + + { // Test uniqueName + Descriptor::Inserter names2; + Descriptor::Ptr emptyDescr = Descriptor::create(AttributeVec3f::attributeType()); + const openvdb::Name uniqueNameEmpty = emptyDescr->uniqueName("test"); + CPPUNIT_ASSERT_EQUAL(uniqueNameEmpty, openvdb::Name("test")); + + names2.add("test", AttributeS::attributeType()); + names2.add("test1", AttributeI::attributeType()); + + Descriptor::Ptr descr1 = Descriptor::create(names2.vec, groupMap, metadata); + + const openvdb::Name uniqueName1 = descr1->uniqueName("test"); + CPPUNIT_ASSERT_EQUAL(uniqueName1, openvdb::Name("test0")); + + Descriptor::Ptr descr2 = descr1->duplicateAppend(uniqueName1, AttributeI::attributeType()); + + const openvdb::Name uniqueName2 = descr2->uniqueName("test"); + CPPUNIT_ASSERT_EQUAL(uniqueName2, openvdb::Name("test2")); + } + + { // Test name validity + + CPPUNIT_ASSERT(Descriptor::validName("test1")); + CPPUNIT_ASSERT(Descriptor::validName("abc_def")); + CPPUNIT_ASSERT(Descriptor::validName("abc|def")); + CPPUNIT_ASSERT(Descriptor::validName("abc:def")); + + CPPUNIT_ASSERT(!Descriptor::validName("")); + CPPUNIT_ASSERT(!Descriptor::validName("test1!")); + CPPUNIT_ASSERT(!Descriptor::validName("abc=def")); + CPPUNIT_ASSERT(!Descriptor::validName("abc def")); + CPPUNIT_ASSERT(!Descriptor::validName("abc*def")); + } + + { // Test enforcement of valid names + Descriptor::Ptr descr = Descriptor::create(Descriptor::Inserter().add( + "test1", AttributeS::attributeType()).vec, groupMap, metadata); + CPPUNIT_ASSERT_THROW(descr->rename("test1", "test1!"), openvdb::RuntimeError); + CPPUNIT_ASSERT_THROW(descr->setGroup("group1!", 1), openvdb::RuntimeError); + + Descriptor::NameAndType invalidAttr("test1!", AttributeS::attributeType()); + CPPUNIT_ASSERT_THROW(descr->duplicateAppend(invalidAttr.name, invalidAttr.type), + openvdb::RuntimeError); + + const openvdb::Index64 offset(0); + const openvdb::Index64 zeroLength(0); + const openvdb::Index64 oneLength(1); + + // write a stream with an invalid attribute + std::ostringstream attrOstr(std::ios_base::binary); + + attrOstr.write(reinterpret_cast(&oneLength), sizeof(openvdb::Index64)); + openvdb::writeString(attrOstr, invalidAttr.type.first); + openvdb::writeString(attrOstr, invalidAttr.type.second); + openvdb::writeString(attrOstr, invalidAttr.name); + attrOstr.write(reinterpret_cast(&offset), sizeof(openvdb::Index64)); + + attrOstr.write(reinterpret_cast(&zeroLength), sizeof(openvdb::Index64)); + + // write a stream with an invalid group + std::ostringstream groupOstr(std::ios_base::binary); + + groupOstr.write(reinterpret_cast(&zeroLength), sizeof(openvdb::Index64)); + + groupOstr.write(reinterpret_cast(&oneLength), sizeof(openvdb::Index64)); + openvdb::writeString(groupOstr, "group1!"); + groupOstr.write(reinterpret_cast(&offset), sizeof(openvdb::Index64)); + + // read the streams back + Descriptor inputDescr; + std::istringstream attrIstr(attrOstr.str(), std::ios_base::binary); + CPPUNIT_ASSERT_THROW(inputDescr.read(attrIstr), openvdb::IoError); + std::istringstream groupIstr(groupOstr.str(), std::ios_base::binary); + CPPUNIT_ASSERT_THROW(inputDescr.read(groupIstr), openvdb::IoError); + } + + { // Test empty string parse + std::vector includeNames; + std::vector excludeNames; + Descriptor::parseNames(includeNames, excludeNames, ""); + CPPUNIT_ASSERT(testStringVector(includeNames)); + CPPUNIT_ASSERT(testStringVector(excludeNames)); + } + + { // Test single token parse + std::vector includeNames; + std::vector excludeNames; + bool includeAll = false; + Descriptor::parseNames(includeNames, excludeNames, includeAll, "group1"); + CPPUNIT_ASSERT(!includeAll); + CPPUNIT_ASSERT(testStringVector(includeNames, "group1")); + CPPUNIT_ASSERT(testStringVector(excludeNames)); + } + + { // Test parse with two include tokens + std::vector includeNames; + std::vector excludeNames; + Descriptor::parseNames(includeNames, excludeNames, "group1 group2"); + CPPUNIT_ASSERT(testStringVector(includeNames, "group1", "group2")); + CPPUNIT_ASSERT(testStringVector(excludeNames)); + } + + { // Test parse with one include and one ^ exclude token + std::vector includeNames; + std::vector excludeNames; + Descriptor::parseNames(includeNames, excludeNames, "group1 ^group2"); + CPPUNIT_ASSERT(testStringVector(includeNames, "group1")); + CPPUNIT_ASSERT(testStringVector(excludeNames, "group2")); + } + + { // Test parse with one include and one ! exclude token + std::vector includeNames; + std::vector excludeNames; + Descriptor::parseNames(includeNames, excludeNames, "group1 !group2"); + CPPUNIT_ASSERT(testStringVector(includeNames, "group1")); + CPPUNIT_ASSERT(testStringVector(excludeNames, "group2")); + } + + { // Test parse one include and one exclude backwards + std::vector includeNames; + std::vector excludeNames; + Descriptor::parseNames(includeNames, excludeNames, "^group1 group2"); + CPPUNIT_ASSERT(testStringVector(includeNames, "group2")); + CPPUNIT_ASSERT(testStringVector(excludeNames, "group1")); + } + + { // Test parse with two exclude tokens + std::vector includeNames; + std::vector excludeNames; + Descriptor::parseNames(includeNames, excludeNames, "^group1 ^group2"); + CPPUNIT_ASSERT(testStringVector(includeNames)); + CPPUNIT_ASSERT(testStringVector(excludeNames, "group1", "group2")); + } + + { // Test parse multiple includes and excludes at the same time + std::vector includeNames; + std::vector excludeNames; + Descriptor::parseNames(includeNames, excludeNames, "group1 ^group2 ^group3 group4"); + CPPUNIT_ASSERT(testStringVector(includeNames, "group1", "group4")); + CPPUNIT_ASSERT(testStringVector(excludeNames, "group2", "group3")); + } + + { // Test parse misplaced negate character failure + std::vector includeNames; + std::vector excludeNames; + CPPUNIT_ASSERT_THROW(Descriptor::parseNames(includeNames, excludeNames, "group1 ^ group2"), + openvdb::RuntimeError); + } + + { // Test parse (*) character + std::vector includeNames; + std::vector excludeNames; + bool includeAll = false; + Descriptor::parseNames(includeNames, excludeNames, includeAll, "*"); + CPPUNIT_ASSERT(includeAll); + CPPUNIT_ASSERT(testStringVector(includeNames)); + CPPUNIT_ASSERT(testStringVector(excludeNames)); + } + + { // Test parse invalid character failure + std::vector includeNames; + std::vector excludeNames; + CPPUNIT_ASSERT_THROW(Descriptor::parseNames(includeNames, excludeNames, "group$1"), + openvdb::RuntimeError); + } + + { // Test hasGroup(), setGroup(), dropGroup(), clearGroups() + Descriptor descr; + + CPPUNIT_ASSERT(!descr.hasGroup("test1")); + + descr.setGroup("test1", 1); + + CPPUNIT_ASSERT(descr.hasGroup("test1")); + CPPUNIT_ASSERT_EQUAL(descr.groupMap().at("test1"), size_t(1)); + + descr.setGroup("test5", 5); + + CPPUNIT_ASSERT(descr.hasGroup("test1")); + CPPUNIT_ASSERT(descr.hasGroup("test5")); + CPPUNIT_ASSERT_EQUAL(descr.groupMap().at("test1"), size_t(1)); + CPPUNIT_ASSERT_EQUAL(descr.groupMap().at("test5"), size_t(5)); + + descr.setGroup("test1", 2); + + CPPUNIT_ASSERT(descr.hasGroup("test1")); + CPPUNIT_ASSERT(descr.hasGroup("test5")); + CPPUNIT_ASSERT_EQUAL(descr.groupMap().at("test1"), size_t(2)); + CPPUNIT_ASSERT_EQUAL(descr.groupMap().at("test5"), size_t(5)); + + descr.dropGroup("test1"); + + CPPUNIT_ASSERT(!descr.hasGroup("test1")); + CPPUNIT_ASSERT(descr.hasGroup("test5")); + + descr.setGroup("test3", 3); + + CPPUNIT_ASSERT(descr.hasGroup("test3")); + CPPUNIT_ASSERT(descr.hasGroup("test5")); + + descr.clearGroups(); + + CPPUNIT_ASSERT(!descr.hasGroup("test1")); + CPPUNIT_ASSERT(!descr.hasGroup("test3")); + CPPUNIT_ASSERT(!descr.hasGroup("test5")); + } + + // I/O test + + std::ostringstream ostr(std::ios_base::binary); + descrA->write(ostr); + + Descriptor inputDescr; + + std::istringstream istr(ostr.str(), std::ios_base::binary); + inputDescr.read(istr); + + CPPUNIT_ASSERT_EQUAL(descrA->size(), inputDescr.size()); + CPPUNIT_ASSERT(*descrA == inputDescr); +} + + +void +TestAttributeSet::testAttributeSet() +{ + // Define and register some common attribute types + using AttributeS = TypedAttributeArray; + using AttributeB = TypedAttributeArray; + using AttributeI = TypedAttributeArray; + using AttributeL = TypedAttributeArray; + using AttributeVec3s = TypedAttributeArray; + + using Descriptor = AttributeSet::Descriptor; + + Descriptor::NameToPosMap groupMap; + openvdb::MetaMap metadata; + + { // construction + Descriptor::Ptr descr = Descriptor::create(AttributeVec3s::attributeType()); + descr = descr->duplicateAppend("test", AttributeI::attributeType()); + AttributeSet attrSet(descr); + CPPUNIT_ASSERT_EQUAL(attrSet.size(), size_t(2)); + + Descriptor::Ptr newDescr = Descriptor::create(AttributeVec3s::attributeType()); + CPPUNIT_ASSERT_THROW(attrSet.resetDescriptor(newDescr), openvdb::LookupError); + CPPUNIT_ASSERT_NO_THROW( + attrSet.resetDescriptor(newDescr, /*allowMismatchingDescriptors=*/true)); + } + + { // transfer of flags on construction + AttributeSet attrSet(Descriptor::create(AttributeVec3s::attributeType())); + AttributeArray::Ptr array1 = attrSet.appendAttribute( + "hidden", AttributeS::attributeType()); + array1->setHidden(true); + AttributeArray::Ptr array2 = attrSet.appendAttribute( + "transient", AttributeS::attributeType()); + array2->setTransient(true); + AttributeSet attrSet2(attrSet, size_t(1)); + CPPUNIT_ASSERT(attrSet2.getConst("hidden")->isHidden()); + CPPUNIT_ASSERT(attrSet2.getConst("transient")->isTransient()); + } + + // construct + + { // invalid append + Descriptor::Ptr descr = Descriptor::create(AttributeVec3s::attributeType()); + AttributeSet invalidAttrSetA(descr, /*arrayLength=*/50); + + CPPUNIT_ASSERT_THROW(invalidAttrSetA.appendAttribute("id", AttributeI::attributeType(), + /*stride=*/0, /*constantStride=*/true), openvdb::ValueError); + CPPUNIT_ASSERT(invalidAttrSetA.find("id") == AttributeSet::INVALID_POS); + CPPUNIT_ASSERT_THROW(invalidAttrSetA.appendAttribute("id", AttributeI::attributeType(), + /*stride=*/49, /*constantStride=*/false), openvdb::ValueError); + CPPUNIT_ASSERT_NO_THROW( + invalidAttrSetA.appendAttribute("testStride1", AttributeI::attributeType(), + /*stride=*/50, /*constantStride=*/false)); + CPPUNIT_ASSERT_NO_THROW( + invalidAttrSetA.appendAttribute("testStride2", AttributeI::attributeType(), + /*stride=*/51, /*constantStride=*/false)); + } + + { // copy construction with varying attribute types and strides + Descriptor::Ptr descr = Descriptor::create(AttributeVec3s::attributeType()); + AttributeSet attrSet(descr, /*arrayLength=*/50); + + attrSet.appendAttribute("float1", AttributeS::attributeType(), /*stride=*/1); + attrSet.appendAttribute("int1", AttributeI::attributeType(), /*stride=*/1); + attrSet.appendAttribute("float3", AttributeS::attributeType(), /*stride=*/3); + attrSet.appendAttribute("vector", AttributeVec3s::attributeType(), /*stride=*/1); + attrSet.appendAttribute("vector3", AttributeVec3s::attributeType(), /*stride=*/3); + attrSet.appendAttribute("bool100", AttributeB::attributeType(), /*stride=*/100); + attrSet.appendAttribute("boolDynamic", AttributeB::attributeType(), /*size=*/100, false); + attrSet.appendAttribute("intDynamic", AttributeI::attributeType(), /*size=*/300, false); + + CPPUNIT_ASSERT_EQUAL(std::string("float"), attrSet.getConst("float1")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("int32"), attrSet.getConst("int1")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("float"), attrSet.getConst("float3")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("vec3s"), attrSet.getConst("vector")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("vec3s"), attrSet.getConst("vector3")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("bool"), attrSet.getConst("bool100")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("bool"), attrSet.getConst("boolDynamic")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("int32"), attrSet.getConst("intDynamic")->type().first); + + CPPUNIT_ASSERT_EQUAL(openvdb::Index(1), attrSet.getConst("float1")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(1), attrSet.getConst("int1")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(3), attrSet.getConst("float3")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(1), attrSet.getConst("vector")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(3), attrSet.getConst("vector3")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(100), attrSet.getConst("bool100")->stride()); + + CPPUNIT_ASSERT_EQUAL(openvdb::Index(50), attrSet.getConst("float1")->size()); + + // error as the new length is greater than the data size of the + // 'boolDynamic' attribute + CPPUNIT_ASSERT_THROW(AttributeSet(attrSet, /*arrayLength=*/200), openvdb::ValueError); + + AttributeSet attrSet2(attrSet, /*arrayLength=*/100); + + CPPUNIT_ASSERT_EQUAL(std::string("float"), attrSet2.getConst("float1")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("int32"), attrSet2.getConst("int1")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("float"), attrSet2.getConst("float3")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("vec3s"), attrSet2.getConst("vector")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("vec3s"), attrSet2.getConst("vector3")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("bool"), attrSet2.getConst("bool100")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("bool"), attrSet2.getConst("boolDynamic")->type().first); + CPPUNIT_ASSERT_EQUAL(std::string("int32"), attrSet2.getConst("intDynamic")->type().first); + + CPPUNIT_ASSERT_EQUAL(openvdb::Index(1), attrSet2.getConst("float1")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(1), attrSet2.getConst("int1")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(3), attrSet2.getConst("float3")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(1), attrSet2.getConst("vector")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(3), attrSet2.getConst("vector3")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(100), attrSet2.getConst("bool100")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(0), attrSet2.getConst("boolDynamic")->stride()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(0), attrSet2.getConst("intDynamic")->stride()); + + CPPUNIT_ASSERT_EQUAL(openvdb::Index(100), attrSet2.getConst("float1")->size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(100), attrSet2.getConst("boolDynamic")->size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(100), attrSet2.getConst("intDynamic")->size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(100), attrSet2.getConst("boolDynamic")->dataSize()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(300), attrSet2.getConst("intDynamic")->dataSize()); + } + + Descriptor::Ptr descr = Descriptor::create(AttributeVec3s::attributeType()); + AttributeSet attrSetA(descr, /*arrayLength=*/50); + + attrSetA.appendAttribute("id", AttributeI::attributeType()); + + // check equality against duplicate array + + Descriptor::Ptr descr2 = Descriptor::create(AttributeVec3s::attributeType()); + AttributeSet attrSetA2(descr2, /*arrayLength=*/50); + + attrSetA2.appendAttribute("id", AttributeI::attributeType()); + + CPPUNIT_ASSERT(attrSetA == attrSetA2); + + // expand uniform values and check equality + + attrSetA.get("P")->expand(); + attrSetA2.get("P")->expand(); + + CPPUNIT_ASSERT(attrSetA == attrSetA2); + + CPPUNIT_ASSERT_EQUAL(size_t(2), attrSetA.size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(50), attrSetA.get(0)->size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(50), attrSetA.get(1)->size()); + + { // copy + CPPUNIT_ASSERT(!attrSetA.isShared(0)); + CPPUNIT_ASSERT(!attrSetA.isShared(1)); + + AttributeSet attrSetB(attrSetA); + + CPPUNIT_ASSERT(matchingAttributeSets(attrSetA, attrSetB)); + + CPPUNIT_ASSERT(attrSetA.isShared(0)); + CPPUNIT_ASSERT(attrSetA.isShared(1)); + CPPUNIT_ASSERT(attrSetB.isShared(0)); + CPPUNIT_ASSERT(attrSetB.isShared(1)); + + attrSetB.makeUnique(0); + attrSetB.makeUnique(1); + + CPPUNIT_ASSERT(matchingAttributeSets(attrSetA, attrSetB)); + + CPPUNIT_ASSERT(!attrSetA.isShared(0)); + CPPUNIT_ASSERT(!attrSetA.isShared(1)); + CPPUNIT_ASSERT(!attrSetB.isShared(0)); + CPPUNIT_ASSERT(!attrSetB.isShared(1)); + } + + { // attribute insertion + AttributeSet attrSetB(attrSetA); + + attrSetB.makeUnique(0); + attrSetB.makeUnique(1); + + Descriptor::Ptr targetDescr = Descriptor::create(Descriptor::Inserter() + .add("P", AttributeVec3s::attributeType()) + .add("id", AttributeI::attributeType()) + .add("test", AttributeS::attributeType()) + .vec, groupMap, metadata); + + Descriptor::Ptr descrB = + attrSetB.descriptor().duplicateAppend("test", AttributeS::attributeType()); + + // should throw if we attempt to add the same attribute name but a different type + CPPUNIT_ASSERT_THROW( + descrB->insert("test", AttributeI::attributeType()), openvdb::KeyError); + + // shouldn't throw if we attempt to add the same attribute name and type + CPPUNIT_ASSERT_NO_THROW(descrB->insert("test", AttributeS::attributeType())); + + openvdb::TypedMetadata defaultValueTest(5); + + // add a default value of the wrong type + + openvdb::TypedMetadata defaultValueInt(5); + + CPPUNIT_ASSERT_THROW(descrB->setDefaultValue("test", defaultValueInt), openvdb::TypeError); + + // add a default value with a name that does not exist + + CPPUNIT_ASSERT_THROW(descrB->setDefaultValue("badname", defaultValueTest), + openvdb::LookupError); + + // add a default value for test of 5 + + descrB->setDefaultValue("test", defaultValueTest); + + { + openvdb::Metadata::Ptr meta = descrB->getMetadata()["default:test"]; + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT(meta->typeName() == "float"); + } + + // ensure attribute order persists + + CPPUNIT_ASSERT_EQUAL(descrB->find("P"), size_t(0)); + CPPUNIT_ASSERT_EQUAL(descrB->find("id"), size_t(1)); + CPPUNIT_ASSERT_EQUAL(descrB->find("test"), size_t(2)); + + { // simple method + AttributeSet attrSetC(attrSetB); + + attrSetC.makeUnique(0); + attrSetC.makeUnique(1); + + attrSetC.appendAttribute("test", AttributeS::attributeType(), /*stride=*/1, + /*constantStride=*/true, defaultValueTest.copy()); + + CPPUNIT_ASSERT(attributeSetMatchesDescriptor(attrSetC, *descrB)); + } + { // descriptor-sharing method + AttributeSet attrSetC(attrSetB); + + attrSetC.makeUnique(0); + attrSetC.makeUnique(1); + + attrSetC.appendAttribute(attrSetC.descriptor(), descrB, size_t(2)); + + CPPUNIT_ASSERT(attributeSetMatchesDescriptor(attrSetC, *targetDescr)); + } + + // add a default value for pos of (1, 3, 1) + + openvdb::TypedMetadata defaultValuePos( + AttributeVec3s::ValueType(1, 3, 1)); + + descrB->setDefaultValue("P", defaultValuePos); + + { + openvdb::Metadata::Ptr meta = descrB->getMetadata()["default:P"]; + CPPUNIT_ASSERT(meta); + CPPUNIT_ASSERT(meta->typeName() == "vec3s"); + CPPUNIT_ASSERT_EQUAL(descrB->getDefaultValue("P"), + defaultValuePos.value()); + } + + // remove default value + + CPPUNIT_ASSERT(descrB->hasDefaultValue("test")); + + descrB->removeDefaultValue("test"); + + CPPUNIT_ASSERT(!descrB->hasDefaultValue("test")); + } + + { // attribute removal + + Descriptor::Ptr descr1 = Descriptor::create(AttributeVec3s::attributeType()); + + AttributeSet attrSetB(descr1, /*arrayLength=*/50); + + attrSetB.appendAttribute("test", AttributeI::attributeType()); + attrSetB.appendAttribute("id", AttributeL::attributeType()); + attrSetB.appendAttribute("test2", AttributeI::attributeType()); + attrSetB.appendAttribute("id2", AttributeL::attributeType()); + attrSetB.appendAttribute("test3", AttributeI::attributeType()); + + descr1 = attrSetB.descriptorPtr(); + + Descriptor::Ptr targetDescr = Descriptor::create(AttributeVec3s::attributeType()); + + targetDescr = targetDescr->duplicateAppend("id", AttributeL::attributeType()); + targetDescr = targetDescr->duplicateAppend("id2", AttributeL::attributeType()); + + // add some default values + + openvdb::TypedMetadata defaultOne(AttributeI::ValueType(1)); + + descr1->setDefaultValue("test", defaultOne); + descr1->setDefaultValue("test2", defaultOne); + + openvdb::TypedMetadata defaultThree(AttributeL::ValueType(3)); + + descr1->setDefaultValue("id", defaultThree); + + std::vector toDrop{ + descr1->find("test"), descr1->find("test2"), descr1->find("test3")}; + + CPPUNIT_ASSERT_EQUAL(toDrop[0], size_t(1)); + CPPUNIT_ASSERT_EQUAL(toDrop[1], size_t(3)); + CPPUNIT_ASSERT_EQUAL(toDrop[2], size_t(5)); + + { // simple method + AttributeSet attrSetC(attrSetB); + + attrSetC.makeUnique(0); + attrSetC.makeUnique(1); + attrSetC.makeUnique(2); + attrSetC.makeUnique(3); + + CPPUNIT_ASSERT(attrSetC.descriptor().getMetadata()["default:test"]); + + attrSetC.dropAttributes(toDrop); + + CPPUNIT_ASSERT_EQUAL(attrSetC.size(), size_t(3)); + + CPPUNIT_ASSERT(attributeSetMatchesDescriptor(attrSetC, *targetDescr)); + + // check default values have been removed for the relevant attributes + + const Descriptor& descrC = attrSetC.descriptor(); + + CPPUNIT_ASSERT(!descrC.getMetadata()["default:test"]); + CPPUNIT_ASSERT(!descrC.getMetadata()["default:test2"]); + CPPUNIT_ASSERT(!descrC.getMetadata()["default:test3"]); + + CPPUNIT_ASSERT(descrC.getMetadata()["default:id"]); + } + + { // reverse removal order + std::vector toDropReverse{ + descr1->find("test3"), descr1->find("test2"), descr1->find("test")}; + + AttributeSet attrSetC(attrSetB); + + attrSetC.makeUnique(0); + attrSetC.makeUnique(1); + attrSetC.makeUnique(2); + attrSetC.makeUnique(3); + + attrSetC.dropAttributes(toDropReverse); + + CPPUNIT_ASSERT_EQUAL(attrSetC.size(), size_t(3)); + + CPPUNIT_ASSERT(attributeSetMatchesDescriptor(attrSetC, *targetDescr)); + } + + { // descriptor-sharing method + AttributeSet attrSetC(attrSetB); + + attrSetC.makeUnique(0); + attrSetC.makeUnique(1); + attrSetC.makeUnique(2); + attrSetC.makeUnique(3); + + Descriptor::Ptr descrB = attrSetB.descriptor().duplicateDrop(toDrop); + + attrSetC.dropAttributes(toDrop, attrSetC.descriptor(), descrB); + + CPPUNIT_ASSERT_EQUAL(attrSetC.size(), size_t(3)); + + CPPUNIT_ASSERT(attributeSetMatchesDescriptor(attrSetC, *targetDescr)); + } + + { // test duplicateDrop configures group mapping + AttributeSet attrSetC; + + const size_t GROUP_BITS = sizeof(GroupType) * CHAR_BIT; + + attrSetC.appendAttribute("test1", AttributeI::attributeType()); + attrSetC.appendAttribute("__group1", GroupAttributeArray::attributeType()); + attrSetC.appendAttribute("test2", AttributeI::attributeType()); + attrSetC.appendAttribute("__group2", GroupAttributeArray::attributeType()); + attrSetC.appendAttribute("__group3", GroupAttributeArray::attributeType()); + attrSetC.appendAttribute("__group4", GroupAttributeArray::attributeType()); + + // 5 attributes exist - append a group as the sixth and then drop + + Descriptor::Ptr descriptor = attrSetC.descriptorPtr(); + size_t count = descriptor->count(GroupAttributeArray::attributeType()); + CPPUNIT_ASSERT_EQUAL(count, size_t(4)); + + descriptor->setGroup("test_group1", /*offset*/0); // __group1 + descriptor->setGroup("test_group2", /*offset=8*/GROUP_BITS); // __group2 + descriptor->setGroup("test_group3", /*offset=16*/GROUP_BITS*2); // __group3 + descriptor->setGroup("test_group4", /*offset=28*/GROUP_BITS*3 + GROUP_BITS/2); // __group4 + + descriptor = descriptor->duplicateDrop({ 1, 2, 3 }); + count = descriptor->count(GroupAttributeArray::attributeType()); + CPPUNIT_ASSERT_EQUAL(count, size_t(2)); + + CPPUNIT_ASSERT_EQUAL(size_t(3), descriptor->size()); + CPPUNIT_ASSERT(!descriptor->hasGroup("test_group1")); + CPPUNIT_ASSERT(!descriptor->hasGroup("test_group2")); + CPPUNIT_ASSERT(descriptor->hasGroup("test_group3")); + CPPUNIT_ASSERT(descriptor->hasGroup("test_group4")); + + CPPUNIT_ASSERT_EQUAL(descriptor->find("__group1"), size_t(AttributeSet::INVALID_POS)); + CPPUNIT_ASSERT_EQUAL(descriptor->find("__group2"), size_t(AttributeSet::INVALID_POS)); + CPPUNIT_ASSERT_EQUAL(descriptor->find("__group3"), size_t(1)); + CPPUNIT_ASSERT_EQUAL(descriptor->find("__group4"), size_t(2)); + + CPPUNIT_ASSERT_EQUAL(descriptor->groupOffset("test_group3"), size_t(0)); + CPPUNIT_ASSERT_EQUAL(descriptor->groupOffset("test_group4"), size_t(GROUP_BITS + GROUP_BITS/2)); + } + } + + // replace existing arrays + + // this replace call should not take effect since the new attribute + // array type does not match with the descriptor type for the given position. + AttributeArray::Ptr floatAttr(new AttributeS(15)); + CPPUNIT_ASSERT(attrSetA.replace(1, floatAttr) == AttributeSet::INVALID_POS); + + AttributeArray::Ptr intAttr(new AttributeI(10)); + CPPUNIT_ASSERT(attrSetA.replace(1, intAttr) != AttributeSet::INVALID_POS); + + CPPUNIT_ASSERT_EQUAL(openvdb::Index(10), attrSetA.get(1)->size()); + + { // reorder attribute set + Descriptor::Ptr descr1 = Descriptor::create(AttributeVec3s::attributeType()); + + AttributeSet attrSetA1(descr1); + + attrSetA1.appendAttribute("test", AttributeI::attributeType()); + attrSetA1.appendAttribute("id", AttributeI::attributeType()); + attrSetA1.appendAttribute("test2", AttributeI::attributeType()); + + descr1 = attrSetA1.descriptorPtr(); + + Descriptor::Ptr descr2x = Descriptor::create(AttributeVec3s::attributeType()); + + AttributeSet attrSetB1(descr2x); + + attrSetB1.appendAttribute("test2", AttributeI::attributeType()); + attrSetB1.appendAttribute("test", AttributeI::attributeType()); + attrSetB1.appendAttribute("id", AttributeI::attributeType()); + + CPPUNIT_ASSERT(attrSetA1 != attrSetB1); + + attrSetB1.reorderAttributes(descr1); + + CPPUNIT_ASSERT(attrSetA1 == attrSetB1); + } + + { // metadata test + Descriptor::Ptr descr1A = Descriptor::create(AttributeVec3s::attributeType()); + + Descriptor::Ptr descr2A = Descriptor::create(AttributeVec3s::attributeType()); + + openvdb::MetaMap& meta = descr1A->getMetadata(); + meta.insertMeta("exampleMeta", openvdb::FloatMetadata(2.0)); + + AttributeSet attrSetA1(descr1A); + AttributeSet attrSetB1(descr2A); + AttributeSet attrSetC1(attrSetA1); + + CPPUNIT_ASSERT(attrSetA1 != attrSetB1); + CPPUNIT_ASSERT(attrSetA1 == attrSetC1); + } + + // add some metadata and register the type + + openvdb::MetaMap& meta = attrSetA.descriptor().getMetadata(); + meta.insertMeta("exampleMeta", openvdb::FloatMetadata(2.0)); + + { // I/O test + std::ostringstream ostr(std::ios_base::binary); + attrSetA.write(ostr); + + AttributeSet attrSetB; + std::istringstream istr(ostr.str(), std::ios_base::binary); + attrSetB.read(istr); + + CPPUNIT_ASSERT(matchingAttributeSets(attrSetA, attrSetB)); + } + + { // I/O transient test + AttributeArray* array = attrSetA.get(0); + array->setTransient(true); + + std::ostringstream ostr(std::ios_base::binary); + attrSetA.write(ostr); + + AttributeSet attrSetB; + std::istringstream istr(ostr.str(), std::ios_base::binary); + attrSetB.read(istr); + + // ensures transient attribute is not written out + + CPPUNIT_ASSERT_EQUAL(attrSetB.size(), size_t(1)); + + std::ostringstream ostr2(std::ios_base::binary); + attrSetA.write(ostr2, /*transient=*/true); + + AttributeSet attrSetC; + std::istringstream istr2(ostr2.str(), std::ios_base::binary); + attrSetC.read(istr2); + + CPPUNIT_ASSERT_EQUAL(attrSetC.size(), size_t(2)); + } +} + + +void +TestAttributeSet::testAttributeSetGroups() +{ + // Define and register some common attribute types + using AttributeI = TypedAttributeArray; + using AttributeVec3s = TypedAttributeArray; + + using Descriptor = AttributeSet::Descriptor; + + Descriptor::NameToPosMap groupMap; + openvdb::MetaMap metadata; + + { // construct + Descriptor::Ptr descr = Descriptor::create(AttributeVec3s::attributeType()); + AttributeSet attrSet(descr, /*arrayLength=*/3); + attrSet.appendAttribute("id", AttributeI::attributeType()); + CPPUNIT_ASSERT(!descr->hasGroup("test1")); + } + + { // group offset + Descriptor::Ptr descr = Descriptor::create(AttributeVec3s::attributeType()); + + descr->setGroup("test1", 1); + + CPPUNIT_ASSERT(descr->hasGroup("test1")); + CPPUNIT_ASSERT_EQUAL(descr->groupMap().at("test1"), size_t(1)); + + AttributeSet attrSet(descr); + + CPPUNIT_ASSERT_EQUAL(attrSet.groupOffset("test1"), size_t(1)); + } + + { // group index + Descriptor::Ptr descr = Descriptor::create(AttributeVec3s::attributeType()); + + AttributeSet attrSet(descr); + + attrSet.appendAttribute("test", AttributeI::attributeType()); + attrSet.appendAttribute("test2", AttributeI::attributeType()); + attrSet.appendAttribute("group1", GroupAttributeArray::attributeType()); + attrSet.appendAttribute("test3", AttributeI::attributeType()); + attrSet.appendAttribute("group2", GroupAttributeArray::attributeType()); + attrSet.appendAttribute("test4", AttributeI::attributeType()); + attrSet.appendAttribute("group3", GroupAttributeArray::attributeType()); + + descr = attrSet.descriptorPtr(); + + std::stringstream ss; + for (int i = 0; i < 17; i++) { + ss.str(""); + ss << "test" << i; + descr->setGroup(ss.str(), i); + } + + Descriptor::GroupIndex index15 = attrSet.groupIndex(15); + CPPUNIT_ASSERT_EQUAL(index15.first, size_t(5)); + CPPUNIT_ASSERT_EQUAL(index15.second, uint8_t(7)); + + CPPUNIT_ASSERT_EQUAL(attrSet.groupOffset(index15), size_t(15)); + CPPUNIT_ASSERT_EQUAL(attrSet.groupOffset("test15"), size_t(15)); + + Descriptor::GroupIndex index15b = attrSet.groupIndex("test15"); + CPPUNIT_ASSERT_EQUAL(index15b.first, size_t(5)); + CPPUNIT_ASSERT_EQUAL(index15b.second, uint8_t(7)); + + Descriptor::GroupIndex index16 = attrSet.groupIndex(16); + CPPUNIT_ASSERT_EQUAL(index16.first, size_t(7)); + CPPUNIT_ASSERT_EQUAL(index16.second, uint8_t(0)); + + CPPUNIT_ASSERT_EQUAL(attrSet.groupOffset(index16), size_t(16)); + CPPUNIT_ASSERT_EQUAL(attrSet.groupOffset("test16"), size_t(16)); + + Descriptor::GroupIndex index16b = attrSet.groupIndex("test16"); + CPPUNIT_ASSERT_EQUAL(index16b.first, size_t(7)); + CPPUNIT_ASSERT_EQUAL(index16b.second, uint8_t(0)); + + // check out of range exception + + CPPUNIT_ASSERT_NO_THROW(attrSet.groupIndex(23)); + CPPUNIT_ASSERT_THROW(attrSet.groupIndex(24), LookupError); + } + + { // group unique name + Descriptor::Ptr descr = Descriptor::create(AttributeVec3s::attributeType()); + const openvdb::Name uniqueNameEmpty = descr->uniqueGroupName("test"); + CPPUNIT_ASSERT_EQUAL(uniqueNameEmpty, openvdb::Name("test")); + + descr->setGroup("test", 1); + descr->setGroup("test1", 2); + + const openvdb::Name uniqueName1 = descr->uniqueGroupName("test"); + CPPUNIT_ASSERT_EQUAL(uniqueName1, openvdb::Name("test0")); + descr->setGroup(uniqueName1, 3); + + const openvdb::Name uniqueName2 = descr->uniqueGroupName("test"); + CPPUNIT_ASSERT_EQUAL(uniqueName2, openvdb::Name("test2")); + } + + { // group rename + Descriptor::Ptr descr = Descriptor::create(AttributeVec3s::attributeType()); + descr->setGroup("test", 1); + descr->setGroup("test1", 2); + + size_t pos = descr->renameGroup("test", "test1"); + CPPUNIT_ASSERT(pos == AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(descr->hasGroup("test")); + CPPUNIT_ASSERT(descr->hasGroup("test1")); + + pos = descr->renameGroup("test", "test2"); + CPPUNIT_ASSERT_EQUAL(pos, size_t(1)); + CPPUNIT_ASSERT(!descr->hasGroup("test")); + CPPUNIT_ASSERT(descr->hasGroup("test1")); + CPPUNIT_ASSERT(descr->hasGroup("test2")); + } +} diff --git a/openvdb/unittest/TestBBox.cc b/openvdb/unittest/TestBBox.cc new file mode 100644 index 00000000..8d36c499 --- /dev/null +++ b/openvdb/unittest/TestBBox.cc @@ -0,0 +1,95 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include + +typedef float Real; + +class TestBBox: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestBBox); + CPPUNIT_TEST(testBBox); + CPPUNIT_TEST(testCenter); + CPPUNIT_TEST(testExtent); + CPPUNIT_TEST_SUITE_END(); + + void testBBox(); + void testCenter(); + void testExtent(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestBBox); + + +void +TestBBox::testBBox() +{ + typedef openvdb::Vec3R Vec3R; + typedef openvdb::math::BBox BBoxType; + + { + BBoxType B(Vec3R(1,1,1),Vec3R(2,2,2)); + + CPPUNIT_ASSERT(B.isSorted()); + CPPUNIT_ASSERT(B.isInside(Vec3R(1.5,2,2))); + CPPUNIT_ASSERT(!B.isInside(Vec3R(2,3,2))); + B.expand(Vec3R(3,3,3)); + CPPUNIT_ASSERT(B.isInside(Vec3R(3,3,3))); + } + + { + BBoxType B; + CPPUNIT_ASSERT(B.empty()); + const Vec3R expected(1); + B.expand(expected); + CPPUNIT_ASSERT_EQUAL(expected, B.min()); + CPPUNIT_ASSERT_EQUAL(expected, B.max()); + } +} + + +void +TestBBox::testCenter() +{ + using namespace openvdb::math; + + const Vec3 expected(1.5); + + BBox fbox(openvdb::Vec3R(1.0), openvdb::Vec3R(2.0)); + CPPUNIT_ASSERT_EQUAL(expected, fbox.getCenter()); + + BBox ibox(openvdb::Vec3i(1), openvdb::Vec3i(2)); + CPPUNIT_ASSERT_EQUAL(expected, ibox.getCenter()); + + openvdb::CoordBBox cbox(openvdb::Coord(1), openvdb::Coord(2)); + CPPUNIT_ASSERT_EQUAL(expected, cbox.getCenter()); +} + +void +TestBBox::testExtent() +{ + typedef openvdb::Vec3R Vec3R; + typedef openvdb::math::BBox BBoxType; + + { + BBoxType B(Vec3R(-20,0,1),Vec3R(2,2,2)); + CPPUNIT_ASSERT_EQUAL(size_t(2), B.minExtent()); + CPPUNIT_ASSERT_EQUAL(size_t(0), B.maxExtent()); + } + { + BBoxType B(Vec3R(1,0,1),Vec3R(2,21,20)); + CPPUNIT_ASSERT_EQUAL(size_t(0), B.minExtent()); + CPPUNIT_ASSERT_EQUAL(size_t(1), B.maxExtent()); + } + { + BBoxType B(Vec3R(1,0,1),Vec3R(3,1.5,20)); + CPPUNIT_ASSERT_EQUAL(size_t(1), B.minExtent()); + CPPUNIT_ASSERT_EQUAL(size_t(2), B.maxExtent()); + } +} diff --git a/openvdb/unittest/TestClip.cc b/openvdb/unittest/TestClip.cc new file mode 100644 index 00000000..13b739bf --- /dev/null +++ b/openvdb/unittest/TestClip.cc @@ -0,0 +1,248 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include // for math::NonlinearFrustumMap +#include + + +// See also TestGrid::testClipping() +class TestClip: public CppUnit::TestFixture +{ +public: + static const openvdb::CoordBBox kCubeBBox, kInnerBBox; + + TestClip(): mCube{ + []() { + auto cube = openvdb::FloatGrid{0.0f}; + cube.fill(kCubeBBox, /*value=*/5.0f, /*active=*/true); + return cube; + }()} + {} + + void setUp() override; + void tearDown() override; + + CPPUNIT_TEST_SUITE(TestClip); + CPPUNIT_TEST(testBBox); + CPPUNIT_TEST(testFrustum); + CPPUNIT_TEST(testMaskGrid); + CPPUNIT_TEST(testBoolMask); + CPPUNIT_TEST(testInvertedBoolMask); + CPPUNIT_TEST(testNonBoolMask); + CPPUNIT_TEST(testInvertedNonBoolMask); + CPPUNIT_TEST_SUITE_END(); + + void testBBox(); + void testFrustum(); + void testMaskGrid(); + void testBoolMask(); + void testInvertedBoolMask(); + void testNonBoolMask(); + void testInvertedNonBoolMask(); + +private: + void validate(const openvdb::FloatGrid&); + + const openvdb::FloatGrid mCube; +}; + +const openvdb::CoordBBox + // The volume to be clipped is a 21 x 21 x 21 solid cube. + TestClip::kCubeBBox{openvdb::Coord{-10}, openvdb::Coord{10}}, + // The clipping mask is a 1 x 1 x 13 segment extending along the Z axis inside the cube. + TestClip::kInnerBBox{openvdb::Coord{4, 4, -6}, openvdb::Coord{4, 4, 6}}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestClip); + + +//////////////////////////////////////// + + +void +TestClip::setUp() +{ + openvdb::initialize(); +} + +void +TestClip::tearDown() +{ + openvdb::uninitialize(); +} + + +void +TestClip::validate(const openvdb::FloatGrid& clipped) +{ + using namespace openvdb; + + const CoordBBox bbox = clipped.evalActiveVoxelBoundingBox(); + CPPUNIT_ASSERT_EQUAL(kInnerBBox.min().x(), bbox.min().x()); + CPPUNIT_ASSERT_EQUAL(kInnerBBox.min().y(), bbox.min().y()); + CPPUNIT_ASSERT_EQUAL(kInnerBBox.min().z(), bbox.min().z()); + CPPUNIT_ASSERT_EQUAL(kInnerBBox.max().x(), bbox.max().x()); + CPPUNIT_ASSERT_EQUAL(kInnerBBox.max().y(), bbox.max().y()); + CPPUNIT_ASSERT_EQUAL(kInnerBBox.max().z(), bbox.max().z()); + CPPUNIT_ASSERT_EQUAL(6 + 6 + 1, int(clipped.activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(2, int(clipped.constTree().leafCount())); + + FloatGrid::ConstAccessor acc = clipped.getConstAccessor(); + const float bg = clipped.background(); + Coord xyz; + int &x = xyz[0], &y = xyz[1], &z = xyz[2]; + for (x = kCubeBBox.min().x(); x <= kCubeBBox.max().x(); ++x) { + for (y = kCubeBBox.min().y(); y <= kCubeBBox.max().y(); ++y) { + for (z = kCubeBBox.min().z(); z <= kCubeBBox.max().z(); ++z) { + if (x == 4 && y == 4 && z >= -6 && z <= 6) { + CPPUNIT_ASSERT_EQUAL(5.f, acc.getValue(Coord(4, 4, z))); + } else { + CPPUNIT_ASSERT_EQUAL(bg, acc.getValue(Coord(x, y, z))); + } + } + } + } +} + + +//////////////////////////////////////// + + +// Test clipping against a bounding box. +void +TestClip::testBBox() +{ + using namespace openvdb; + BBoxd clipBox(Vec3d(4.0, 4.0, -6.0), Vec3d(4.9, 4.9, 6.0)); + FloatGrid::Ptr clipped = tools::clip(mCube, clipBox); + validate(*clipped); +} + + +// Test clipping against a camera frustum. +void +TestClip::testFrustum() +{ + using namespace openvdb; + + const auto d = double(kCubeBBox.max().z()); + const math::NonlinearFrustumMap frustum{ + /*position=*/Vec3d{0.0, 0.0, 5.0 * d}, + /*direction=*/Vec3d{0.0, 0.0, -1.0}, + /*up=*/Vec3d{0.0, d / 2.0, 0.0}, + /*aspect=*/1.0, + /*near=*/4.0 * d + 1.0, + /*depth=*/kCubeBBox.dim().z() - 2.0, + /*x_count=*/100, + /*z_count=*/100}; + const auto frustumIndexBBox = frustum.getBBox(); + + { + auto clipped = tools::clip(mCube, frustum); + + const auto bbox = clipped->evalActiveVoxelBoundingBox(); + const auto cubeDim = kCubeBBox.dim(); + CPPUNIT_ASSERT_EQUAL(kCubeBBox.min().z() + 1, bbox.min().z()); + CPPUNIT_ASSERT_EQUAL(kCubeBBox.max().z() - 1, bbox.max().z()); + CPPUNIT_ASSERT(int(bbox.volume()) < int(cubeDim.x() * cubeDim.y() * (cubeDim.z() - 2))); + + // Note: mCube index space corresponds to world space. + for (auto it = clipped->beginValueOn(); it; ++it) { + const auto xyz = frustum.applyInverseMap(it.getCoord().asVec3d()); + CPPUNIT_ASSERT(frustumIndexBBox.isInside(xyz)); + } + } + { + auto tile = openvdb::FloatGrid{0.0f}; + tile.tree().addTile(/*level=*/2, Coord{0}, /*value=*/5.0f, /*active=*/true); + + auto clipped = tools::clip(tile, frustum); + CPPUNIT_ASSERT(!clipped->empty()); + for (auto it = clipped->beginValueOn(); it; ++it) { + const auto xyz = frustum.applyInverseMap(it.getCoord().asVec3d()); + CPPUNIT_ASSERT(frustumIndexBBox.isInside(xyz)); + } + + clipped = tools::clip(tile, frustum, /*keepInterior=*/false); + CPPUNIT_ASSERT(!clipped->empty()); + for (auto it = clipped->beginValueOn(); it; ++it) { + const auto xyz = frustum.applyInverseMap(it.getCoord().asVec3d()); + CPPUNIT_ASSERT(!frustumIndexBBox.isInside(xyz)); + } + } +} + + +// Test clipping against a MaskGrid. +void +TestClip::testMaskGrid() +{ + using namespace openvdb; + MaskGrid mask(false); + mask.fill(kInnerBBox, true, true); + FloatGrid::Ptr clipped = tools::clip(mCube, mask); + validate(*clipped); +} + + +// Test clipping against a boolean mask grid. +void +TestClip::testBoolMask() +{ + using namespace openvdb; + BoolGrid mask(false); + mask.fill(kInnerBBox, true, true); + FloatGrid::Ptr clipped = tools::clip(mCube, mask); + validate(*clipped); +} + + +// Test clipping against a boolean mask grid with mask inversion. +void +TestClip::testInvertedBoolMask() +{ + using namespace openvdb; + // Construct a mask grid that is the "inverse" of the mask used in the other tests. + // (This is not a true inverse, since the mask's active voxel bounds are finite.) + BoolGrid mask(false); + mask.fill(kCubeBBox, true, true); + mask.fill(kInnerBBox, false, false); + // Clipping against the "inverted" mask with mask inversion enabled + // should give the same results as clipping normally against the normal mask. + FloatGrid::Ptr clipped = tools::clip(mCube, mask, /*keepInterior=*/false); + clipped->pruneGrid(); + validate(*clipped); +} + + +// Test clipping against a non-boolean mask grid. +void +TestClip::testNonBoolMask() +{ + using namespace openvdb; + Int32Grid mask(0); + mask.fill(kInnerBBox, -5, true); + FloatGrid::Ptr clipped = tools::clip(mCube, mask); + validate(*clipped); +} + + +// Test clipping against a non-boolean mask grid with mask inversion. +void +TestClip::testInvertedNonBoolMask() +{ + using namespace openvdb; + // Construct a mask grid that is the "inverse" of the mask used in the other tests. + // (This is not a true inverse, since the mask's active voxel bounds are finite.) + Grid mask(0); + auto paddedCubeBBox = kCubeBBox; + paddedCubeBBox.expand(2); + mask.fill(paddedCubeBBox, 99, true); + mask.fill(kInnerBBox, 0, false); + // Clipping against the "inverted" mask with mask inversion enabled + // should give the same results as clipping normally against the normal mask. + FloatGrid::Ptr clipped = tools::clip(mCube, mask, /*keepInterior=*/false); + clipped->pruneGrid(); + validate(*clipped); +} diff --git a/openvdb/unittest/TestConjGradient.cc b/openvdb/unittest/TestConjGradient.cc new file mode 100644 index 00000000..edff82a4 --- /dev/null +++ b/openvdb/unittest/TestConjGradient.cc @@ -0,0 +1,211 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + + +class TestConjGradient: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestConjGradient); + CPPUNIT_TEST(testJacobi); + CPPUNIT_TEST(testIncompleteCholesky); + CPPUNIT_TEST(testVectorDotProduct); + CPPUNIT_TEST_SUITE_END(); + + void testJacobi(); + void testIncompleteCholesky(); + void testVectorDotProduct(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestConjGradient); + + +//////////////////////////////////////// + + +void +TestConjGradient::testJacobi() +{ + using namespace openvdb; + + typedef math::pcg::SparseStencilMatrix MatrixType; + + const math::pcg::SizeType rows = 5; + + MatrixType A(rows); + A.setValue(0, 0, 24.0); + A.setValue(0, 2, 6.0); + A.setValue(1, 1, 8.0); + A.setValue(1, 2, 2.0); + A.setValue(2, 0, 6.0); + A.setValue(2, 1, 2.0); + A.setValue(2, 2, 8.0); + A.setValue(2, 3, -6.0); + A.setValue(2, 4, 2.0); + A.setValue(3, 2, -6.0); + A.setValue(3, 3, 24.0); + A.setValue(4, 2, 2.0); + A.setValue(4, 4, 8.0); + + CPPUNIT_ASSERT(A.isFinite()); + + MatrixType::VectorType + x(rows, 0.0), + b(rows, 1.0), + expected(rows); + + expected[0] = 0.0104167; + expected[1] = 0.09375; + expected[2] = 0.125; + expected[3] = 0.0729167; + expected[4] = 0.09375; + + math::pcg::JacobiPreconditioner precond(A); + + // Solve A * x = b for x. + math::pcg::State result = math::pcg::solve( + A, b, x, precond, math::pcg::terminationDefaults()); + + CPPUNIT_ASSERT(result.success); + CPPUNIT_ASSERT(result.iterations <= 20); + CPPUNIT_ASSERT(x.eq(expected, 1.0e-5)); +} + + +void +TestConjGradient::testIncompleteCholesky() +{ + using namespace openvdb; + + typedef math::pcg::SparseStencilMatrix MatrixType; + typedef math::pcg::IncompleteCholeskyPreconditioner CholeskyPrecond; + + const math::pcg::SizeType rows = 5; + + MatrixType A(5); + A.setValue(0, 0, 24.0); + A.setValue(0, 2, 6.0); + A.setValue(1, 1, 8.0); + A.setValue(1, 2, 2.0); + A.setValue(2, 0, 6.0); + A.setValue(2, 1, 2.0); + A.setValue(2, 2, 8.0); + A.setValue(2, 3, -6.0); + A.setValue(2, 4, 2.0); + A.setValue(3, 2, -6.0); + A.setValue(3, 3, 24.0); + A.setValue(4, 2, 2.0); + A.setValue(4, 4, 8.0); + + CPPUNIT_ASSERT(A.isFinite()); + + CholeskyPrecond precond(A); + { + const CholeskyPrecond::TriangularMatrix lower = precond.lowerMatrix(); + + CholeskyPrecond::TriangularMatrix expected(5); + expected.setValue(0, 0, 4.89898); + expected.setValue(1, 1, 2.82843); + expected.setValue(2, 0, 1.22474); + expected.setValue(2, 1, 0.707107); + expected.setValue(2, 2, 2.44949); + expected.setValue(3, 2, -2.44949); + expected.setValue(3, 3, 4.24264); + expected.setValue(4, 2, 0.816497); + expected.setValue(4, 4, 2.70801); + +#if 0 + std::cout << "Expected:\n"; + for (int i = 0; i < 5; ++i) { + std::cout << " " << expected.getConstRow(i).str() << std::endl; + } + std::cout << "Actual:\n"; + for (int i = 0; i < 5; ++i) { + std::cout << " " << lower.getConstRow(i).str() << std::endl; + } +#endif + + CPPUNIT_ASSERT(lower.eq(expected, 1.0e-5)); + } + { + const CholeskyPrecond::TriangularMatrix upper = precond.upperMatrix(); + + CholeskyPrecond::TriangularMatrix expected(5); + { + expected.setValue(0, 0, 4.89898); + expected.setValue(0, 2, 1.22474); + expected.setValue(1, 1, 2.82843); + expected.setValue(1, 2, 0.707107); + expected.setValue(2, 2, 2.44949); + expected.setValue(2, 3, -2.44949); + expected.setValue(2, 4, 0.816497); + expected.setValue(3, 3, 4.24264); + expected.setValue(4, 4, 2.70801); + } + +#if 0 + std::cout << "Expected:\n"; + for (int i = 0; i < 5; ++i) { + std::cout << " " << expected.getConstRow(i).str() << std::endl; + } + std::cout << "Actual:\n"; + for (int i = 0; i < 5; ++i) { + std::cout << " " << upper.getConstRow(i).str() << std::endl; + } +#endif + + CPPUNIT_ASSERT(upper.eq(expected, 1.0e-5)); + } + + MatrixType::VectorType + x(rows, 0.0), + b(rows, 1.0), + expected(rows); + + expected[0] = 0.0104167; + expected[1] = 0.09375; + expected[2] = 0.125; + expected[3] = 0.0729167; + expected[4] = 0.09375; + + // Solve A * x = b for x. + math::pcg::State result = math::pcg::solve( + A, b, x, precond, math::pcg::terminationDefaults()); + + CPPUNIT_ASSERT(result.success); + CPPUNIT_ASSERT(result.iterations <= 20); + CPPUNIT_ASSERT(x.eq(expected, 1.0e-5)); +} + +void +TestConjGradient::testVectorDotProduct() +{ + using namespace openvdb; + + typedef math::pcg::Vector VectorType; + + // Test small vector - runs in series + { + const size_t length = 1000; + VectorType aVec(length, 2.0); + VectorType bVec(length, 3.0); + + VectorType::ValueType result = aVec.dot(bVec); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(result, 6.0 * length, 1.0e-7); + } + // Test long vector - runs in parallel + { + const size_t length = 10034502; + VectorType aVec(length, 2.0); + VectorType bVec(length, 3.0); + + VectorType::ValueType result = aVec.dot(bVec); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(result, 6.0 * length, 1.0e-7); + } +} diff --git a/openvdb/unittest/TestCoord.cc b/openvdb/unittest/TestCoord.cc new file mode 100644 index 00000000..78bb1740 --- /dev/null +++ b/openvdb/unittest/TestCoord.cc @@ -0,0 +1,396 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include // for tbb::split + + +class TestCoord: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestCoord); + CPPUNIT_TEST(testCoord); + CPPUNIT_TEST(testConversion); + CPPUNIT_TEST(testIO); + CPPUNIT_TEST(testCoordBBox); + CPPUNIT_TEST(testCoordHash); + CPPUNIT_TEST_SUITE_END(); + + void testCoord(); + void testConversion(); + void testIO(); + void testCoordBBox(); + void testCoordHash(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestCoord); + + +void +TestCoord::testCoord() +{ + using openvdb::Coord; + + for (int i=0; i<3; ++i) { + CPPUNIT_ASSERT_EQUAL(Coord::min()[i], std::numeric_limits::min()); + CPPUNIT_ASSERT_EQUAL(Coord::max()[i], std::numeric_limits::max()); + } + + Coord xyz(-1, 2, 4); + Coord xyz2 = -xyz; + CPPUNIT_ASSERT_EQUAL(Coord(1, -2, -4), xyz2); + + CPPUNIT_ASSERT_EQUAL(Coord(1, 2, 4), openvdb::math::Abs(xyz)); + + xyz2 = -xyz2; + CPPUNIT_ASSERT_EQUAL(xyz, xyz2); + + xyz.setX(-xyz.x()); + CPPUNIT_ASSERT_EQUAL(Coord(1, 2, 4), xyz); + + xyz2 = xyz >> 1; + CPPUNIT_ASSERT_EQUAL(Coord(0, 1, 2), xyz2); + + xyz2 |= 1; + CPPUNIT_ASSERT_EQUAL(Coord(1, 1, 3), xyz2); + + CPPUNIT_ASSERT(xyz2 != xyz); + CPPUNIT_ASSERT(xyz2 < xyz); + CPPUNIT_ASSERT(xyz2 <= xyz); + + Coord xyz3(xyz2); + xyz2 -= xyz3; + CPPUNIT_ASSERT_EQUAL(Coord(), xyz2); + + xyz2.reset(0, 4, 4); + xyz2.offset(-1); + CPPUNIT_ASSERT_EQUAL(Coord(-1, 3, 3), xyz2); + + // xyz = (1, 2, 4), xyz2 = (-1, 3, 3) + CPPUNIT_ASSERT_EQUAL(Coord(-1, 2, 3), Coord::minComponent(xyz, xyz2)); + CPPUNIT_ASSERT_EQUAL(Coord(1, 3, 4), Coord::maxComponent(xyz, xyz2)); +} + + +void +TestCoord::testConversion() +{ + using openvdb::Coord; + + openvdb::Vec3I iv(1, 2, 4); + Coord xyz(iv); + CPPUNIT_ASSERT_EQUAL(Coord(1, 2, 4), xyz); + CPPUNIT_ASSERT_EQUAL(iv, xyz.asVec3I()); + CPPUNIT_ASSERT_EQUAL(openvdb::Vec3i(1, 2, 4), xyz.asVec3i()); + + iv = (xyz + iv) + xyz; + CPPUNIT_ASSERT_EQUAL(openvdb::Vec3I(3, 6, 12), iv); + iv = iv - xyz; + CPPUNIT_ASSERT_EQUAL(openvdb::Vec3I(2, 4, 8), iv); + + openvdb::Vec3s fv = xyz.asVec3s(); + CPPUNIT_ASSERT(openvdb::math::isExactlyEqual(openvdb::Vec3s(1, 2, 4), fv)); +} + + +void +TestCoord::testIO() +{ + using openvdb::Coord; + + Coord xyz(-1, 2, 4), xyz2; + + std::ostringstream os(std::ios_base::binary); + CPPUNIT_ASSERT_NO_THROW(xyz.write(os)); + + std::istringstream is(os.str(), std::ios_base::binary); + CPPUNIT_ASSERT_NO_THROW(xyz2.read(is)); + + CPPUNIT_ASSERT_EQUAL(xyz, xyz2); + + os.str(""); + os << xyz; + CPPUNIT_ASSERT_EQUAL(std::string("[-1, 2, 4]"), os.str()); +} + +void +TestCoord::testCoordBBox() +{ + {// Empty constructor + openvdb::CoordBBox b; + CPPUNIT_ASSERT_EQUAL(openvdb::Coord::max(), b.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord::min(), b.max()); + CPPUNIT_ASSERT(b.empty()); + } + {// Construct bbox from min and max + const openvdb::Coord min(-1,-2,30), max(20,30,55); + openvdb::CoordBBox b(min, max); + CPPUNIT_ASSERT_EQUAL(min, b.min()); + CPPUNIT_ASSERT_EQUAL(max, b.max()); + } + {// Construct bbox from components of min and max + const openvdb::Coord min(-1,-2,30), max(20,30,55); + openvdb::CoordBBox b(min[0], min[1], min[2], + max[0], max[1], max[2]); + CPPUNIT_ASSERT_EQUAL(min, b.min()); + CPPUNIT_ASSERT_EQUAL(max, b.max()); + } + {// tbb::split constructor + const openvdb::Coord min(-1,-2,30), max(20,30,55); + openvdb::CoordBBox a(min, max), b(a, tbb::split()); + CPPUNIT_ASSERT_EQUAL(min, b.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(20, 14, 55), b.max()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(-1, 15, 30), a.min()); + CPPUNIT_ASSERT_EQUAL(max, a.max()); + } + {// createCube + const openvdb::Coord min(0,8,16); + const openvdb::CoordBBox b = openvdb::CoordBBox::createCube(min, 8); + CPPUNIT_ASSERT_EQUAL(min, b.min()); + CPPUNIT_ASSERT_EQUAL(min + openvdb::Coord(8-1), b.max()); + } + {// inf + const openvdb::CoordBBox b = openvdb::CoordBBox::inf(); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord::min(), b.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord::max(), b.max()); + } + {// empty, dim, hasVolume and volume + const openvdb::Coord c(1,2,3); + const openvdb::CoordBBox b0(c, c), b1(c, c.offsetBy(0,-1,0)), b2; + CPPUNIT_ASSERT( b0.hasVolume() && !b0.empty()); + CPPUNIT_ASSERT(!b1.hasVolume() && b1.empty()); + CPPUNIT_ASSERT(!b2.hasVolume() && b2.empty()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(1), b0.dim()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), b1.dim()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), b2.dim()); + CPPUNIT_ASSERT_EQUAL(uint64_t(1), b0.volume()); + CPPUNIT_ASSERT_EQUAL(uint64_t(0), b1.volume()); + CPPUNIT_ASSERT_EQUAL(uint64_t(0), b2.volume()); + } + {// volume and split constructor + const openvdb::Coord min(-1,-2,30), max(20,30,55); + const openvdb::CoordBBox bbox(min,max); + openvdb::CoordBBox a(bbox), b(a, tbb::split()); + CPPUNIT_ASSERT_EQUAL(bbox.volume(), a.volume() + b.volume()); + openvdb::CoordBBox c(b, tbb::split()); + CPPUNIT_ASSERT_EQUAL(bbox.volume(), a.volume() + b.volume() + c.volume()); + } + {// getCenter + const openvdb::Coord min(1,2,3), max(6,10,15); + const openvdb::CoordBBox b(min, max); + CPPUNIT_ASSERT_EQUAL(openvdb::Vec3d(3.5, 6.0, 9.0), b.getCenter()); + } + {// moveMin + const openvdb::Coord min(1,2,3), max(6,10,15); + openvdb::CoordBBox b(min, max); + const openvdb::Coord dim = b.dim(); + b.moveMin(openvdb::Coord(0)); + CPPUNIT_ASSERT_EQUAL(dim, b.dim()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), b.min()); + CPPUNIT_ASSERT_EQUAL(max-min, b.max()); + } + {// moveMax + const openvdb::Coord min(1,2,3), max(6,10,15); + openvdb::CoordBBox b(min, max); + const openvdb::Coord dim = b.dim(); + b.moveMax(openvdb::Coord(0)); + CPPUNIT_ASSERT_EQUAL(dim, b.dim()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), b.max()); + CPPUNIT_ASSERT_EQUAL(min-max, b.min()); + } + {// a volume that overflows Int32. + using Int32 = openvdb::Int32; + Int32 maxInt32 = std::numeric_limits::max(); + const openvdb::Coord min(Int32(0), Int32(0), Int32(0)); + const openvdb::Coord max(maxInt32-Int32(2), Int32(2), Int32(2)); + + const openvdb::CoordBBox b(min, max); + uint64_t volume = UINT64_C(19327352814); + CPPUNIT_ASSERT_EQUAL(volume, b.volume()); + } + {// minExtent and maxExtent + const openvdb::Coord min(1,2,3); + { + const openvdb::Coord max = min + openvdb::Coord(1,2,3); + const openvdb::CoordBBox b(min, max); + CPPUNIT_ASSERT_EQUAL(int(b.minExtent()), 0); + CPPUNIT_ASSERT_EQUAL(int(b.maxExtent()), 2); + } + { + const openvdb::Coord max = min + openvdb::Coord(1,3,2); + const openvdb::CoordBBox b(min, max); + CPPUNIT_ASSERT_EQUAL(int(b.minExtent()), 0); + CPPUNIT_ASSERT_EQUAL(int(b.maxExtent()), 1); + } + { + const openvdb::Coord max = min + openvdb::Coord(2,1,3); + const openvdb::CoordBBox b(min, max); + CPPUNIT_ASSERT_EQUAL(int(b.minExtent()), 1); + CPPUNIT_ASSERT_EQUAL(int(b.maxExtent()), 2); + } + { + const openvdb::Coord max = min + openvdb::Coord(2,3,1); + const openvdb::CoordBBox b(min, max); + CPPUNIT_ASSERT_EQUAL(int(b.minExtent()), 2); + CPPUNIT_ASSERT_EQUAL(int(b.maxExtent()), 1); + } + { + const openvdb::Coord max = min + openvdb::Coord(3,1,2); + const openvdb::CoordBBox b(min, max); + CPPUNIT_ASSERT_EQUAL(int(b.minExtent()), 1); + CPPUNIT_ASSERT_EQUAL(int(b.maxExtent()), 0); + } + { + const openvdb::Coord max = min + openvdb::Coord(3,2,1); + const openvdb::CoordBBox b(min, max); + CPPUNIT_ASSERT_EQUAL(int(b.minExtent()), 2); + CPPUNIT_ASSERT_EQUAL(int(b.maxExtent()), 0); + } + } + + {//reset + openvdb::CoordBBox b; + CPPUNIT_ASSERT_EQUAL(openvdb::Coord::max(), b.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord::min(), b.max()); + CPPUNIT_ASSERT(b.empty()); + + const openvdb::Coord min(-1,-2,30), max(20,30,55); + b.reset(min, max); + CPPUNIT_ASSERT_EQUAL(min, b.min()); + CPPUNIT_ASSERT_EQUAL(max, b.max()); + CPPUNIT_ASSERT(!b.empty()); + + b.reset(); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord::max(), b.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord::min(), b.max()); + CPPUNIT_ASSERT(b.empty()); + } + + {// ZYX Iterator 1 + const openvdb::Coord min(-1,-2,3), max(2,3,5); + const openvdb::CoordBBox b(min, max); + const size_t count = b.volume(); + size_t n = 0; + openvdb::CoordBBox::ZYXIterator ijk(b); + for (int i=min[0]; i<=max[0]; ++i) { + for (int j=min[1]; j<=max[1]; ++j) { + for (int k=min[2]; k<=max[2]; ++k, ++ijk, ++n) { + CPPUNIT_ASSERT(ijk); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(i,j,k), *ijk); + } + } + } + CPPUNIT_ASSERT_EQUAL(count, n); + CPPUNIT_ASSERT(!ijk); + ++ijk; + CPPUNIT_ASSERT(!ijk); + } + + {// ZYX Iterator 2 + const openvdb::Coord min(-1,-2,3), max(2,3,5); + const openvdb::CoordBBox b(min, max); + const size_t count = b.volume(); + size_t n = 0; + openvdb::Coord::ValueType unused = 0; + for (const auto& ijk: b) { + unused += ijk[0]; + CPPUNIT_ASSERT(++n <= count); + } + CPPUNIT_ASSERT_EQUAL(count, n); + } + + {// XYZ Iterator 1 + const openvdb::Coord min(-1,-2,3), max(2,3,5); + const openvdb::CoordBBox b(min, max); + const size_t count = b.volume(); + size_t n = 0; + openvdb::CoordBBox::XYZIterator ijk(b); + for (int k=min[2]; k<=max[2]; ++k) { + for (int j=min[1]; j<=max[1]; ++j) { + for (int i=min[0]; i<=max[0]; ++i, ++ijk, ++n) { + CPPUNIT_ASSERT( ijk ); + CPPUNIT_ASSERT_EQUAL( openvdb::Coord(i,j,k), *ijk ); + } + } + } + CPPUNIT_ASSERT_EQUAL(count, n); + CPPUNIT_ASSERT( !ijk ); + ++ijk; + CPPUNIT_ASSERT( !ijk ); + } + + {// XYZ Iterator 2 + const openvdb::Coord min(-1,-2,3), max(2,3,5); + const openvdb::CoordBBox b(min, max); + const size_t count = b.volume(); + size_t n = 0; + for (auto ijk = b.beginXYZ(); ijk; ++ijk) { + CPPUNIT_ASSERT( ++n <= count ); + } + CPPUNIT_ASSERT_EQUAL(count, n); + } + + {// bit-wise operations + const openvdb::Coord min(-1,-2,3), max(2,3,5); + const openvdb::CoordBBox b(min, max); + CPPUNIT_ASSERT_EQUAL(openvdb::CoordBBox(min>>1,max>>1), b>>size_t(1)); + CPPUNIT_ASSERT_EQUAL(openvdb::CoordBBox(min>>3,max>>3), b>>size_t(3)); + CPPUNIT_ASSERT_EQUAL(openvdb::CoordBBox(min<<1,max<<1), b<() != b.hash<>()); + CPPUNIT_ASSERT(a.hash<10>() != b.hash<10>()); + CPPUNIT_ASSERT(a.hash<5>() != b.hash<5>()); + } + + {//test std::hash function + std::hash h; + openvdb::Coord a(-1, 34, 67), b(-2, 34, 67); + CPPUNIT_ASSERT(h(a) != h(b)); + } + + {//test hash map (= unordered_map) + using KeyT = openvdb::Coord; + using ValueT = size_t; + using HashT = std::hash; + + std::unordered_map h; + const openvdb::Coord min(-10,-20,30), max(20,30,50); + const openvdb::CoordBBox bbox(min, max); + size_t n = 0; + for (const auto& ijk: bbox) h[ijk] = n++; + CPPUNIT_ASSERT_EQUAL(h.size(), n); + n = 0; + for (const auto& ijk: bbox) CPPUNIT_ASSERT_EQUAL(h[ijk], n++); + CPPUNIT_ASSERT(h.load_factor() <= 1.0f);// no hask key collisions! + } +} diff --git a/openvdb/unittest/TestCpt.cc b/openvdb/unittest/TestCpt.cc new file mode 100644 index 00000000..1f0e7bf0 --- /dev/null +++ b/openvdb/unittest/TestCpt.cc @@ -0,0 +1,535 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include // for old GradientStencil +#include "util.h" // for unittest_util::makeSphere() + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + +class TestCpt: public CppUnit::TestFixture +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestCpt); + CPPUNIT_TEST(testCpt); // Cpt in World Space + CPPUNIT_TEST(testCptStencil); + CPPUNIT_TEST(testCptTool); // Cpt tool + CPPUNIT_TEST(testCptMaskedTool); + CPPUNIT_TEST(testOldStyleStencils); // old stencil impl + CPPUNIT_TEST_SUITE_END(); + + void testCpt(); + void testCptStencil(); + void testCptTool(); + void testCptMaskedTool(); + void testOldStyleStencils(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestCpt); + +void +TestCpt::testCpt() +{ + using namespace openvdb; + + typedef FloatGrid::ConstAccessor AccessorType; + + { // unit voxel size tests + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + const FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Coord dim(64,64,64); + const Vec3f center(35.0, 30.0f, 40.0f); + const float radius=0;//point at {35,30,40} + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + + AccessorType inAccessor = grid->getConstAccessor(); + // this uses the gradient. Only test for a few maps, since the gradient is + // tested elsewhere + + Coord xyz(35,30,30); + + math::TranslationMap translate; + // Note the CPT::result is in continuous index space + Vec3f P = math::CPT::result(translate, inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + // CPT_RANGE::result is in the range of the map + // CPT_RANGE::result = map.applyMap(CPT::result()) + // for our tests, the map is an identity so in this special case + // the two versions of the Cpt should exactly agree + P = math::CPT_RANGE::result(translate, inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + xyz.reset(35,30,35); + + P = math::CPT::result(translate, inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + + P = math::CPT_RANGE::result(translate, inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + } + { + // NON-UNIT VOXEL SIZE + + double voxel_size = 0.5; + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(voxel_size)); + CPPUNIT_ASSERT(grid->empty()); + AccessorType inAccessor = grid->getConstAccessor(); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10;//i.e. (16,8,10) and (6,8,0) are on the sphere + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + + Coord xyz(20,16,20);//i.e. (10,8,10) in world space or 6 world units inside the sphere + math::AffineMap affine(voxel_size*math::Mat3d::identity()); + + Vec3f P = math::CPT::result(affine, inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(32,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(16,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(20,P[2]); + + + P = math::CPT_RANGE::result(affine, inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(16,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(8,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(10,P[2]); + + xyz.reset(12,16,10); + + P = math::CPT::result(affine, inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(12,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(16,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0,P[2]); + + + P = math::CPT_RANGE::result(affine, inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(6,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(8,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0,P[2]); + + } + { + // NON-UNIFORM SCALING + Vec3d voxel_sizes(0.5, 1, 0.5); + math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes)); + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::Ptr(new math::Transform(base_map))); + + CPPUNIT_ASSERT(grid->empty()); + AccessorType inAccessor = grid->getConstAccessor(); + + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10;//i.e. (16,8,10) and (6,8,0) are on the sphere + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + + Coord ijk = grid->transform().worldToIndexNodeCentered(Vec3d(10,8,10)); + + //Coord xyz(20,16,20);//i.e. (10,8,10) in world space or 6 world units inside the sphere + math::ScaleMap scale(voxel_sizes); + Vec3f P; + P = math::CPT::result(scale, inAccessor, ijk); + ASSERT_DOUBLES_EXACTLY_EQUAL(32,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(8,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(20,P[2]); + + + // world space result + P = math::CPT_RANGE::result(scale, inAccessor, ijk); + CPPUNIT_ASSERT_DOUBLES_EQUAL(16,P[0], 0.02 ); + CPPUNIT_ASSERT_DOUBLES_EQUAL(8, P[1], 0.02); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10,P[2], 0.02); + + //xyz.reset(12,16,10); + ijk = grid->transform().worldToIndexNodeCentered(Vec3d(6,8,5)); + + P = math::CPT::result(scale, inAccessor, ijk); + ASSERT_DOUBLES_EXACTLY_EQUAL(12,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(8,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0,P[2]); + + + P = math::CPT_RANGE::result(scale, inAccessor, ijk); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6,P[0], 0.02); + CPPUNIT_ASSERT_DOUBLES_EQUAL(8,P[1], 0.02); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0,P[2], 0.02); + + } + +} + + +void +TestCpt::testCptStencil() +{ + using namespace openvdb; + + { // UNIT VOXEL TEST + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + const FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f ,30.0f, 40.0f); + const float radius=0.0f; + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + // this uses the gradient. Only test for a few maps, since the gradient is + // tested elsewhere + + math::SevenPointStencil sevenpt(*grid); + math::SecondOrderDenseStencil dense_2nd(*grid); + + + Coord xyz(35,30,30); + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + + sevenpt.moveTo(xyz); + dense_2nd.moveTo(xyz); + + math::TranslationMap translate; + // Note the CPT::result is in continuous index space + Vec3f P = math::CPT::result(translate, sevenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + // CPT_RANGE::result_stencil is in the range of the map + // CPT_RANGE::result_stencil = map.applyMap(CPT::result_stencil()) + // for our tests, the map is an identity so in this special case + // the two versions of the Cpt should exactly agree + P = math::CPT_RANGE::result(translate, sevenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + xyz.reset(35,30,35); + + sevenpt.moveTo(xyz); + dense_2nd.moveTo(xyz); + + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + + P = math::CPT::result(translate, sevenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + + P = math::CPT_RANGE::result(translate, sevenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + + xyz.reset(35,30,30); + + sevenpt.moveTo(xyz); + dense_2nd.moveTo(xyz); + + math::AffineMap affine; + + P = math::CPT::result(affine, dense_2nd); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + + P = math::CPT_RANGE::result(affine, dense_2nd); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + xyz.reset(35,30,35); + + sevenpt.moveTo(xyz); + dense_2nd.moveTo(xyz); + + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + + P = math::CPT::result(affine, dense_2nd); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + + P = math::CPT_RANGE::result(affine, dense_2nd); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + } + { + // NON-UNIT VOXEL SIZE + + double voxel_size = 0.5; + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(voxel_size)); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10;//i.e. (16,8,10) and (6,8,0) are on the sphere + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + + + math::SecondOrderDenseStencil dense_2nd(*grid); + + + Coord xyz(20,16,20);//i.e. (10,8,10) in world space or 6 world units inside the sphere + math::AffineMap affine(voxel_size*math::Mat3d::identity()); + dense_2nd.moveTo(xyz); + + Vec3f P = math::CPT::result(affine, dense_2nd); + ASSERT_DOUBLES_EXACTLY_EQUAL(32,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(16,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(20,P[2]); + + + P = math::CPT_RANGE::result(affine, dense_2nd); + ASSERT_DOUBLES_EXACTLY_EQUAL(16,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(8,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(10,P[2]); + + xyz.reset(12,16,10); + dense_2nd.moveTo(xyz); + + P = math::CPT::result(affine, dense_2nd); + ASSERT_DOUBLES_EXACTLY_EQUAL(12,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(16,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0,P[2]); + + + P = math::CPT_RANGE::result(affine, dense_2nd); + ASSERT_DOUBLES_EXACTLY_EQUAL(6,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(8,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0,P[2]); + + } + { + // NON-UNIFORM SCALING + Vec3d voxel_sizes(0.5, 1, 0.5); + math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes)); + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::Ptr(new math::Transform(base_map))); + + CPPUNIT_ASSERT(grid->empty()); + + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10;//i.e. (16,8,10) and (6,8,0) are on the sphere + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + + Coord ijk = grid->transform().worldToIndexNodeCentered(Vec3d(10,8,10)); + math::SevenPointStencil sevenpt(*grid); + + sevenpt.moveTo(ijk); + + //Coord xyz(20,16,20);//i.e. (10,8,10) in world space or 6 world units inside the sphere + math::ScaleMap scale(voxel_sizes); + Vec3f P; + P = math::CPT::result(scale, sevenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(32,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(8,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(20,P[2]); + + + // world space result + P = math::CPT_RANGE::result(scale, sevenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(16,P[0], 0.02 ); + CPPUNIT_ASSERT_DOUBLES_EQUAL(8, P[1], 0.02); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10,P[2], 0.02); + + //xyz.reset(12,16,10); + ijk = grid->transform().worldToIndexNodeCentered(Vec3d(6,8,5)); + sevenpt.moveTo(ijk); + P = math::CPT::result(scale, sevenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(12,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(8,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0,P[2]); + + + P = math::CPT_RANGE::result(scale, sevenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6,P[0], 0.02); + CPPUNIT_ASSERT_DOUBLES_EQUAL(8,P[1], 0.02); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0,P[2], 0.02); + + } + + +} + +void +TestCpt::testCptTool() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + const FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f); + const float radius=0;//point at {35,30,40} + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + // run the tool + typedef openvdb::tools::Cpt FloatCpt; + FloatCpt cpt(*grid); + FloatCpt::OutGridType::Ptr cptGrid = + cpt.process(true/*threaded*/, false/*use world transform*/); + + FloatCpt::OutGridType::ConstAccessor cptAccessor = cptGrid->getConstAccessor(); + + Coord xyz(35,30,30); + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + + Vec3f P = cptAccessor.getValue(xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); + + xyz.reset(35,30,35); + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + + P = cptAccessor.getValue(xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0],P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1],P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2],P[2]); +} + +void +TestCpt::testCptMaskedTool() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + const FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f); + const float radius=0;//point at {35,30,40} + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + const openvdb::CoordBBox maskbbox(openvdb::Coord(35, 30, 30), openvdb::Coord(41, 41, 41)); + BoolGrid::Ptr maskGrid = BoolGrid::create(false); + maskGrid->fill(maskbbox, true/*value*/, true/*activate*/); + + // run the tool + //typedef openvdb::tools::Cpt FloatCpt;//fails because MaskT defaults to MaskGrid + typedef openvdb::tools::Cpt FloatCpt; + FloatCpt cpt(*grid, *maskGrid); + FloatCpt::OutGridType::Ptr cptGrid = + cpt.process(true/*threaded*/, false/*use world transform*/); + + FloatCpt::OutGridType::ConstAccessor cptAccessor = cptGrid->getConstAccessor(); + + // inside the masked region + Coord xyz(35,30,30); + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + + Vec3f P = cptAccessor.getValue(xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[0], P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[1], P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(center[2], P[2]); + + // outside the masked region + xyz.reset(42,42,42); + CPPUNIT_ASSERT(!cptAccessor.isValueOn(xyz)); +} + +void +TestCpt::testOldStyleStencils() +{ + using namespace openvdb; + + {// test of level set to sphere at (6,8,10) with R=10 and dx=0.5 + + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(/*voxel size=*/0.5)); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f,8.0f,10.0f);//i.e. (12,16,20) in index space + const float radius=10;//i.e. (16,8,10) and (6,8,0) are on the sphere + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + math::GradStencil gs(*grid); + + Coord xyz(20,16,20);//i.e. (10,8,10) in world space or 6 world units inside the sphere + gs.moveTo(xyz); + float dist = gs.getValue();//signed closest distance to sphere in world coordinates + Vec3f P = gs.cpt();//closes point to sphere in index space + ASSERT_DOUBLES_EXACTLY_EQUAL(dist,-6); + ASSERT_DOUBLES_EXACTLY_EQUAL(32,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(16,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(20,P[2]); + + xyz.reset(12,16,10);//i.e. (6,8,5) in world space or 15 world units inside the sphere + gs.moveTo(xyz); + dist = gs.getValue();//signed closest distance to sphere in world coordinates + P = gs.cpt();//closes point to sphere in index space + ASSERT_DOUBLES_EXACTLY_EQUAL(-5,dist); + ASSERT_DOUBLES_EXACTLY_EQUAL(12,P[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(16,P[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL( 0,P[2]); + } +} diff --git a/openvdb/unittest/TestCurl.cc b/openvdb/unittest/TestCurl.cc new file mode 100644 index 00000000..c0a7ad15 --- /dev/null +++ b/openvdb/unittest/TestCurl.cc @@ -0,0 +1,561 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/1e-6); + +namespace { +const int GRID_DIM = 10; +} + + +class TestCurl: public CppUnit::TestFixture +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestCurl); + CPPUNIT_TEST(testISCurl); // Gradient in Index Space + CPPUNIT_TEST(testISCurlStencil); + CPPUNIT_TEST(testWSCurl); // Gradient in World Space + CPPUNIT_TEST(testWSCurlStencil); + CPPUNIT_TEST(testCurlTool); // Gradient tool + CPPUNIT_TEST(testCurlMaskedTool); // Gradient tool + + CPPUNIT_TEST_SUITE_END(); + + void testISCurl(); + void testISCurlStencil(); + void testWSCurl(); + void testWSCurlStencil(); + void testCurlTool(); + void testCurlMaskedTool(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestCurl); + + +void +TestCurl::testCurlTool() +{ + using namespace openvdb; + + VectorGrid::Ptr inGrid = VectorGrid::create(); + const VectorTree& inTree = inGrid->tree(); + CPPUNIT_ASSERT(inTree.empty()); + + VectorGrid::Accessor inAccessor = inGrid->getAccessor(); + int dim = GRID_DIM; + for (int x = -dim; xactiveVoxelCount())); + + VectorGrid::ConstAccessor curlAccessor = curl_grid->getConstAccessor(); + --dim;//ignore boundary curl vectors + for (int x = -dim; x +#include +#include +#include +#include +#include +#ifdef BENCHMARK_TEST +#include +#endif + + +class TestDense: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestDense); + + CPPUNIT_TEST(testDenseZYX); + CPPUNIT_TEST(testDenseXYZ); + + CPPUNIT_TEST(testCopyZYX); + CPPUNIT_TEST(testCopyXYZ); + + CPPUNIT_TEST(testCopyBoolZYX); + CPPUNIT_TEST(testCopyBoolXYZ); + + CPPUNIT_TEST(testCopyFromDenseWithOffsetZYX); + CPPUNIT_TEST(testCopyFromDenseWithOffsetXYZ); + + CPPUNIT_TEST(testDense2SparseZYX); + CPPUNIT_TEST(testDense2SparseXYZ); + + CPPUNIT_TEST(testDense2Sparse2ZYX); + CPPUNIT_TEST(testDense2Sparse2XYZ); + + CPPUNIT_TEST(testInvalidBBoxZYX); + CPPUNIT_TEST(testInvalidBBoxXYZ); + + CPPUNIT_TEST(testDense2Sparse2DenseZYX); + CPPUNIT_TEST(testDense2Sparse2DenseXYZ); + CPPUNIT_TEST_SUITE_END(); + + void testDenseZYX(); + void testDenseXYZ(); + + template + void testCopy(); + void testCopyZYX() { this->testCopy(); } + void testCopyXYZ() { this->testCopy(); } + + template + void testCopyBool(); + void testCopyBoolZYX() { this->testCopyBool(); } + void testCopyBoolXYZ() { this->testCopyBool(); } + + template + void testCopyFromDenseWithOffset(); + void testCopyFromDenseWithOffsetZYX() { + this->testCopyFromDenseWithOffset(); + } + void testCopyFromDenseWithOffsetXYZ() { + this->testCopyFromDenseWithOffset(); + } + + template + void testDense2Sparse(); + void testDense2SparseZYX() { this->testDense2Sparse(); } + void testDense2SparseXYZ() { this->testDense2Sparse(); } + + template + void testDense2Sparse2(); + void testDense2Sparse2ZYX() { this->testDense2Sparse2(); } + void testDense2Sparse2XYZ() { this->testDense2Sparse2(); } + + template + void testInvalidBBox(); + void testInvalidBBoxZYX() { this->testInvalidBBox(); } + void testInvalidBBoxXYZ() { this->testInvalidBBox(); } + + template + void testDense2Sparse2Dense(); + void testDense2Sparse2DenseZYX() { this->testDense2Sparse2Dense(); } + void testDense2Sparse2DenseXYZ() { this->testDense2Sparse2Dense(); } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestDense); + + +void +TestDense::testDenseZYX() +{ + const openvdb::CoordBBox bbox(openvdb::Coord(-40,-5, 6), + openvdb::Coord(-11, 7,22)); + openvdb::tools::Dense dense(bbox);//LayoutZYX is the default + + // Check Desne::origin() + CPPUNIT_ASSERT(openvdb::Coord(-40,-5, 6) == dense.origin()); + + // Check coordToOffset and offsetToCoord + size_t offset = 0; + for (openvdb::Coord P(bbox.min()); P[0] <= bbox.max()[0]; ++P[0]) { + for (P[1] = bbox.min()[1]; P[1] <= bbox.max()[1]; ++P[1]) { + for (P[2] = bbox.min()[2]; P[2] <= bbox.max()[2]; ++P[2]) { + //std::cerr << "offset = " << offset << " P = " << P << std::endl; + CPPUNIT_ASSERT_EQUAL(offset, dense.coordToOffset(P)); + CPPUNIT_ASSERT_EQUAL(P - dense.origin(), dense.offsetToLocalCoord(offset)); + CPPUNIT_ASSERT_EQUAL(P, dense.offsetToCoord(offset)); + ++offset; + } + } + } + + // Check Dense::valueCount + const int size = static_cast(dense.valueCount()); + CPPUNIT_ASSERT_EQUAL(30*13*17, size); + + // Check Dense::fill(float) and Dense::getValue(size_t) + const float v = 0.234f; + dense.fill(v); + for (int i=0; i dense(bbox); + + // Check Desne::origin() + CPPUNIT_ASSERT(openvdb::Coord(-40,-5, 6) == dense.origin()); + + // Check coordToOffset and offsetToCoord + size_t offset = 0; + for (openvdb::Coord P(bbox.min()); P[2] <= bbox.max()[2]; ++P[2]) { + for (P[1] = bbox.min()[1]; P[1] <= bbox.max()[1]; ++P[1]) { + for (P[0] = bbox.min()[0]; P[0] <= bbox.max()[0]; ++P[0]) { + //std::cerr << "offset = " << offset << " P = " << P << std::endl; + CPPUNIT_ASSERT_EQUAL(offset, dense.coordToOffset(P)); + CPPUNIT_ASSERT_EQUAL(P - dense.origin(), dense.offsetToLocalCoord(offset)); + CPPUNIT_ASSERT_EQUAL(P, dense.offsetToCoord(offset)); + ++offset; + } + } + } + + // Check Dense::valueCount + const int size = static_cast(dense.valueCount()); + CPPUNIT_ASSERT_EQUAL(30*13*17, size); + + // Check Dense::fill(float) and Dense::getValue(size_t) + const float v = 0.234f; + dense.fill(v); + for (int i=0; i > +class CheckDense +{ +public: + typedef typename TreeT::ValueType ValueT; + + CheckDense() : mTree(NULL), mDense(NULL) + { + CPPUNIT_ASSERT(DenseT::memoryLayout() == openvdb::tools::LayoutZYX || + DenseT::memoryLayout() == openvdb::tools::LayoutXYZ ); + } + + void check(const TreeT& tree, const DenseT& dense) + { + mTree = &tree; + mDense = &dense; + tbb::parallel_for(dense.bbox(), *this); + } + void operator()(const openvdb::CoordBBox& bbox) const + { + openvdb::tree::ValueAccessor acc(*mTree); + + if (DenseT::memoryLayout() == openvdb::tools::LayoutZYX) {//resolved at compiletime + for (openvdb::Coord P(bbox.min()); P[0] <= bbox.max()[0]; ++P[0]) { + for (P[1] = bbox.min()[1]; P[1] <= bbox.max()[1]; ++P[1]) { + for (P[2] = bbox.min()[2]; P[2] <= bbox.max()[2]; ++P[2]) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(acc.getValue(P), mDense->getValue(P), + /*tolerance=*/0.0001); + } + } + } + } else { + for (openvdb::Coord P(bbox.min()); P[2] <= bbox.max()[2]; ++P[2]) { + for (P[1] = bbox.min()[1]; P[1] <= bbox.max()[1]; ++P[1]) { + for (P[0] = bbox.min()[0]; P[0] <= bbox.max()[0]; ++P[0]) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(acc.getValue(P), mDense->getValue(P), + /*tolerance=*/0.0001); + } + } + } + } + } +private: + const TreeT* mTree; + const DenseT* mDense; +};// CheckDense + +template +void +TestDense::testCopy() +{ + using namespace openvdb; + + //std::cerr << "\nTesting testCopy with " + // << (Layout == tools::LayoutXYZ ? "XYZ" : "ZYX") << " memory layout" + // << std::endl; + + typedef tools::Dense DenseT; + CheckDense checkDense; + const float radius = 10.0f, tolerance = 0.00001f; + const Vec3f center(0.0f); + // decrease the voxelSize to test larger grids +#ifdef BENCHMARK_TEST + const float voxelSize = 0.05f, width = 5.0f; +#else + const float voxelSize = 0.5f, width = 5.0f; +#endif + + // Create a VDB containing a level set of a sphere + FloatGrid::Ptr grid = + tools::createLevelSetSphere(radius, center, voxelSize, width); + FloatTree& tree0 = grid->tree(); + + // Create an empty dense grid + DenseT dense(grid->evalActiveVoxelBoundingBox()); +#ifdef BENCHMARK_TEST + std::cerr << "\nBBox = " << grid->evalActiveVoxelBoundingBox() << std::endl; +#endif + + {//check Dense::fill + dense.fill(voxelSize); +#ifndef BENCHMARK_TEST + checkDense.check(FloatTree(voxelSize), dense); +#endif + } + + {// parallel convert to dense +#ifdef BENCHMARK_TEST + util::CpuTimer ts; + ts.start("CopyToDense"); +#endif + tools::copyToDense(*grid, dense); +#ifdef BENCHMARK_TEST + ts.stop(); +#else + checkDense.check(tree0, dense); +#endif + } + + {// Parallel create from dense +#ifdef BENCHMARK_TEST + util::CpuTimer ts; + ts.start("CopyFromDense"); +#endif + FloatTree tree1(tree0.background()); + tools::copyFromDense(dense, tree1, tolerance); +#ifdef BENCHMARK_TEST + ts.stop(); +#else + checkDense.check(tree1, dense); +#endif + } +} + +template +void +TestDense::testCopyBool() +{ + using namespace openvdb; + + //std::cerr << "\nTesting testCopyBool with " + // << (Layout == tools::LayoutXYZ ? "XYZ" : "ZYX") << " memory layout" + // << std::endl; + + const Coord bmin(-1), bmax(8); + const CoordBBox bbox(bmin, bmax); + + BoolGrid::Ptr grid = createGrid(false); + BoolGrid::ConstAccessor acc = grid->getConstAccessor(); + + typedef openvdb::tools::Dense DenseT; + DenseT dense(bbox); + dense.fill(false); + + // Start with sparse and dense grids both filled with false. + Coord xyz; + int &x = xyz[0], &y = xyz[1], &z = xyz[2]; + for (x = bmin.x(); x <= bmax.x(); ++x) { + for (y = bmin.y(); y <= bmax.y(); ++y) { + for (z = bmin.z(); z <= bmax.z(); ++z) { + CPPUNIT_ASSERT_EQUAL(false, dense.getValue(xyz)); + CPPUNIT_ASSERT_EQUAL(false, acc.getValue(xyz)); + } + } + } + + // Fill the dense grid with true. + dense.fill(true); + // Copy the contents of the dense grid to the sparse grid. + tools::copyFromDense(dense, *grid, /*tolerance=*/false); + + // Verify that both sparse and dense grids are now filled with true. + for (x = bmin.x(); x <= bmax.x(); ++x) { + for (y = bmin.y(); y <= bmax.y(); ++y) { + for (z = bmin.z(); z <= bmax.z(); ++z) { + CPPUNIT_ASSERT_EQUAL(true, dense.getValue(xyz)); + CPPUNIT_ASSERT_EQUAL(true, acc.getValue(xyz)); + } + } + } + + // Fill the dense grid with false. + dense.fill(false); + // Copy the contents (= true) of the sparse grid to the dense grid. + tools::copyToDense(*grid, dense); + + // Verify that the dense grid is now filled with true. + for (x = bmin.x(); x <= bmax.x(); ++x) { + for (y = bmin.y(); y <= bmax.y(); ++y) { + for (z = bmin.z(); z <= bmax.z(); ++z) { + CPPUNIT_ASSERT_EQUAL(true, dense.getValue(xyz)); + } + } + } +} + + +// Test copying from a dense grid to a sparse grid with various bounding boxes. +template +void +TestDense::testCopyFromDenseWithOffset() +{ + using namespace openvdb; + + //std::cerr << "\nTesting testCopyFromDenseWithOffset with " + // << (Layout == tools::LayoutXYZ ? "XYZ" : "ZYX") << " memory layout" + // << std::endl; + + typedef openvdb::tools::Dense DenseT; + + const int DIM = 20, COUNT = DIM * DIM * DIM; + const float FOREGROUND = 99.0f, BACKGROUND = 5000.0f; + + const int OFFSET[] = { 1, -1, 1001, -1001 }; + for (int offsetIdx = 0; offsetIdx < 4; ++offsetIdx) { + + const int offset = OFFSET[offsetIdx]; + const CoordBBox bbox = CoordBBox::createCube(Coord(offset), DIM); + + DenseT dense(bbox, FOREGROUND); + CPPUNIT_ASSERT_EQUAL(bbox, dense.bbox()); + + FloatGrid grid(BACKGROUND); + tools::copyFromDense(dense, grid, /*tolerance=*/0.0); + + const CoordBBox gridBBox = grid.evalActiveVoxelBoundingBox(); + CPPUNIT_ASSERT_EQUAL(bbox, gridBBox); + CPPUNIT_ASSERT_EQUAL(COUNT, int(grid.activeVoxelCount())); + + FloatGrid::ConstAccessor acc = grid.getConstAccessor(); + for (int i = gridBBox.min()[0], ie = gridBBox.max()[0]; i < ie; ++i) { + for (int j = gridBBox.min()[1], je = gridBBox.max()[1]; j < je; ++j) { + for (int k = gridBBox.min()[2], ke = gridBBox.max()[2]; k < ke; ++k) { + const Coord ijk(i, j, k); + CPPUNIT_ASSERT_DOUBLES_EQUAL( + FOREGROUND, acc.getValue(ijk), /*tolerance=*/0.0); + CPPUNIT_ASSERT(acc.isValueOn(ijk)); + } + } + } + } +} + +template +void +TestDense::testDense2Sparse() +{ + // The following test revealed a bug in v2.0.0b2 + using namespace openvdb; + + //std::cerr << "\nTesting testDense2Sparse with " + // << (Layout == tools::LayoutXYZ ? "XYZ" : "ZYX") << " memory layout" + // << std::endl; + + typedef tools::Dense DenseT; + + // Test Domain Resolution + Int32 sizeX = 8, sizeY = 8, sizeZ = 9; + + // Define a dense grid + DenseT dense(Coord(sizeX, sizeY, sizeZ)); + const CoordBBox bboxD = dense.bbox(); + // std::cerr << "\nDense bbox" << bboxD << std::endl; + + // Verify that the CoordBBox is truely used as [inclusive, inclusive] + CPPUNIT_ASSERT(int(dense.valueCount()) == int(sizeX * sizeY * sizeZ)); + + // Fill the dense grid with constant value 1. + dense.fill(1.0f); + + // Create two empty float grids + FloatGrid::Ptr gridS = FloatGrid::create(0.0f /*background*/); + FloatGrid::Ptr gridP = FloatGrid::create(0.0f /*background*/); + + // Convert in serial and parallel modes + tools::copyFromDense(dense, *gridS, /*tolerance*/0.0f, /*serial = */ true); + tools::copyFromDense(dense, *gridP, /*tolerance*/0.0f, /*serial = */ false); + + float minS, maxS; + float minP, maxP; + + gridS->evalMinMax(minS, maxS); + gridP->evalMinMax(minP, maxP); + + const float tolerance = 0.0001f; + + CPPUNIT_ASSERT_DOUBLES_EQUAL(minS, minP, tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(maxS, maxP, tolerance); + CPPUNIT_ASSERT_EQUAL(gridP->activeVoxelCount(), Index64(sizeX * sizeY * sizeZ)); + + const FloatTree& treeS = gridS->tree(); + const FloatTree& treeP = gridP->tree(); + + // Values in Test Domain are correct + for (Coord ijk(bboxD.min()); ijk[0] <= bboxD.max()[0]; ++ijk[0]) { + for (ijk[1] = bboxD.min()[1]; ijk[1] <= bboxD.max()[1]; ++ijk[1]) { + for (ijk[2] = bboxD.min()[2]; ijk[2] <= bboxD.max()[2]; ++ijk[2]) { + + const float expected = bboxD.isInside(ijk) ? 1.f : 0.f; + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, 1.f, tolerance); + + const float& vS = treeS.getValue(ijk); + const float& vP = treeP.getValue(ijk); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, vS, tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, vP, tolerance); + } + } + } + + CoordBBox bboxP = gridP->evalActiveVoxelBoundingBox(); + const Index64 voxelCountP = gridP->activeVoxelCount(); + //std::cerr << "\nParallel: bbox=" << bboxP << " voxels=" << voxelCountP << std::endl; + CPPUNIT_ASSERT( bboxP == bboxD ); + CPPUNIT_ASSERT_EQUAL( dense.valueCount(), voxelCountP); + + CoordBBox bboxS = gridS->evalActiveVoxelBoundingBox(); + const Index64 voxelCountS = gridS->activeVoxelCount(); + //std::cerr << "\nSerial: bbox=" << bboxS << " voxels=" << voxelCountS << std::endl; + CPPUNIT_ASSERT( bboxS == bboxD ); + CPPUNIT_ASSERT_EQUAL( dense.valueCount(), voxelCountS); + + // Topology + CPPUNIT_ASSERT( bboxS.isInside(bboxS) ); + CPPUNIT_ASSERT( bboxP.isInside(bboxP) ); + CPPUNIT_ASSERT( bboxS.isInside(bboxP) ); + CPPUNIT_ASSERT( bboxP.isInside(bboxS) ); + + /// Check that the two grids agree + for (Coord ijk(bboxS.min()); ijk[0] <= bboxS.max()[0]; ++ijk[0]) { + for (ijk[1] = bboxS.min()[1]; ijk[1] <= bboxS.max()[1]; ++ijk[1]) { + for (ijk[2] = bboxS.min()[2]; ijk[2] <= bboxS.max()[2]; ++ijk[2]) { + + const float& vS = treeS.getValue(ijk); + const float& vP = treeP.getValue(ijk); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(vS, vP, tolerance); + + // the value we should get based on the original domain + const float expected = bboxD.isInside(ijk) ? 1.f : 0.f; + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, vP, tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, vS, tolerance); + } + } + } + + + // Verify the tree topology matches. + + CPPUNIT_ASSERT_EQUAL(gridP->activeVoxelCount(), gridS->activeVoxelCount()); + CPPUNIT_ASSERT(gridP->evalActiveVoxelBoundingBox() == gridS->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT(treeP.hasSameTopology(treeS) ); + +} + +template +void +TestDense::testDense2Sparse2() +{ + // The following tests copying a dense grid into a VDB tree with + // existing values outside the bbox of the dense grid. + + using namespace openvdb; + + //std::cerr << "\nTesting testDense2Sparse2 with " + // << (Layout == tools::LayoutXYZ ? "XYZ" : "ZYX") << " memory layout" + // << std::endl; + + typedef tools::Dense DenseT; + + // Test Domain Resolution + const int sizeX = 8, sizeY = 8, sizeZ = 9; + const Coord magicVoxel(sizeX, sizeY, sizeZ); + + // Define a dense grid + DenseT dense(Coord(sizeX, sizeY, sizeZ)); + const CoordBBox bboxD = dense.bbox(); + //std::cerr << "\nDense bbox" << bboxD << std::endl; + + // Verify that the CoordBBox is truely used as [inclusive, inclusive] + CPPUNIT_ASSERT_EQUAL(sizeX * sizeY * sizeZ, static_cast(dense.valueCount())); + + // Fill the dense grid with constant value 1. + dense.fill(1.0f); + + // Create two empty float grids + FloatGrid::Ptr gridS = FloatGrid::create(0.0f /*background*/); + FloatGrid::Ptr gridP = FloatGrid::create(0.0f /*background*/); + gridS->tree().setValue(magicVoxel, 5.0f); + gridP->tree().setValue(magicVoxel, 5.0f); + + // Convert in serial and parallel modes + tools::copyFromDense(dense, *gridS, /*tolerance*/0.0f, /*serial = */ true); + tools::copyFromDense(dense, *gridP, /*tolerance*/0.0f, /*serial = */ false); + + float minS, maxS; + float minP, maxP; + + gridS->evalMinMax(minS, maxS); + gridP->evalMinMax(minP, maxP); + + const float tolerance = 0.0001f; + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0f, minP, tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0f, minS, tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(5.0f, maxP, tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(5.0f, maxS, tolerance); + CPPUNIT_ASSERT_EQUAL(gridP->activeVoxelCount(), Index64(1 + sizeX * sizeY * sizeZ)); + + const FloatTree& treeS = gridS->tree(); + const FloatTree& treeP = gridP->tree(); + + // Values in Test Domain are correct + for (Coord ijk(bboxD.min()); ijk[0] <= bboxD.max()[0]; ++ijk[0]) { + for (ijk[1] = bboxD.min()[1]; ijk[1] <= bboxD.max()[1]; ++ijk[1]) { + for (ijk[2] = bboxD.min()[2]; ijk[2] <= bboxD.max()[2]; ++ijk[2]) { + + const float expected = bboxD.isInside(ijk) ? 1.0f : 0.0f; + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, 1.0f, tolerance); + + const float& vS = treeS.getValue(ijk); + const float& vP = treeP.getValue(ijk); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, vS, tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, vP, tolerance); + } + } + } + + CoordBBox bboxP = gridP->evalActiveVoxelBoundingBox(); + const Index64 voxelCountP = gridP->activeVoxelCount(); + //std::cerr << "\nParallel: bbox=" << bboxP << " voxels=" << voxelCountP << std::endl; + CPPUNIT_ASSERT( bboxP != bboxD ); + CPPUNIT_ASSERT( bboxP == CoordBBox(Coord(0,0,0), magicVoxel) ); + CPPUNIT_ASSERT_EQUAL( dense.valueCount()+1, voxelCountP); + + CoordBBox bboxS = gridS->evalActiveVoxelBoundingBox(); + const Index64 voxelCountS = gridS->activeVoxelCount(); + //std::cerr << "\nSerial: bbox=" << bboxS << " voxels=" << voxelCountS << std::endl; + CPPUNIT_ASSERT( bboxS != bboxD ); + CPPUNIT_ASSERT( bboxS == CoordBBox(Coord(0,0,0), magicVoxel) ); + CPPUNIT_ASSERT_EQUAL( dense.valueCount()+1, voxelCountS); + + // Topology + CPPUNIT_ASSERT( bboxS.isInside(bboxS) ); + CPPUNIT_ASSERT( bboxP.isInside(bboxP) ); + CPPUNIT_ASSERT( bboxS.isInside(bboxP) ); + CPPUNIT_ASSERT( bboxP.isInside(bboxS) ); + + /// Check that the two grids agree + for (Coord ijk(bboxS.min()); ijk[0] <= bboxS.max()[0]; ++ijk[0]) { + for (ijk[1] = bboxS.min()[1]; ijk[1] <= bboxS.max()[1]; ++ijk[1]) { + for (ijk[2] = bboxS.min()[2]; ijk[2] <= bboxS.max()[2]; ++ijk[2]) { + + const float& vS = treeS.getValue(ijk); + const float& vP = treeP.getValue(ijk); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(vS, vP, tolerance); + + // the value we should get based on the original domain + const float expected = bboxD.isInside(ijk) ? 1.0f + : ijk == magicVoxel ? 5.0f : 0.0f; + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, vP, tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, vS, tolerance); + } + } + } + + // Verify the tree topology matches. + + CPPUNIT_ASSERT_EQUAL(gridP->activeVoxelCount(), gridS->activeVoxelCount()); + CPPUNIT_ASSERT(gridP->evalActiveVoxelBoundingBox() == gridS->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT(treeP.hasSameTopology(treeS) ); + +} + +template +void +TestDense::testInvalidBBox() +{ + using namespace openvdb; + + //std::cerr << "\nTesting testInvalidBBox with " + // << (Layout == tools::LayoutXYZ ? "XYZ" : "ZYX") << " memory layout" + // << std::endl; + + typedef tools::Dense DenseT; + const CoordBBox badBBox(Coord(1, 1, 1), Coord(-1, 2, 2)); + + CPPUNIT_ASSERT(badBBox.empty()); + CPPUNIT_ASSERT_THROW(DenseT dense(badBBox), ValueError); +} + +template +void +TestDense::testDense2Sparse2Dense() +{ + using namespace openvdb; + + //std::cerr << "\nTesting testDense2Sparse2Dense with " + // << (Layout == tools::LayoutXYZ ? "XYZ" : "ZYX") << " memory layout" + // << std::endl; + + typedef tools::Dense DenseT; + + const CoordBBox bboxBig(Coord(-12, 7, -32), Coord(12, 14, -15)); + const CoordBBox bboxSmall(Coord(-10, 8, -31), Coord(10, 12, -20)); + + + // A larger bbox + CoordBBox bboxBigger = bboxBig; + bboxBigger.expand(Coord(10)); + + + // Small is in big + CPPUNIT_ASSERT(bboxBig.isInside(bboxSmall)); + + // Big is in Bigger + CPPUNIT_ASSERT(bboxBigger.isInside(bboxBig)); + + // Construct a small dense grid + DenseT denseSmall(bboxSmall, 0.f); + { + // insert non-const values + const int n = static_cast(denseSmall.valueCount()); + float* d = denseSmall.data(); + for (int i = 0; i < n; ++i) { d[i] = static_cast(i); } + } + // Construct large dense grid + DenseT denseBig(bboxBig, 0.f); + { + // insert non-const values + const int n = static_cast(denseBig.valueCount()); + float* d = denseBig.data(); + for (int i = 0; i < n; ++i) { d[i] = static_cast(i); } + } + + // Make a sparse grid to copy this data into + FloatGrid::Ptr grid = FloatGrid::create(3.3f /*background*/); + tools::copyFromDense(denseBig, *grid, /*tolerance*/0.0f, /*serial = */ true); + tools::copyFromDense(denseSmall, *grid, /*tolerance*/0.0f, /*serial = */ false); + + const FloatTree& tree = grid->tree(); + // + CPPUNIT_ASSERT_EQUAL(bboxBig.volume(), grid->activeVoxelCount()); + + // iterate over the Bigger + for (Coord ijk(bboxBigger.min()); ijk[0] <= bboxBigger.max()[0]; ++ijk[0]) { + for (ijk[1] = bboxBigger.min()[1]; ijk[1] <= bboxBigger.max()[1]; ++ijk[1]) { + for (ijk[2] = bboxBigger.min()[2]; ijk[2] <= bboxBigger.max()[2]; ++ijk[2]) { + + float expected = 3.3f; + if (bboxSmall.isInside(ijk)) { + expected = denseSmall.getValue(ijk); + } else if (bboxBig.isInside(ijk)) { + expected = denseBig.getValue(ijk); + } + + const float& value = tree.getValue(ijk); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, value, 0.0001); + + } + } + } + + // Convert to Dense in small bbox + { + DenseT denseSmall2(bboxSmall); + tools::copyToDense(*grid, denseSmall2, true /* serial */); + + // iterate over the Bigger + for (Coord ijk(bboxSmall.min()); ijk[0] <= bboxSmall.max()[0]; ++ijk[0]) { + for (ijk[1] = bboxSmall.min()[1]; ijk[1] <= bboxSmall.max()[1]; ++ijk[1]) { + for (ijk[2] = bboxSmall.min()[2]; ijk[2] <= bboxSmall.max()[2]; ++ijk[2]) { + + const float& expected = denseSmall.getValue(ijk); + const float& value = denseSmall2.getValue(ijk); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, value, 0.0001); + } + } + } + } + // Convert to Dense in large bbox + { + DenseT denseBig2(bboxBig); + + tools::copyToDense(*grid, denseBig2, false /* serial */); + // iterate over the Bigger + for (Coord ijk(bboxBig.min()); ijk[0] <= bboxBig.max()[0]; ++ijk[0]) { + for (ijk[1] = bboxBig.min()[1]; ijk[1] <= bboxBig.max()[1]; ++ijk[1]) { + for (ijk[2] = bboxBig.min()[2]; ijk[2] <= bboxBig.max()[2]; ++ijk[2]) { + + float expected = -1.f; // should never be this + if (bboxSmall.isInside(ijk)) { + expected = denseSmall.getValue(ijk); + } else if (bboxBig.isInside(ijk)) { + expected = denseBig.getValue(ijk); + } + const float& value = denseBig2.getValue(ijk); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, value, 0.0001); + } + } + } + } +} +#undef BENCHMARK_TEST diff --git a/openvdb/unittest/TestDenseSparseTools.cc b/openvdb/unittest/TestDenseSparseTools.cc new file mode 100644 index 00000000..ec3c53b9 --- /dev/null +++ b/openvdb/unittest/TestDenseSparseTools.cc @@ -0,0 +1,380 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +class TestDenseSparseTools: public CppUnit::TestCase +{ +public: + virtual void setUp(); + virtual void tearDown() { if (mDense) delete mDense;} + + CPPUNIT_TEST_SUITE(TestDenseSparseTools); + CPPUNIT_TEST(testExtractSparseFloatTree); + CPPUNIT_TEST(testExtractSparseBoolTree); + CPPUNIT_TEST(testExtractSparseAltDenseLayout); + CPPUNIT_TEST(testExtractSparseMaskedTree); + CPPUNIT_TEST(testDenseTransform); + CPPUNIT_TEST(testOver); + CPPUNIT_TEST_SUITE_END(); + + void testExtractSparseFloatTree(); + void testExtractSparseBoolTree(); + void testExtractSparseAltDenseLayout(); + void testExtractSparseMaskedTree(); + void testDenseTransform(); + void testOver(); + +private: + openvdb::tools::Dense* mDense; + openvdb::math::Coord mijk; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestDenseSparseTools); + + +void +TestDenseSparseTools::setUp() +{ + namespace vdbmath = openvdb::math; + + // Domain for the dense grid + + vdbmath::CoordBBox domain(vdbmath::Coord(-100, -16, 12), + vdbmath::Coord( 90, 103, 100)); + + // Create dense grid, filled with 0.f + + mDense = new openvdb::tools::Dense(domain, 0.f); + + // Insert non-zero values + + mijk[0] = 1; mijk[1] = -2; mijk[2] = 14; +} + + +namespace { + + // Simple Rule for extracting data greater than a determined mMaskValue + // and producing a tree that holds type ValueType + namespace vdbmath = openvdb::math; + + class FloatRule + { + public: + // Standard tree type (e.g. BoolTree or FloatTree in openvdb.h) + typedef openvdb::FloatTree ResultTreeType; + typedef ResultTreeType::LeafNodeType ResultLeafNodeType; + + typedef float ResultValueType; + typedef float DenseValueType; + + FloatRule(const DenseValueType& value): mMaskValue(value){} + + template + void operator()(const DenseValueType& a, const IndexOrCoord& offset, + ResultLeafNodeType* leaf) const + { + if (a > mMaskValue) { + leaf->setValueOn(offset, a); + } + } + + private: + const DenseValueType mMaskValue; + }; + + class BoolRule + { + public: + // Standard tree type (e.g. BoolTree or FloatTree in openvdb.h) + typedef openvdb::BoolTree ResultTreeType; + typedef ResultTreeType::LeafNodeType ResultLeafNodeType; + + typedef bool ResultValueType; + typedef float DenseValueType; + + BoolRule(const DenseValueType& value): mMaskValue(value){} + + template + void operator()(const DenseValueType& a, const IndexOrCoord& offset, + ResultLeafNodeType* leaf) const + { + if (a > mMaskValue) { + leaf->setValueOn(offset, true); + } + } + + private: + const DenseValueType mMaskValue; + }; + + + // Square each value + struct SqrOp + { + float operator()(const float& in) const + { return in * in; } + }; +} + + +void +TestDenseSparseTools::testExtractSparseFloatTree() +{ + namespace vdbmath = openvdb::math; + + + FloatRule rule(0.5f); + + const float testvalue = 1.f; + mDense->setValue(mijk, testvalue); + const float background(0.f); + openvdb::FloatTree::Ptr result + = openvdb::tools::extractSparseTree(*mDense, rule, background); + + // The result should have only one active value. + + CPPUNIT_ASSERT(result->activeVoxelCount() == 1); + + // The result should have only one leaf + + CPPUNIT_ASSERT(result->leafCount() == 1); + + // The background + + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, result->background(), 1.e-6); + + // The stored value + + CPPUNIT_ASSERT_DOUBLES_EQUAL(testvalue, result->getValue(mijk), 1.e-6); +} + + +void +TestDenseSparseTools::testExtractSparseBoolTree() +{ + + const float testvalue = 1.f; + mDense->setValue(mijk, testvalue); + + const float cutoff(0.5); + + openvdb::BoolTree::Ptr result + = openvdb::tools::extractSparseTree(*mDense, BoolRule(cutoff), false); + + // The result should have only one active value. + + CPPUNIT_ASSERT(result->activeVoxelCount() == 1); + + // The result should have only one leaf + + CPPUNIT_ASSERT(result->leafCount() == 1); + + // The background + + CPPUNIT_ASSERT(result->background() == false); + + // The stored value + + CPPUNIT_ASSERT(result->getValue(mijk) == true); +} + + +void +TestDenseSparseTools::testExtractSparseAltDenseLayout() +{ + namespace vdbmath = openvdb::math; + + FloatRule rule(0.5f); + // Create a dense grid with the alternate data layout + // but the same domain as mDense + openvdb::tools::Dense dense(mDense->bbox(), 0.f); + + const float testvalue = 1.f; + dense.setValue(mijk, testvalue); + + const float background(0.f); + openvdb::FloatTree::Ptr result = openvdb::tools::extractSparseTree(dense, rule, background); + + // The result should have only one active value. + + CPPUNIT_ASSERT(result->activeVoxelCount() == 1); + + // The result should have only one leaf + + CPPUNIT_ASSERT(result->leafCount() == 1); + + // The background + + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, result->background(), 1.e-6); + + // The stored value + + CPPUNIT_ASSERT_DOUBLES_EQUAL(testvalue, result->getValue(mijk), 1.e-6); +} + + +void +TestDenseSparseTools::testExtractSparseMaskedTree() +{ + namespace vdbmath = openvdb::math; + + const float testvalue = 1.f; + mDense->setValue(mijk, testvalue); + + // Create a mask with two values. One in the domain of + // interest and one outside. The intersection of the active + // state topology of the mask and the domain of interest will define + // the topology of the extracted result. + + openvdb::FloatTree mask(0.f); + + // turn on a point inside the bouding domain of the dense grid + mask.setValue(mijk, 5.f); + + // turn on a point outside the bounding domain of the dense grid + vdbmath::Coord outsidePoint = mDense->bbox().min() - vdbmath::Coord(3, 3, 3); + mask.setValue(outsidePoint, 1.f); + + float background = 10.f; + + openvdb::FloatTree::Ptr result + = openvdb::tools::extractSparseTreeWithMask(*mDense, mask, background); + + // The result should have only one active value. + + CPPUNIT_ASSERT(result->activeVoxelCount() == 1); + + // The result should have only one leaf + + CPPUNIT_ASSERT(result->leafCount() == 1); + + // The background + + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, result->background(), 1.e-6); + + // The stored value + + CPPUNIT_ASSERT_DOUBLES_EQUAL(testvalue, result->getValue(mijk), 1.e-6); +} + + +void +TestDenseSparseTools::testDenseTransform() +{ + + namespace vdbmath = openvdb::math; + + vdbmath::CoordBBox domain(vdbmath::Coord(-4, -6, 10), + vdbmath::Coord( 1, 2, 15)); + + // Create dense grid, filled with value + const float value(2.f); const float valueSqr(value*value); + + openvdb::tools::Dense dense(domain, 0.f); + dense.fill(value); + + SqrOp op; + + vdbmath::CoordBBox smallBBox(vdbmath::Coord(-5, -5, 11), + vdbmath::Coord( 0, 1, 13) ); + + // Apply the transformation + openvdb::tools::transformDense(dense, smallBBox, op, true); + + vdbmath::Coord ijk; + // Test results. + for (ijk[0] = domain.min().x(); ijk[0] < domain.max().x() + 1; ++ijk[0]) { + for (ijk[1] = domain.min().y(); ijk[1] < domain.max().y() + 1; ++ijk[1]) { + for (ijk[2] = domain.min().z(); ijk[2] < domain.max().z() + 1; ++ijk[2]) { + + if (smallBBox.isInside(ijk)) { + // the functor was applied here + // the value should be base * base + CPPUNIT_ASSERT_DOUBLES_EQUAL(dense.getValue(ijk), valueSqr, 1.e-6); + } else { + // the original value + CPPUNIT_ASSERT_DOUBLES_EQUAL(dense.getValue(ijk), value, 1.e-6); + } + } + } + } +} + + +void +TestDenseSparseTools::testOver() +{ + namespace vdbmath = openvdb::math; + + const vdbmath::CoordBBox domain(vdbmath::Coord(-10, 0, 5), vdbmath::Coord( 10, 5, 10)); + const openvdb::Coord ijk = domain.min() + openvdb::Coord(1, 1, 1); + // Create dense grid, filled with value + const float value(2.f); + const float strength(1.f); + const float beta(1.f); + + openvdb::FloatTree src(0.f); + src.setValue(ijk, 1.f); + openvdb::FloatTree alpha(0.f); + alpha.setValue(ijk, 1.f); + + + const float expected = openvdb::tools::ds::OpOver::apply( + value, alpha.getValue(ijk), src.getValue(ijk), strength, beta, 1.f); + + { // testing composite function + openvdb::tools::Dense dense(domain, 0.f); + dense.fill(value); + + openvdb::tools::compositeToDense( + dense, src, alpha, beta, strength, true /*threaded*/); + + // Check for over value + CPPUNIT_ASSERT_DOUBLES_EQUAL(dense.getValue(ijk), expected, 1.e-6); + // Check for original value + CPPUNIT_ASSERT_DOUBLES_EQUAL(dense.getValue(openvdb::Coord(1,1,1) + ijk), value, 1.e-6); + } + + { // testing sparse explict sparse composite + openvdb::tools::Dense dense(domain, 0.f); + dense.fill(value); + + typedef openvdb::tools::ds::CompositeFunctorTranslator + CompositeTool; + typedef CompositeTool::OpT Method; + openvdb::tools::SparseToDenseCompositor + sparseToDense(dense, src, alpha, beta, strength); + + sparseToDense.sparseComposite(true); + // Check for over value + CPPUNIT_ASSERT_DOUBLES_EQUAL(dense.getValue(ijk), expected, 1.e-6); + // Check for original value + CPPUNIT_ASSERT_DOUBLES_EQUAL(dense.getValue(openvdb::Coord(1,1,1) + ijk), value, 1.e-6); + } + + { // testing sparse explict dense composite + openvdb::tools::Dense dense(domain, 0.f); + dense.fill(value); + + typedef openvdb::tools::ds::CompositeFunctorTranslator + CompositeTool; + typedef CompositeTool::OpT Method; + openvdb::tools::SparseToDenseCompositor + sparseToDense(dense, src, alpha, beta, strength); + + sparseToDense.denseComposite(true); + // Check for over value + CPPUNIT_ASSERT_DOUBLES_EQUAL(dense.getValue(ijk), expected, 1.e-6); + // Check for original value + CPPUNIT_ASSERT_DOUBLES_EQUAL(dense.getValue(openvdb::Coord(1,1,1) + ijk), value, 1.e-6); + } +} diff --git a/openvdb/unittest/TestDiagnostics.cc b/openvdb/unittest/TestDiagnostics.cc new file mode 100644 index 00000000..774711c8 --- /dev/null +++ b/openvdb/unittest/TestDiagnostics.cc @@ -0,0 +1,384 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class TestDiagnostics: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestDiagnostics); + CPPUNIT_TEST(testCheck); + CPPUNIT_TEST(testDiagnose); + CPPUNIT_TEST(testCheckLevelSet); + CPPUNIT_TEST(testCheckFogVolume); + CPPUNIT_TEST(testUniqueInactiveValues); + CPPUNIT_TEST_SUITE_END(); + + void testCheck(); + void testDiagnose(); + void testCheckLevelSet(); + void testCheckFogVolume(); + void testUniqueInactiveValues(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestDiagnostics); + + +//////////////////////////////////////// + +void +TestDiagnostics::testCheck() +{ + const float val = 1.0f; + const float nan = std::numeric_limits::quiet_NaN(); + const float inf1= std::numeric_limits::infinity(); + const openvdb::math::Vec3 inf2(val, inf1, val); + + {//test CheckNan + openvdb::tools::CheckNan c; + CPPUNIT_ASSERT(!c(val)); + CPPUNIT_ASSERT( c(nan)); + CPPUNIT_ASSERT( c(nan)); + CPPUNIT_ASSERT(!c(inf1)); + CPPUNIT_ASSERT(!c(inf2)); + } + {//test CheckInf + openvdb::tools::CheckInf c; + CPPUNIT_ASSERT(!c(val)); + CPPUNIT_ASSERT(!c(nan)); + CPPUNIT_ASSERT(!c(nan)); + CPPUNIT_ASSERT( c(inf1)); + CPPUNIT_ASSERT( c(inf2)); + } + {//test CheckFinite + openvdb::tools::CheckFinite c; + CPPUNIT_ASSERT(!c(val)); + CPPUNIT_ASSERT( c(nan)); + CPPUNIT_ASSERT( c(nan)); + CPPUNIT_ASSERT( c(inf1)); + CPPUNIT_ASSERT( c(inf2)); + } + {//test CheckMin + openvdb::tools::CheckMin c(0.0f); + CPPUNIT_ASSERT(!c( 0.5f)); + CPPUNIT_ASSERT(!c( 0.0f)); + CPPUNIT_ASSERT(!c( 1.0f)); + CPPUNIT_ASSERT(!c( 1.1f)); + CPPUNIT_ASSERT( c(-0.1f)); + } + {//test CheckMax + openvdb::tools::CheckMax c(0.0f); + CPPUNIT_ASSERT( c( 0.5f)); + CPPUNIT_ASSERT(!c( 0.0f)); + CPPUNIT_ASSERT( c( 1.0f)); + CPPUNIT_ASSERT( c( 1.1f)); + CPPUNIT_ASSERT(!c(-0.1f)); + } + {//test CheckRange + // first check throw on construction from an invalid range + CPPUNIT_ASSERT_THROW(openvdb::tools::CheckRange c(1.0f, 0.0f), + openvdb::ValueError); + openvdb::tools::CheckRange c(0.0f, 1.0f); + CPPUNIT_ASSERT(!c(0.5f)); + CPPUNIT_ASSERT(!c(0.0f)); + CPPUNIT_ASSERT(!c(1.0f)); + CPPUNIT_ASSERT( c(1.1f)); + CPPUNIT_ASSERT(c(-0.1f)); + } +}//testCheck + +void +TestDiagnostics::testDiagnose() +{ + using namespace openvdb; + const float val = 1.0f; + const float nan = std::numeric_limits::quiet_NaN(); + const float inf = std::numeric_limits::infinity(); + + {//empty grid + FloatGrid grid; + tools::Diagnose d(grid); + tools::CheckNan c; + std::string str = d.check(c); + //std::cerr << "Empty grid:\n" << str; + CPPUNIT_ASSERT_EQUAL(std::string(), str); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {//non-empty grid + FloatGrid grid; + grid.tree().setValue(Coord(-1,3,6), val); + tools::Diagnose d(grid); + tools::CheckNan c; + std::string str = d.check(c); + //std::cerr << "Non-Empty grid:\n" << str; + CPPUNIT_ASSERT_EQUAL(std::string(), str); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {//nan grid + FloatGrid grid; + grid.tree().setValue(Coord(-1,3,6), nan); + tools::Diagnose d(grid); + tools::CheckNan c; + std::string str = d.check(c); + //std::cerr << "NaN grid:\n" << str; + CPPUNIT_ASSERT(!str.empty()); + CPPUNIT_ASSERT_EQUAL(1, int(d.failureCount())); + } + + {//nan and infinite grid + FloatGrid grid; + grid.tree().setValue(Coord(-1,3,6), nan); + grid.tree().setValue(Coord(10,30,60), inf); + tools::Diagnose d(grid); + tools::CheckFinite c; + std::string str = d.check(c); + //std::cerr << "Not Finite grid:\n" << str; + CPPUNIT_ASSERT(!str.empty()); + CPPUNIT_ASSERT_EQUAL(2, int(d.failureCount())); + } + {//out-of-range grid + FloatGrid grid(10.0f); + grid.tree().setValue(Coord(-1,3,6), 1.0f); + grid.tree().setValue(Coord(10,30,60), 1.5); + grid.tree().fill(math::CoordBBox::createCube(math::Coord(0),8), 20.0f, true); + tools::Diagnose d(grid); + tools::CheckRange c(0.0f, 1.0f); + std::string str = d.check(c); + //std::cerr << "out-of-range grid:\n" << str; + CPPUNIT_ASSERT(!str.empty()); + CPPUNIT_ASSERT_EQUAL(3, int(d.failureCount())); + } + + const float radius = 4.3f; + const openvdb::Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.1f, width = 2.0f, gamma=voxelSize*width; + + FloatGrid::Ptr gridSphere = + tools::createLevelSetSphere(radius, center, voxelSize, width); + + //gridSphere->print(std::cerr, 2); + + {// Check min/max of active values + math::Extrema ex = tools::extrema(gridSphere->cbeginValueOn()); + //std::cerr << "Min = " << ex.min() << " max = " << ex.max() << std::endl; + CPPUNIT_ASSERT(ex.min() > -voxelSize*width); + CPPUNIT_ASSERT(ex.max() < voxelSize*width); + + } + {// Check min/max of all values + math::Extrema ex = tools::extrema(gridSphere->cbeginValueAll()); + //std::cerr << "Min = " << ex.min() << " max = " << ex.max() << std::endl; + CPPUNIT_ASSERT(ex.min() >= -voxelSize*width); + CPPUNIT_ASSERT(ex.max() <= voxelSize*width); + + } + {// check range of all values in a sphere w/o mask + tools::CheckRange c(-gamma, gamma); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c); + //std::cerr << "Values out of range:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {// check range of on values in a sphere w/o mask + tools::CheckRange c(-gamma, gamma); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c); + //std::cerr << "Values out of range:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {// check range of off tiles in a sphere w/o mask + tools::CheckRange c(-gamma, gamma); + tools::Diagnose d(*gridSphere); + {// check off tile iterator + FloatGrid::ValueOffCIter i(gridSphere->tree()); + i.setMaxDepth(FloatGrid::ValueOffCIter::LEAF_DEPTH - 1); + for (; i; ++i) CPPUNIT_ASSERT( math::Abs(*i) <= gamma); + } + std::string str = d.check(c); + //std::cerr << "Values out of range:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {// check range of sphere w/o mask + tools::CheckRange c(0.0f, gamma); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c); + //std::cerr << "Values out of range:\n" << str; + CPPUNIT_ASSERT(!str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT(d.failureCount() < gridSphere->activeVoxelCount()); + } + {// check range of sphere w mask + tools::CheckRange c(0.0f, gamma); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c, true); + //std::cerr << "Values out of range:\n" << str; + CPPUNIT_ASSERT(!str.empty()); + CPPUNIT_ASSERT_EQUAL(d.valueCount(), d.valueCount()); + CPPUNIT_ASSERT(d.failureCount() < gridSphere->activeVoxelCount()); + } + {// check min of sphere w/o mask + tools::CheckMin c(-gamma); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c); + //std::cerr << "Min values:\n" << str; + CPPUNIT_ASSERT_EQUAL(std::string(), str); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {// check max of sphere w/o mask + tools::CheckMax c(gamma); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c); + //std::cerr << "MAX values:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {// check norm of gradient of sphere w/o mask + tools::CheckEikonal c(*gridSphere, 0.97f, 1.03f); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c, false, true, false, false); + //std::cerr << "NormGrad:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {// check norm of gradient of sphere w/o mask + tools::CheckNormGrad c(*gridSphere, 0.75f, 1.25f); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c, false, true, false, false); + //std::cerr << "NormGrad:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {// check inactive values + tools::CheckMagnitude c(gamma); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c); + //std::cerr << "Magnitude:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } +}// testDiagnose + +void +TestDiagnostics::testCheckLevelSet() +{ + using namespace openvdb; + const float radius = 4.3f; + const Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.1f, width = LEVEL_SET_HALF_WIDTH; + + FloatGrid::Ptr grid = + tools::createLevelSetSphere(radius, center, voxelSize, width); + + //tools::CheckLevelSet c(*grid); + //std::string str = c.check(); + std::string str = tools::checkLevelSet(*grid); + CPPUNIT_ASSERT(str.empty()); + //std::cerr << "\n" << str << std::endl; + + grid->tree().setValue(Coord(0,0,0), voxelSize*(width+0.5f)); + //str = c.check(); + str = tools::checkLevelSet(*grid); + CPPUNIT_ASSERT(!str.empty()); + //std::cerr << "\n" << str << std::endl; + + //str = c.check(6); + str = tools::checkLevelSet(*grid, 6); + CPPUNIT_ASSERT(str.empty()); + +}// testCheckLevelSet + +void +TestDiagnostics::testCheckFogVolume() +{ + using namespace openvdb; + const float radius = 4.3f; + const Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.1f, width = LEVEL_SET_HALF_WIDTH; + + FloatGrid::Ptr grid = + tools::createLevelSetSphere(radius, center, voxelSize, width); + tools::sdfToFogVolume(*grid); + + //tools::CheckFogVolume c(*grid); + //std::string str = c.check(); + std::string str = tools::checkFogVolume(*grid); + CPPUNIT_ASSERT(str.empty()); + //std::cerr << "\n" << str << std::endl; + + grid->tree().setValue(Coord(0,0,0), 1.5f); + //str = c.check(); + str = tools::checkFogVolume(*grid); + CPPUNIT_ASSERT(!str.empty()); + //std::cerr << "\n" << str << std::endl; + + str = tools::checkFogVolume(*grid, 5); + //str = c.check(5); + CPPUNIT_ASSERT(str.empty()); + +}// testCheckFogVolume + +void +TestDiagnostics::testUniqueInactiveValues() +{ + openvdb::FloatGrid grid; + + grid.tree().setValueOff(openvdb::Coord(0,0,0), -1); + grid.tree().setValueOff(openvdb::Coord(0,0,1), -2); + grid.tree().setValueOff(openvdb::Coord(0,1,0), -3); + grid.tree().setValue(openvdb::Coord(1,0,0), 1); + + std::vector values; + + CPPUNIT_ASSERT(openvdb::tools::uniqueInactiveValues(grid, values, 4)); + + CPPUNIT_ASSERT_EQUAL(4, int(values.size())); + + CPPUNIT_ASSERT(openvdb::math::isApproxEqual(values[0], -3.0f)); + CPPUNIT_ASSERT(openvdb::math::isApproxEqual(values[1], -2.0f)); + CPPUNIT_ASSERT(openvdb::math::isApproxEqual(values[2], -1.0f)); + CPPUNIT_ASSERT(openvdb::math::isApproxEqual(values[3], 0.0f)); + + + // test with level set sphere + const float radius = 4.3f; + const openvdb::Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.5f, width = 2.0f; + + openvdb::FloatGrid::Ptr gridSphere = + openvdb::tools::createLevelSetSphere(radius, center, voxelSize, width); + + CPPUNIT_ASSERT(openvdb::tools::uniqueInactiveValues(*gridSphere.get(), values, 2)); + + CPPUNIT_ASSERT_EQUAL(2, int(values.size())); + CPPUNIT_ASSERT(openvdb::math::isApproxEqual(values[0], -voxelSize * width)); + CPPUNIT_ASSERT(openvdb::math::isApproxEqual(values[1], voxelSize * width)); + + // test with fog volume + openvdb::tools::sdfToFogVolume(*gridSphere); + + CPPUNIT_ASSERT(openvdb::tools::uniqueInactiveValues(*gridSphere.get(), values, 1)); + + CPPUNIT_ASSERT_EQUAL(1, int(values.size())); + CPPUNIT_ASSERT(openvdb::math::isApproxEqual(values[0], 0.0f)); +} diff --git a/openvdb/unittest/TestDivergence.cc b/openvdb/unittest/TestDivergence.cc new file mode 100644 index 00000000..bd4e60b3 --- /dev/null +++ b/openvdb/unittest/TestDivergence.cc @@ -0,0 +1,679 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + +namespace { +const int GRID_DIM = 10; +} + + +class TestDivergence: public CppUnit::TestFixture +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestDivergence); + CPPUNIT_TEST(testISDivergence); // Divergence in Index Space + CPPUNIT_TEST(testISDivergenceStencil); + CPPUNIT_TEST(testWSDivergence); // Divergence in World Space + CPPUNIT_TEST(testWSDivergenceStencil); + CPPUNIT_TEST(testDivergenceTool); // Divergence tool + CPPUNIT_TEST(testDivergenceMaskedTool); // Divergence tool + CPPUNIT_TEST(testStaggeredDivergence); + CPPUNIT_TEST_SUITE_END(); + + void testISDivergence(); + void testISDivergenceStencil(); + void testWSDivergence(); + void testWSDivergenceStencil(); + void testDivergenceTool(); + void testDivergenceMaskedTool(); + void testStaggeredDivergence(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestDivergence); + + +void +TestDivergence::testDivergenceTool() +{ + using namespace openvdb; + + VectorGrid::Ptr inGrid = VectorGrid::create(); + VectorTree& inTree = inGrid->tree(); + CPPUNIT_ASSERT(inTree.empty()); + + int dim = GRID_DIM; + for (int x = -dim; xactiveVoxelCount())); + + FloatGrid::ConstAccessor accessor = divGrid->getConstAccessor(); + --dim;//ignore boundary divergence + for (int x = -dim; xtree(); + CPPUNIT_ASSERT(inTree.empty()); + + int dim = GRID_DIM; + for (int x = -dim; xfill(maskBBox, true /*value*/, true /*activate*/); + + FloatGrid::Ptr divGrid = tools::divergence(*inGrid, *maskGrid); + CPPUNIT_ASSERT_EQUAL(math::Pow3(dim), int(divGrid->activeVoxelCount())); + + FloatGrid::ConstAccessor accessor = divGrid->getConstAccessor(); + --dim;//ignore boundary divergence + for (int x = -dim; xsetGridClass( GRID_STAGGERED ); + VectorTree& inTree = inGrid->tree(); + CPPUNIT_ASSERT(inTree.empty()); + + int dim = GRID_DIM; + for (int x = -dim; xactiveVoxelCount())); + + FloatGrid::ConstAccessor accessor = divGrid->getConstAccessor(); + --dim;//ignore boundary divergence + for (int x = -dim; xtree(); + CPPUNIT_ASSERT(inTree.empty()); + + int dim = GRID_DIM; + for (int x = -dim; xgetConstAccessor(); + CPPUNIT_ASSERT(!inTree.empty()); + CPPUNIT_ASSERT_EQUAL(math::Pow3(2*dim), int(inTree.activeVoxelCount())); + + --dim;//ignore boundary divergence + // test index space divergence + for (int x = -dim; x::result(inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + + d = math::ISDivergence::result(inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + + d = math::ISDivergence::result(inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + } + } + } + + --dim;//ignore boundary divergence + // test index space divergence + for (int x = -dim; x::result(inAccessor, xyz); + + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + d = math::ISDivergence::result(inAccessor, xyz); + + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + d = math::ISDivergence::result(inAccessor, xyz); + + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + } + } + } + + --dim;//ignore boundary divergence + // test index space divergence + for (int x = -dim; x::result(inAccessor, xyz); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + + d = math::ISDivergence::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2, d, /*tolerance=*/0.00001); + + d = math::ISDivergence::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2, d, /*tolerance=*/0.00001); + } + } + } +} + + +void +TestDivergence::testISDivergenceStencil() +{ + using namespace openvdb; + + VectorGrid::Ptr inGrid = VectorGrid::create(); + VectorTree& inTree = inGrid->tree(); + CPPUNIT_ASSERT(inTree.empty()); + + int dim = GRID_DIM; + for (int x = -dim; x sevenpt(*inGrid); + math::ThirteenPointStencil thirteenpt(*inGrid); + math::NineteenPointStencil nineteenpt(*inGrid); + + --dim;//ignore boundary divergence + // test index space divergence + for (int x = -dim; x::result(sevenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + + d = math::ISDivergence::result(sevenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + + d = math::ISDivergence::result(sevenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + } + } + } + + --dim;//ignore boundary divergence + // test index space divergence + for (int x = -dim; x::result(thirteenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + + d = math::ISDivergence::result(thirteenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + + d = math::ISDivergence::result(thirteenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + } + } + } + + --dim;//ignore boundary divergence + // test index space divergence + for (int x = -dim; x::result(nineteenpt); + ASSERT_DOUBLES_EXACTLY_EQUAL(2, d); + + d = math::ISDivergence::result(nineteenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2, d, /*tolerance=*/0.00001); + + d = math::ISDivergence::result(nineteenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2, d, /*tolerance=*/0.00001); + } + } + } +} + + +void +TestDivergence::testWSDivergence() +{ + using namespace openvdb; + + typedef VectorGrid::ConstAccessor Accessor; + + { // non-unit voxel size + double voxel_size = 0.5; + VectorGrid::Ptr inGrid = VectorGrid::create(); + inGrid->setTransform(math::Transform::createLinearTransform(voxel_size)); + + VectorTree& inTree = inGrid->tree(); + CPPUNIT_ASSERT(inTree.empty()); + + int dim = GRID_DIM; + for (int x = -dim; xindexToWorld(Vec3d(x,y,z)); + inTree.setValue(Coord(x,y,z), + VectorTree::ValueType(float(location.x()), float(location.y()), 0.f)); + } + } + } + + Accessor inAccessor = inGrid->getConstAccessor(); + CPPUNIT_ASSERT(!inTree.empty()); + CPPUNIT_ASSERT_EQUAL(math::Pow3(2*dim), int(inTree.activeVoxelCount())); + + --dim;//ignore boundary divergence + + // test with a map + // test with a map + math::AffineMap map(voxel_size*math::Mat3d::identity()); + math::UniformScaleMap uniform_map(voxel_size); + math::UniformScaleTranslateMap uniform_translate_map(voxel_size, Vec3d(0,0,0)); + + for (int x = -dim; xtypeName().compare("double") == 0); + + DoubleMetadata *s = dynamic_cast(m.get()); + //CPPUNIT_ASSERT(s->value() == 1.23); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.23,s->value(),0); + s->value() = 4.56; + //CPPUNIT_ASSERT(s->value() == 4.56); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.56,s->value(),0); + + m2->copy(*s); + + s = dynamic_cast(m2.get()); + //CPPUNIT_ASSERT(s->value() == 4.56); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.56,s->value(),0); +} diff --git a/openvdb/unittest/TestExceptions.cc b/openvdb/unittest/TestExceptions.cc new file mode 100644 index 00000000..854bf6ff --- /dev/null +++ b/openvdb/unittest/TestExceptions.cc @@ -0,0 +1,80 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + + +class TestExceptions : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestExceptions); + CPPUNIT_TEST(testArithmeticError); + CPPUNIT_TEST(testIndexError); + CPPUNIT_TEST(testIoError); + CPPUNIT_TEST(testKeyError); + CPPUNIT_TEST(testLookupError); + CPPUNIT_TEST(testNotImplementedError); + CPPUNIT_TEST(testReferenceError); + CPPUNIT_TEST(testRuntimeError); + CPPUNIT_TEST(testTypeError); + CPPUNIT_TEST(testValueError); + CPPUNIT_TEST_SUITE_END(); + + void testArithmeticError() { testException(); } + void testIndexError() { testException(); } + void testIoError() { testException(); } + void testKeyError() { testException(); } + void testLookupError() { testException(); } + void testNotImplementedError() { testException(); } + void testReferenceError() { testException(); } + void testRuntimeError() { testException(); } + void testTypeError() { testException(); } + void testValueError() { testException(); } + +private: + template void testException(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestExceptions); + + +template struct ExceptionTraits +{ static std::string name() { return ""; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "ArithmeticError"; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "IndexError"; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "IoError"; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "KeyError"; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "LookupError"; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "NotImplementedError"; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "ReferenceError"; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "RuntimeError"; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "TypeError"; } }; +template<> struct ExceptionTraits +{ static std::string name() { return "ValueError"; } }; + + +template +void +TestExceptions::testException() +{ + std::string ErrorMsg("Error message"); + + CPPUNIT_ASSERT_THROW(OPENVDB_THROW(ExceptionT, ErrorMsg), ExceptionT); + + try { + OPENVDB_THROW(ExceptionT, ErrorMsg); + } catch (openvdb::Exception& e) { + const std::string expectedMsg = ExceptionTraits::name() + ": " + ErrorMsg; + CPPUNIT_ASSERT_EQUAL(expectedMsg, std::string(e.what())); + } +} diff --git a/openvdb/unittest/TestFile.cc b/openvdb/unittest/TestFile.cc new file mode 100644 index 00000000..b18c1161 --- /dev/null +++ b/openvdb/unittest/TestFile.cc @@ -0,0 +1,2729 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include // for tools::sdfToFogVolume() +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() +#include +#include // for tbb::this_tbb_thread::sleep() +#include // for std::sort() +#include // for remove() and rename() +#include +#include // for std::bind() +#include +#include +#include +#include +#include +#include +#include +#include // for stat() +#include +#ifndef _WIN32 +#include +#endif +#ifdef OPENVDB_USE_BLOSC +#include +#include // for memset() +#endif + + +class TestFile: public CppUnit::TestCase +{ +public: + void setUp() override {} + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestFile); + CPPUNIT_TEST(testHeader); + CPPUNIT_TEST(testWriteGrid); + CPPUNIT_TEST(testWriteMultipleGrids); + CPPUNIT_TEST(testWriteFloatAsHalf); + CPPUNIT_TEST(testWriteInstancedGrids); + CPPUNIT_TEST(testReadGridDescriptors); + CPPUNIT_TEST(testGridNaming); + CPPUNIT_TEST(testEmptyFile); + CPPUNIT_TEST(testEmptyGridIO); + CPPUNIT_TEST(testOpen); + CPPUNIT_TEST(testNonVdbOpen); + CPPUNIT_TEST(testGetMetadata); + CPPUNIT_TEST(testReadAll); + CPPUNIT_TEST(testWriteOpenFile); + CPPUNIT_TEST(testReadGridMetadata); + CPPUNIT_TEST(testReadGrid); + CPPUNIT_TEST(testReadClippedGrid); + CPPUNIT_TEST(testMultiPassIO); + CPPUNIT_TEST(testHasGrid); + CPPUNIT_TEST(testNameIterator); + CPPUNIT_TEST(testReadOldFileFormat); + CPPUNIT_TEST(testCompression); + CPPUNIT_TEST(testAsync); +#ifdef OPENVDB_USE_BLOSC + CPPUNIT_TEST(testBlosc); +#endif + CPPUNIT_TEST(testDelayedLoadMetadata); + CPPUNIT_TEST_SUITE_END(); + + void testHeader(); + void testWriteGrid(); + void testWriteMultipleGrids(); + void testWriteFloatAsHalf(); + void testWriteInstancedGrids(); + void testReadGridDescriptors(); + void testGridNaming(); + void testEmptyFile(); + void testEmptyGridIO(); + void testOpen(); + void testNonVdbOpen(); + void testGetMetadata(); + void testReadAll(); + void testWriteOpenFile(); + void testReadGridMetadata(); + void testReadGrid(); + void testReadClippedGrid(); + void testMultiPassIO(); + void testHasGrid(); + void testNameIterator(); + void testReadOldFileFormat(); + void testCompression(); + void testAsync(); +#ifdef OPENVDB_USE_BLOSC + void testBlosc(); +#endif + void testDelayedLoadMetadata(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestFile); + + +//////////////////////////////////////// + + +void +TestFile::testHeader() +{ + using namespace openvdb::io; + + File file("something.vdb2"); + + std::ostringstream + ostr(std::ios_base::binary), + ostr2(std::ios_base::binary); + + file.writeHeader(ostr2, /*seekable=*/true); + std::string uuidStr = file.getUniqueTag(); + + file.writeHeader(ostr, /*seekable=*/true); + // Verify that a file gets a new UUID each time it is written. + CPPUNIT_ASSERT(!file.isIdentical(uuidStr)); + uuidStr = file.getUniqueTag(); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + + bool unique=true; + CPPUNIT_ASSERT_NO_THROW(unique=file.readHeader(istr)); + + CPPUNIT_ASSERT(!unique);//reading same file again + + uint32_t version = openvdb::OPENVDB_FILE_VERSION; + + CPPUNIT_ASSERT_EQUAL(version, file.fileVersion()); + CPPUNIT_ASSERT_EQUAL(openvdb::OPENVDB_LIBRARY_MAJOR_VERSION, file.libraryVersion().first); + CPPUNIT_ASSERT_EQUAL(openvdb::OPENVDB_LIBRARY_MINOR_VERSION, file.libraryVersion().second); + CPPUNIT_ASSERT_EQUAL(uuidStr, file.getUniqueTag()); + + //std::cerr << "\nuuid=" << uuidStr << std::endl; + + CPPUNIT_ASSERT(file.isIdentical(uuidStr)); + + remove("something.vdb2"); +} + + +void +TestFile::testWriteGrid() +{ + using namespace openvdb; + using namespace openvdb::io; + + using TreeType = Int32Tree; + using GridType = Grid; + + logging::LevelScope suppressLogging{logging::Level::Fatal}; + + File file("something.vdb2"); + + std::ostringstream ostr(std::ios_base::binary); + + // Create a grid with transform. + math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1); + + GridType::Ptr grid = createGrid(/*bg=*/1); + TreeType& tree = grid->tree(); + grid->setTransform(trans); + tree.setValue(Coord(10, 1, 2), 10); + tree.setValue(Coord(0, 0, 0), 5); + + // Add some metadata. + Metadata::clearRegistry(); + StringMetadata::registerType(); + const std::string meta0Val, meta1Val("Hello, world."); + Metadata::Ptr stringMetadata = Metadata::createMetadata(typeNameAsString()); + CPPUNIT_ASSERT(stringMetadata); + if (stringMetadata) { + grid->insertMeta("meta0", *stringMetadata); + grid->metaValue("meta0") = meta0Val; + grid->insertMeta("meta1", *stringMetadata); + grid->metaValue("meta1") = meta1Val; + } + + // Create the grid descriptor out of this grid. + GridDescriptor gd(Name("temperature"), grid->type()); + + // Write out the grid. + file.writeGrid(gd, grid, ostr, /*seekable=*/true); + + CPPUNIT_ASSERT(gd.getGridPos() != 0); + CPPUNIT_ASSERT(gd.getBlockPos() != 0); + CPPUNIT_ASSERT(gd.getEndPos() != 0); + + // Read in the grid descriptor. + GridDescriptor gd2; + std::istringstream istr(ostr.str(), std::ios_base::binary); + + // Since the input is only a fragment of a VDB file (in particular, + // it doesn't have a header), set the file format version number explicitly. + io::setCurrentVersion(istr); + + GridBase::Ptr gd2_grid; + CPPUNIT_ASSERT_THROW(gd2.read(istr), openvdb::LookupError); + + // Register the grid and the transform and the blocks. + GridBase::clearRegistry(); + GridType::registerGrid(); + + // Register transform maps + math::MapRegistry::clear(); + math::AffineMap::registerMap(); + math::ScaleMap::registerMap(); + math::UniformScaleMap::registerMap(); + math::TranslationMap::registerMap(); + math::ScaleTranslateMap::registerMap(); + math::UniformScaleTranslateMap::registerMap(); + math::NonlinearFrustumMap::registerMap(); + + istr.seekg(0, std::ios_base::beg); + CPPUNIT_ASSERT_NO_THROW(gd2_grid = gd2.read(istr)); + + CPPUNIT_ASSERT_EQUAL(gd.gridName(), gd2.gridName()); + CPPUNIT_ASSERT_EQUAL(GridType::gridType(), gd2_grid->type()); + CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), gd2.getGridPos()); + CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), gd2.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), gd2.getEndPos()); + + // Position the stream to beginning of the grid storage and read the grid. + gd2.seekToGrid(istr); + Archive::readGridCompression(istr); + gd2_grid->readMeta(istr); + gd2_grid->readTransform(istr); + gd2_grid->readTopology(istr); + + // Remove delay load metadata if it exists. + if ((*gd2_grid)["file_delayed_load"]) { + gd2_grid->removeMeta("file_delayed_load"); + } + + // Ensure that we have the same metadata. + CPPUNIT_ASSERT_EQUAL(grid->metaCount(), gd2_grid->metaCount()); + CPPUNIT_ASSERT((*gd2_grid)["meta0"]); + CPPUNIT_ASSERT((*gd2_grid)["meta1"]); + CPPUNIT_ASSERT_EQUAL(meta0Val, gd2_grid->metaValue("meta0")); + CPPUNIT_ASSERT_EQUAL(meta1Val, gd2_grid->metaValue("meta1")); + + // Ensure that we have the same topology and transform. + CPPUNIT_ASSERT_EQUAL( + grid->baseTree().leafCount(), gd2_grid->baseTree().leafCount()); + CPPUNIT_ASSERT_EQUAL( + grid->baseTree().nonLeafCount(), gd2_grid->baseTree().nonLeafCount()); + CPPUNIT_ASSERT_EQUAL( + grid->baseTree().treeDepth(), gd2_grid->baseTree().treeDepth()); + + //CPPUNIT_ASSERT_EQUAL(0.1, gd2_grid->getTransform()->getVoxelSizeX()); + //CPPUNIT_ASSERT_EQUAL(0.1, gd2_grid->getTransform()->getVoxelSizeY()); + //CPPUNIT_ASSERT_EQUAL(0.1, gd2_grid->getTransform()->getVoxelSizeZ()); + + // Read in the data blocks. + gd2.seekToBlocks(istr); + gd2_grid->readBuffers(istr); + TreeType::Ptr tree2 = DynamicPtrCast(gd2_grid->baseTreePtr()); + CPPUNIT_ASSERT(tree2.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(10, tree2->getValue(Coord(10, 1, 2))); + CPPUNIT_ASSERT_EQUAL(5, tree2->getValue(Coord(0, 0, 0))); + + CPPUNIT_ASSERT_EQUAL(1, tree2->getValue(Coord(1000, 1000, 16000))); + // Clear registries. + GridBase::clearRegistry(); + Metadata::clearRegistry(); + math::MapRegistry::clear(); + + remove("something.vdb2"); +} + + +void +TestFile::testWriteMultipleGrids() +{ + using namespace openvdb; + using namespace openvdb::io; + + using TreeType = Int32Tree; + using GridType = Grid; + + logging::LevelScope suppressLogging{logging::Level::Fatal}; + + File file("something.vdb2"); + + std::ostringstream ostr(std::ios_base::binary); + + // Create a grid with transform. + GridType::Ptr grid = createGrid(/*bg=*/1); + TreeType& tree = grid->tree(); + tree.setValue(Coord(10, 1, 2), 10); + tree.setValue(Coord(0, 0, 0), 5); + math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1); + grid->setTransform(trans); + + GridType::Ptr grid2 = createGrid(/*bg=*/2); + TreeType& tree2 = grid2->tree(); + tree2.setValue(Coord(0, 0, 0), 10); + tree2.setValue(Coord(1000, 1000, 1000), 50); + math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.2); + grid2->setTransform(trans2); + + // Create the grid descriptor out of this grid. + GridDescriptor gd(Name("temperature"), grid->type()); + GridDescriptor gd2(Name("density"), grid2->type()); + + // Write out the grids. + file.writeGrid(gd, grid, ostr, /*seekable=*/true); + file.writeGrid(gd2, grid2, ostr, /*seekable=*/true); + + CPPUNIT_ASSERT(gd.getGridPos() != 0); + CPPUNIT_ASSERT(gd.getBlockPos() != 0); + CPPUNIT_ASSERT(gd.getEndPos() != 0); + + CPPUNIT_ASSERT(gd2.getGridPos() != 0); + CPPUNIT_ASSERT(gd2.getBlockPos() != 0); + CPPUNIT_ASSERT(gd2.getEndPos() != 0); + + // register the grid + GridBase::clearRegistry(); + GridType::registerGrid(); + + // register maps + math::MapRegistry::clear(); + math::AffineMap::registerMap(); + math::ScaleMap::registerMap(); + math::UniformScaleMap::registerMap(); + math::TranslationMap::registerMap(); + math::ScaleTranslateMap::registerMap(); + math::UniformScaleTranslateMap::registerMap(); + math::NonlinearFrustumMap::registerMap(); + + // Read in the first grid descriptor. + GridDescriptor gd_in; + std::istringstream istr(ostr.str(), std::ios_base::binary); + io::setCurrentVersion(istr); + + GridBase::Ptr gd_in_grid; + CPPUNIT_ASSERT_NO_THROW(gd_in_grid = gd_in.read(istr)); + + // Ensure read in the right values. + CPPUNIT_ASSERT_EQUAL(gd.gridName(), gd_in.gridName()); + CPPUNIT_ASSERT_EQUAL(GridType::gridType(), gd_in_grid->type()); + CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), gd_in.getGridPos()); + CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), gd_in.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), gd_in.getEndPos()); + + // Position the stream to beginning of the grid storage and read the grid. + gd_in.seekToGrid(istr); + Archive::readGridCompression(istr); + gd_in_grid->readMeta(istr); + gd_in_grid->readTransform(istr); + gd_in_grid->readTopology(istr); + + // Ensure that we have the same topology and transform. + CPPUNIT_ASSERT_EQUAL( + grid->baseTree().leafCount(), gd_in_grid->baseTree().leafCount()); + CPPUNIT_ASSERT_EQUAL( + grid->baseTree().nonLeafCount(), gd_in_grid->baseTree().nonLeafCount()); + CPPUNIT_ASSERT_EQUAL( + grid->baseTree().treeDepth(), gd_in_grid->baseTree().treeDepth()); + + // CPPUNIT_ASSERT_EQUAL(0.1, gd_in_grid->getTransform()->getVoxelSizeX()); + // CPPUNIT_ASSERT_EQUAL(0.1, gd_in_grid->getTransform()->getVoxelSizeY()); + // CPPUNIT_ASSERT_EQUAL(0.1, gd_in_grid->getTransform()->getVoxelSizeZ()); + + // Read in the data blocks. + gd_in.seekToBlocks(istr); + gd_in_grid->readBuffers(istr); + TreeType::Ptr grid_in = DynamicPtrCast(gd_in_grid->baseTreePtr()); + CPPUNIT_ASSERT(grid_in.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(10, grid_in->getValue(Coord(10, 1, 2))); + CPPUNIT_ASSERT_EQUAL(5, grid_in->getValue(Coord(0, 0, 0))); + CPPUNIT_ASSERT_EQUAL(1, grid_in->getValue(Coord(1000, 1000, 16000))); + + ///////////////////////////////////////////////////////////////// + // Now read in the second grid descriptor. Make use of hte end offset. + /////////////////////////////////////////////////////////////// + + gd_in.seekToEnd(istr); + + GridDescriptor gd2_in; + GridBase::Ptr gd2_in_grid; + CPPUNIT_ASSERT_NO_THROW(gd2_in_grid = gd2_in.read(istr)); + + // Ensure that we read in the right values. + CPPUNIT_ASSERT_EQUAL(gd2.gridName(), gd2_in.gridName()); + CPPUNIT_ASSERT_EQUAL(TreeType::treeType(), gd2_in_grid->type()); + CPPUNIT_ASSERT_EQUAL(gd2.getGridPos(), gd2_in.getGridPos()); + CPPUNIT_ASSERT_EQUAL(gd2.getBlockPos(), gd2_in.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd2.getEndPos(), gd2_in.getEndPos()); + + // Position the stream to beginning of the grid storage and read the grid. + gd2_in.seekToGrid(istr); + Archive::readGridCompression(istr); + gd2_in_grid->readMeta(istr); + gd2_in_grid->readTransform(istr); + gd2_in_grid->readTopology(istr); + + // Ensure that we have the same topology and transform. + CPPUNIT_ASSERT_EQUAL( + grid2->baseTree().leafCount(), gd2_in_grid->baseTree().leafCount()); + CPPUNIT_ASSERT_EQUAL( + grid2->baseTree().nonLeafCount(), gd2_in_grid->baseTree().nonLeafCount()); + CPPUNIT_ASSERT_EQUAL( + grid2->baseTree().treeDepth(), gd2_in_grid->baseTree().treeDepth()); + // CPPUNIT_ASSERT_EQUAL(0.2, gd2_in_grid->getTransform()->getVoxelSizeX()); + // CPPUNIT_ASSERT_EQUAL(0.2, gd2_in_grid->getTransform()->getVoxelSizeY()); + // CPPUNIT_ASSERT_EQUAL(0.2, gd2_in_grid->getTransform()->getVoxelSizeZ()); + + // Read in the data blocks. + gd2_in.seekToBlocks(istr); + gd2_in_grid->readBuffers(istr); + TreeType::Ptr grid2_in = DynamicPtrCast(gd2_in_grid->baseTreePtr()); + CPPUNIT_ASSERT(grid2_in.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(50, grid2_in->getValue(Coord(1000, 1000, 1000))); + CPPUNIT_ASSERT_EQUAL(10, grid2_in->getValue(Coord(0, 0, 0))); + CPPUNIT_ASSERT_EQUAL(2, grid2_in->getValue(Coord(100000, 100000, 16000))); + + // Clear registries. + GridBase::clearRegistry(); + + math::MapRegistry::clear(); + remove("something.vdb2"); +} + + +void +TestFile::testWriteFloatAsHalf() +{ + using namespace openvdb; + using namespace openvdb::io; + + using TreeType = Vec3STree; + using GridType = Grid; + + // Register all grid types. + initialize(); + // Ensure that the registry is cleared on exit. + struct Local { static void uninitialize(char*) { openvdb::uninitialize(); } }; + SharedPtr onExit(nullptr, Local::uninitialize); + + // Create two test grids. + GridType::Ptr grid1 = createGrid(/*bg=*/Vec3s(1, 1, 1)); + TreeType& tree1 = grid1->tree(); + CPPUNIT_ASSERT(grid1.get() != nullptr); + grid1->setTransform(math::Transform::createLinearTransform(0.1)); + grid1->setName("grid1"); + + GridType::Ptr grid2 = createGrid(/*bg=*/Vec3s(2, 2, 2)); + CPPUNIT_ASSERT(grid2.get() != nullptr); + TreeType& tree2 = grid2->tree(); + grid2->setTransform(math::Transform::createLinearTransform(0.2)); + // Flag this grid for 16-bit float output. + grid2->setSaveFloatAsHalf(true); + grid2->setName("grid2"); + + for (int x = 0; x < 40; ++x) { + for (int y = 0; y < 40; ++y) { + for (int z = 0; z < 40; ++z) { + tree1.setValue(Coord(x, y, z), Vec3s(float(x), float(y), float(z))); + tree2.setValue(Coord(x, y, z), Vec3s(float(x), float(y), float(z))); + } + } + } + + GridPtrVec grids; + grids.push_back(grid1); + grids.push_back(grid2); + + const char* filename = "something.vdb2"; + { + // Write both grids to a file. + File vdbFile(filename); + vdbFile.write(grids); + } + { + // Verify that both grids can be read back successfully from the file. + File vdbFile(filename); + vdbFile.open(); + GridBase::Ptr + bgrid1 = vdbFile.readGrid("grid1"), + bgrid2 = vdbFile.readGrid("grid2"); + vdbFile.close(); + + CPPUNIT_ASSERT(bgrid1.get() != nullptr); + CPPUNIT_ASSERT(bgrid1->isType()); + CPPUNIT_ASSERT(bgrid2.get() != nullptr); + CPPUNIT_ASSERT(bgrid2->isType()); + + const TreeType& btree1 = StaticPtrCast(bgrid1)->tree(); + CPPUNIT_ASSERT_EQUAL(Vec3s(10, 10, 10), btree1.getValue(Coord(10, 10, 10))); + const TreeType& btree2 = StaticPtrCast(bgrid2)->tree(); + CPPUNIT_ASSERT_EQUAL(Vec3s(10, 10, 10), btree2.getValue(Coord(10, 10, 10))); + } +} + + +void +TestFile::testWriteInstancedGrids() +{ + using namespace openvdb; + + // Register data types. + openvdb::initialize(); + + // Remove something.vdb2 when done. We must declare this here before the + // other grid smart_ptr's because we re-use them in the test several times. + // We will not be able to remove something.vdb2 on Windows if the pointers + // are still referencing data opened by the "file" variable. + const char* filename = "something.vdb2"; + SharedPtr scopedFile(filename, ::remove); + + // Create grids. + Int32Tree::Ptr tree1(new Int32Tree(1)); + FloatTree::Ptr tree2(new FloatTree(2.0)); + GridBase::Ptr + grid1 = createGrid(tree1), + grid2 = createGrid(tree1), // instance of grid1 + grid3 = createGrid(tree2), + grid4 = createGrid(tree2); // instance of grid3 + grid1->setName("density"); + grid2->setName("density_copy"); + // Leave grid3 and grid4 unnamed. + + // Create transforms. + math::Transform::Ptr trans1 = math::Transform::createLinearTransform(0.1); + math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1); + grid1->setTransform(trans1); + grid2->setTransform(trans2); + grid3->setTransform(trans2); + grid4->setTransform(trans1); + + // Set some values. + tree1->setValue(Coord(0, 0, 0), 5); + tree1->setValue(Coord(100, 0, 0), 6); + tree2->setValue(Coord(0, 0, 0), 10); + tree2->setValue(Coord(0, 100, 0), 11); + + MetaMap::Ptr meta(new MetaMap); + meta->insertMeta("author", StringMetadata("Einstein")); + meta->insertMeta("year", Int32Metadata(2009)); + + GridPtrVecPtr grids(new GridPtrVec); + grids->push_back(grid1); + grids->push_back(grid2); + grids->push_back(grid3); + grids->push_back(grid4); + + // Write the grids to a file and then close the file. + { + io::File vdbFile(filename); + vdbFile.write(*grids, *meta); + } + meta.reset(); + + // Read the grids back in. + io::File file(filename); + file.open(); + grids = file.getGrids(); + meta = file.getMetadata(); + + // Verify the metadata. + CPPUNIT_ASSERT(meta.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(2, int(meta->metaCount())); + CPPUNIT_ASSERT_EQUAL(std::string("Einstein"), meta->metaValue("author")); + CPPUNIT_ASSERT_EQUAL(2009, meta->metaValue("year")); + + // Verify the grids. + CPPUNIT_ASSERT(grids.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(4, int(grids->size())); + + GridBase::Ptr grid = findGridByName(*grids, "density"); + CPPUNIT_ASSERT(grid.get() != nullptr); + Int32Tree::Ptr density = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(density.get() != nullptr); + + grid.reset(); + grid = findGridByName(*grids, "density_copy"); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr().get() != nullptr); + // Verify that "density_copy" is an instance of (i.e., shares a tree with) "density". + CPPUNIT_ASSERT_EQUAL(density, gridPtrCast(grid)->treePtr()); + + grid.reset(); + grid = findGridByName(*grids, ""); + CPPUNIT_ASSERT(grid.get() != nullptr); + FloatTree::Ptr temperature = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(temperature.get() != nullptr); + + grid.reset(); + for (GridPtrVec::reverse_iterator it = grids->rbegin(); !grid && it != grids->rend(); ++it) { + // Search for the second unnamed grid starting from the end of the list. + if ((*it)->getName() == "") grid = *it; + } + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr().get() != nullptr); + // Verify that the second unnamed grid is an instance of the first. + CPPUNIT_ASSERT_EQUAL(temperature, gridPtrCast(grid)->treePtr()); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(5, density->getValue(Coord(0, 0, 0)), /*tolerance=*/0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6, density->getValue(Coord(100, 0, 0)), /*tolerance=*/0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10, temperature->getValue(Coord(0, 0, 0)), /*tolerance=*/0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(11, temperature->getValue(Coord(0, 100, 0)), /*tolerance=*/0); + + // Reread with instancing disabled. + file.close(); + file.setInstancingEnabled(false); + file.open(); + grids = file.getGrids(); + CPPUNIT_ASSERT_EQUAL(4, int(grids->size())); + + grid = findGridByName(*grids, "density"); + CPPUNIT_ASSERT(grid.get() != nullptr); + density = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(density.get() != nullptr); + grid = findGridByName(*grids, "density_copy"); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr().get() != nullptr); + // Verify that "density_copy" is *not* an instance of "density". + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr() != density); + + // Verify that the two unnamed grids are not instances of each other. + grid = findGridByName(*grids, ""); + CPPUNIT_ASSERT(grid.get() != nullptr); + temperature = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(temperature.get() != nullptr); + grid.reset(); + for (GridPtrVec::reverse_iterator it = grids->rbegin(); !grid && it != grids->rend(); ++it) { + // Search for the second unnamed grid starting from the end of the list. + if ((*it)->getName() == "") grid = *it; + } + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr().get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr() != temperature); + + // Rewrite with instancing disabled, then reread with instancing enabled. + file.close(); + { + /// @todo (FX-7063) For now, write to a new file, then, when there's + /// no longer a need for delayed load from the old file, replace it + /// with the new file. + const char* tempFilename = "somethingelse.vdb"; + SharedPtr scopedTempFile(tempFilename, ::remove); + io::File vdbFile(tempFilename); + vdbFile.setInstancingEnabled(false); + vdbFile.write(*grids, *meta); + grids.reset(); + // Note: Windows requires that the destination not exist, before we can rename to it. + std::remove(filename); + std::rename(tempFilename, filename); + } + file.setInstancingEnabled(true); + file.open(); + grids = file.getGrids(); + CPPUNIT_ASSERT_EQUAL(4, int(grids->size())); + + // Verify that "density_copy" is not an instance of "density". + grid = findGridByName(*grids, "density"); + CPPUNIT_ASSERT(grid.get() != nullptr); + density = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(density.get() != nullptr); +#if OPENVDB_ABI_VERSION_NUMBER >= 4 + CPPUNIT_ASSERT(density->unallocatedLeafCount() > 0); + CPPUNIT_ASSERT_EQUAL(density->leafCount(), density->unallocatedLeafCount()); +#endif + grid = findGridByName(*grids, "density_copy"); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr().get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr() != density); + + // Verify that the two unnamed grids are not instances of each other. + grid = findGridByName(*grids, ""); + CPPUNIT_ASSERT(grid.get() != nullptr); + temperature = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(temperature.get() != nullptr); + grid.reset(); + for (GridPtrVec::reverse_iterator it = grids->rbegin(); !grid && it != grids->rend(); ++it) { + // Search for the second unnamed grid starting from the end of the list. + if ((*it)->getName() == "") grid = *it; + } + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr().get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr() != temperature); +} + + +void +TestFile::testReadGridDescriptors() +{ + using namespace openvdb; + using namespace openvdb::io; + + using GridType = Int32Grid; + using TreeType = GridType::TreeType; + + File file("something.vdb2"); + + std::ostringstream ostr(std::ios_base::binary); + + // Create a grid with transform. + GridType::Ptr grid = createGrid(1); + TreeType& tree = grid->tree(); + tree.setValue(Coord(10, 1, 2), 10); + tree.setValue(Coord(0, 0, 0), 5); + math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1); + grid->setTransform(trans); + + // Create another grid with transform. + GridType::Ptr grid2 = createGrid(2); + TreeType& tree2 = grid2->tree(); + tree2.setValue(Coord(0, 0, 0), 10); + tree2.setValue(Coord(1000, 1000, 1000), 50); + math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.2); + grid2->setTransform(trans2); + + // Create the grid descriptor out of this grid. + GridDescriptor gd(Name("temperature"), grid->type()); + GridDescriptor gd2(Name("density"), grid2->type()); + + // Write out the number of grids. + int32_t gridCount = 2; + ostr.write(reinterpret_cast(&gridCount), sizeof(int32_t)); + // Write out the grids. + file.writeGrid(gd, grid, ostr, /*seekable=*/true); + file.writeGrid(gd2, grid2, ostr, /*seekable=*/true); + + // Register the grid and the transform and the blocks. + GridBase::clearRegistry(); + GridType::registerGrid(); + // register maps + math::MapRegistry::clear(); + math::AffineMap::registerMap(); + math::ScaleMap::registerMap(); + math::UniformScaleMap::registerMap(); + math::TranslationMap::registerMap(); + math::ScaleTranslateMap::registerMap(); + math::UniformScaleTranslateMap::registerMap(); + math::NonlinearFrustumMap::registerMap(); + + // Read in the grid descriptors. + File file2("something.vdb2"); + std::istringstream istr(ostr.str(), std::ios_base::binary); + io::setCurrentVersion(istr); + file2.readGridDescriptors(istr); + + // Compare with the initial grid descriptors. + File::NameMapCIter it = file2.findDescriptor("temperature"); + CPPUNIT_ASSERT(it != file2.gridDescriptors().end()); + GridDescriptor file2gd = it->second; + CPPUNIT_ASSERT_EQUAL(gd.gridName(), file2gd.gridName()); + CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), file2gd.getGridPos()); + CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), file2gd.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), file2gd.getEndPos()); + + it = file2.findDescriptor("density"); + CPPUNIT_ASSERT(it != file2.gridDescriptors().end()); + file2gd = it->second; + CPPUNIT_ASSERT_EQUAL(gd2.gridName(), file2gd.gridName()); + CPPUNIT_ASSERT_EQUAL(gd2.getGridPos(), file2gd.getGridPos()); + CPPUNIT_ASSERT_EQUAL(gd2.getBlockPos(), file2gd.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd2.getEndPos(), file2gd.getEndPos()); + + // Clear registries. + GridBase::clearRegistry(); + math::MapRegistry::clear(); + + remove("something.vdb2"); +} + + +void +TestFile::testGridNaming() +{ + using namespace openvdb; + using namespace openvdb::io; + + using TreeType = Int32Tree; + + // Register data types. + openvdb::initialize(); + + logging::LevelScope suppressLogging{logging::Level::Fatal}; + + // Create several grids that share a single tree. + TreeType::Ptr tree(new TreeType(1)); + tree->setValue(Coord(10, 1, 2), 10); + tree->setValue(Coord(0, 0, 0), 5); + GridBase::Ptr + grid1 = openvdb::createGrid(tree), + grid2 = openvdb::createGrid(tree), + grid3 = openvdb::createGrid(tree); + + std::vector gridVec; + gridVec.push_back(grid1); + gridVec.push_back(grid2); + gridVec.push_back(grid3); + + // Give all grids the same name, but also some metadata to distinguish them. + for (int n = 0; n <= 2; ++n) { + gridVec[n]->setName("grid"); + gridVec[n]->insertMeta("index", Int32Metadata(n)); + } + + const char* filename = "testGridNaming.vdb2"; + SharedPtr scopedFile(filename, ::remove); + + // Test first with grid instancing disabled, then with instancing enabled. + for (int instancing = 0; instancing <= 1; ++instancing) { + { + // Write the grids out to a file. + File file(filename); + file.setInstancingEnabled(instancing); + file.write(gridVec); + } + + // Open the file for reading. + File file(filename); + file.setInstancingEnabled(instancing); + file.open(); + + int n = 0; + for (File::NameIterator i = file.beginName(), e = file.endName(); i != e; ++i, ++n) { + CPPUNIT_ASSERT(file.hasGrid(i.gridName())); + } + // Verify that the file contains three grids. + CPPUNIT_ASSERT_EQUAL(3, n); + + // Read each grid. + for (n = -1; n <= 2; ++n) { + openvdb::Name name("grid"); + + // On the first iteration, read the grid named "grid", then read "grid[0]" + // (which is synonymous with "grid"), then "grid[1]", then "grid[2]". + if (n >= 0) { + name = GridDescriptor::nameAsString(GridDescriptor::addSuffix(name, n)); + } + + CPPUNIT_ASSERT(file.hasGrid(name)); + + // Read the current grid. + GridBase::ConstPtr grid = file.readGrid(name); + CPPUNIT_ASSERT(grid.get() != nullptr); + + // Verify that the grid is named "grid". + CPPUNIT_ASSERT_EQUAL(openvdb::Name("grid"), grid->getName()); + CPPUNIT_ASSERT_EQUAL((n < 0 ? 0 : n), grid->metaValue("index")); + } + + // Read all three grids at once. + GridPtrVecPtr allGrids = file.getGrids(); + CPPUNIT_ASSERT(allGrids.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(3, int(allGrids->size())); + + GridBase::ConstPtr firstGrid; + std::vector indices; + for (GridPtrVecCIter i = allGrids->begin(), e = allGrids->end(); i != e; ++i) { + GridBase::ConstPtr grid = *i; + CPPUNIT_ASSERT(grid.get() != nullptr); + + indices.push_back(grid->metaValue("index")); + + // If instancing is enabled, verify that all grids share the same tree. + if (instancing) { + if (!firstGrid) firstGrid = grid; + CPPUNIT_ASSERT_EQUAL(firstGrid->baseTreePtr(), grid->baseTreePtr()); + } + } + // Verify that three distinct grids were read, + // by examining their "index" metadata. + CPPUNIT_ASSERT_EQUAL(3, int(indices.size())); + std::sort(indices.begin(), indices.end()); + CPPUNIT_ASSERT_EQUAL(0, indices[0]); + CPPUNIT_ASSERT_EQUAL(1, indices[1]); + CPPUNIT_ASSERT_EQUAL(2, indices[2]); + } + + { + // Try writing and then reading a grid with a weird name + // that might conflict with the grid name indexing scheme. + const openvdb::Name weirdName("grid[4]"); + gridVec[0]->setName(weirdName); + { + File file(filename); + file.write(gridVec); + } + File file(filename); + file.open(); + + // Verify that the grid can be read and that its index is 0. + GridBase::ConstPtr grid = file.readGrid(weirdName); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(weirdName, grid->getName()); + CPPUNIT_ASSERT_EQUAL(0, grid->metaValue("index")); + + // Verify that the other grids can still be read successfully. + grid = file.readGrid("grid[0]"); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(openvdb::Name("grid"), grid->getName()); + // Because there are now only two grids named "grid", the one with + // index 1 is now "grid[0]". + CPPUNIT_ASSERT_EQUAL(1, grid->metaValue("index")); + + grid = file.readGrid("grid[1]"); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(openvdb::Name("grid"), grid->getName()); + // Because there are now only two grids named "grid", the one with + // index 2 is now "grid[1]". + CPPUNIT_ASSERT_EQUAL(2, grid->metaValue("index")); + + // Verify that there is no longer a third grid named "grid". + CPPUNIT_ASSERT_THROW(file.readGrid("grid[2]"), openvdb::KeyError); + } +} + + +void +TestFile::testEmptyFile() +{ + using namespace openvdb; + using namespace openvdb::io; + + const char* filename = "testEmptyFile.vdb2"; + SharedPtr scopedFile(filename, ::remove); + + { + File file(filename); + file.write(GridPtrVec(), MetaMap()); + } + File file(filename); + file.open(); + + GridPtrVecPtr grids = file.getGrids(); + MetaMap::Ptr meta = file.getMetadata(); + + CPPUNIT_ASSERT(grids.get() != nullptr); + CPPUNIT_ASSERT(grids->empty()); + + CPPUNIT_ASSERT(meta.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(0, int(meta->metaCount())); +} + + +void +TestFile::testEmptyGridIO() +{ + using namespace openvdb; + using namespace openvdb::io; + + using GridType = Int32Grid; + + logging::LevelScope suppressLogging{logging::Level::Fatal}; + + const char* filename = "something.vdb2"; + SharedPtr scopedFile(filename, ::remove); + + File file(filename); + + std::ostringstream ostr(std::ios_base::binary); + + // Create a grid with transform. + GridType::Ptr grid = createGrid(/*bg=*/1); + math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1); + grid->setTransform(trans); + + // Create another grid with transform. + math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.2); + GridType::Ptr grid2 = createGrid(/*bg=*/2); + grid2->setTransform(trans2); + + // Create the grid descriptor out of this grid. + GridDescriptor gd(Name("temperature"), grid->type()); + GridDescriptor gd2(Name("density"), grid2->type()); + + // Write out the number of grids. + int32_t gridCount = 2; + ostr.write(reinterpret_cast(&gridCount), sizeof(int32_t)); + // Write out the grids. + file.writeGrid(gd, grid, ostr, /*seekable=*/true); + file.writeGrid(gd2, grid2, ostr, /*seekable=*/true); + + // Ensure that the block offset and the end offsets are equivalent. + CPPUNIT_ASSERT_EQUAL(0, int(grid->baseTree().leafCount())); + CPPUNIT_ASSERT_EQUAL(0, int(grid2->baseTree().leafCount())); + CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), gd.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd2.getEndPos(), gd2.getBlockPos()); + + // Register the grid and the transform and the blocks. + GridBase::clearRegistry(); + GridType::registerGrid(); + // register maps + math::MapRegistry::clear(); + math::AffineMap::registerMap(); + math::ScaleMap::registerMap(); + math::UniformScaleMap::registerMap(); + math::TranslationMap::registerMap(); + math::ScaleTranslateMap::registerMap(); + math::UniformScaleTranslateMap::registerMap(); + math::NonlinearFrustumMap::registerMap(); + + // Read in the grid descriptors. + File file2(filename); + std::istringstream istr(ostr.str(), std::ios_base::binary); + io::setCurrentVersion(istr); + file2.readGridDescriptors(istr); + + // Compare with the initial grid descriptors. + File::NameMapCIter it = file2.findDescriptor("temperature"); + CPPUNIT_ASSERT(it != file2.gridDescriptors().end()); + GridDescriptor file2gd = it->second; + file2gd.seekToGrid(istr); + GridBase::Ptr gd_grid = GridBase::createGrid(file2gd.gridType()); + Archive::readGridCompression(istr); + gd_grid->readMeta(istr); + gd_grid->readTransform(istr); + gd_grid->readTopology(istr); + CPPUNIT_ASSERT_EQUAL(gd.gridName(), file2gd.gridName()); + CPPUNIT_ASSERT(gd_grid.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(0, int(gd_grid->baseTree().leafCount())); + //CPPUNIT_ASSERT_EQUAL(8, int(gd_grid->baseTree().nonLeafCount())); + CPPUNIT_ASSERT_EQUAL(4, int(gd_grid->baseTree().treeDepth())); + CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), file2gd.getGridPos()); + CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), file2gd.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), file2gd.getEndPos()); + + it = file2.findDescriptor("density"); + CPPUNIT_ASSERT(it != file2.gridDescriptors().end()); + file2gd = it->second; + file2gd.seekToGrid(istr); + gd_grid = GridBase::createGrid(file2gd.gridType()); + Archive::readGridCompression(istr); + gd_grid->readMeta(istr); + gd_grid->readTransform(istr); + gd_grid->readTopology(istr); + CPPUNIT_ASSERT_EQUAL(gd2.gridName(), file2gd.gridName()); + CPPUNIT_ASSERT(gd_grid.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(0, int(gd_grid->baseTree().leafCount())); + //CPPUNIT_ASSERT_EQUAL(8, int(gd_grid->nonLeafCount())); + CPPUNIT_ASSERT_EQUAL(4, int(gd_grid->baseTree().treeDepth())); + CPPUNIT_ASSERT_EQUAL(gd2.getGridPos(), file2gd.getGridPos()); + CPPUNIT_ASSERT_EQUAL(gd2.getBlockPos(), file2gd.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd2.getEndPos(), file2gd.getEndPos()); + + // Clear registries. + GridBase::clearRegistry(); + math::MapRegistry::clear(); +} + + +void +TestFile::testOpen() +{ + using namespace openvdb; + + using FloatGrid = openvdb::FloatGrid; + using IntGrid = openvdb::Int32Grid; + using FloatTree = FloatGrid::TreeType; + using IntTree = Int32Grid::TreeType; + + // Create a VDB to write. + + // Create grids + IntGrid::Ptr grid = createGrid(/*bg=*/1); + IntTree& tree = grid->tree(); + grid->setName("density"); + + FloatGrid::Ptr grid2 = createGrid(/*bg=*/2.0); + FloatTree& tree2 = grid2->tree(); + grid2->setName("temperature"); + + // Create transforms + math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1); + math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1); + grid->setTransform(trans); + grid2->setTransform(trans2); + + // Set some values + tree.setValue(Coord(0, 0, 0), 5); + tree.setValue(Coord(100, 0, 0), 6); + tree2.setValue(Coord(0, 0, 0), 10); + tree2.setValue(Coord(0, 100, 0), 11); + + MetaMap meta; + meta.insertMeta("author", StringMetadata("Einstein")); + meta.insertMeta("year", Int32Metadata(2009)); + + GridPtrVec grids; + grids.push_back(grid); + grids.push_back(grid2); + + CPPUNIT_ASSERT(findGridByName(grids, "density") == grid); + CPPUNIT_ASSERT(findGridByName(grids, "temperature") == grid2); + CPPUNIT_ASSERT(meta.metaValue("author") == "Einstein"); + CPPUNIT_ASSERT_EQUAL(2009, meta.metaValue("year")); + + // Register grid and transform. + GridBase::clearRegistry(); + IntGrid::registerGrid(); + FloatGrid::registerGrid(); + Metadata::clearRegistry(); + StringMetadata::registerType(); + Int32Metadata::registerType(); + // register maps + math::MapRegistry::clear(); + math::AffineMap::registerMap(); + math::ScaleMap::registerMap(); + math::UniformScaleMap::registerMap(); + math::TranslationMap::registerMap(); + math::ScaleTranslateMap::registerMap(); + math::UniformScaleTranslateMap::registerMap(); + math::NonlinearFrustumMap::registerMap(); + + // Write the vdb out to a file. + io::File vdbfile("something.vdb2"); + vdbfile.write(grids, meta); + + // Now we can read in the file. + CPPUNIT_ASSERT(!vdbfile.open());//opening the same file + // Can't open same file multiple times without closing. + CPPUNIT_ASSERT_THROW(vdbfile.open(), openvdb::IoError); + vdbfile.close(); + CPPUNIT_ASSERT(!vdbfile.open());//opening the same file + + CPPUNIT_ASSERT(vdbfile.isOpen()); + + uint32_t version = OPENVDB_FILE_VERSION; + + CPPUNIT_ASSERT_EQUAL(version, vdbfile.fileVersion()); + CPPUNIT_ASSERT_EQUAL(version, io::getFormatVersion(vdbfile.inputStream())); + CPPUNIT_ASSERT_EQUAL(OPENVDB_LIBRARY_MAJOR_VERSION, vdbfile.libraryVersion().first); + CPPUNIT_ASSERT_EQUAL(OPENVDB_LIBRARY_MINOR_VERSION, vdbfile.libraryVersion().second); + CPPUNIT_ASSERT_EQUAL(OPENVDB_LIBRARY_MAJOR_VERSION, + io::getLibraryVersion(vdbfile.inputStream()).first); + CPPUNIT_ASSERT_EQUAL(OPENVDB_LIBRARY_MINOR_VERSION, + io::getLibraryVersion(vdbfile.inputStream()).second); + + // Ensure that we read in the vdb metadata. + CPPUNIT_ASSERT(vdbfile.getMetadata()); + CPPUNIT_ASSERT(vdbfile.getMetadata()->metaValue("author") == "Einstein"); + CPPUNIT_ASSERT_EQUAL(2009, vdbfile.getMetadata()->metaValue("year")); + + // Ensure we got the grid descriptors. + CPPUNIT_ASSERT_EQUAL(1, int(vdbfile.gridDescriptors().count("density"))); + CPPUNIT_ASSERT_EQUAL(1, int(vdbfile.gridDescriptors().count("temperature"))); + + io::File::NameMapCIter it = vdbfile.findDescriptor("density"); + CPPUNIT_ASSERT(it != vdbfile.gridDescriptors().end()); + io::GridDescriptor gd = it->second; + CPPUNIT_ASSERT_EQUAL(IntTree::treeType(), gd.gridType()); + + it = vdbfile.findDescriptor("temperature"); + CPPUNIT_ASSERT(it != vdbfile.gridDescriptors().end()); + gd = it->second; + CPPUNIT_ASSERT_EQUAL(FloatTree::treeType(), gd.gridType()); + + // Ensure we throw an error if there is no file. + io::File vdbfile2("somethingelses.vdb2"); + CPPUNIT_ASSERT_THROW(vdbfile2.open(), openvdb::IoError); + CPPUNIT_ASSERT_THROW(vdbfile2.inputStream(), openvdb::IoError); + + // Clear registries. + GridBase::clearRegistry(); + Metadata::clearRegistry(); + math::MapRegistry::clear(); + + // Test closing the file. + vdbfile.close(); + CPPUNIT_ASSERT(vdbfile.isOpen() == false); + CPPUNIT_ASSERT(vdbfile.fileMetadata().get() == nullptr); + CPPUNIT_ASSERT_EQUAL(0, int(vdbfile.gridDescriptors().size())); + CPPUNIT_ASSERT_THROW(vdbfile.inputStream(), openvdb::IoError); + + remove("something.vdb2"); +} + + +void +TestFile::testNonVdbOpen() +{ + std::ofstream file("dummy.vdb2", std::ios_base::binary); + + int64_t something = 1; + file.write(reinterpret_cast(&something), sizeof(int64_t)); + + file.close(); + + openvdb::io::File vdbfile("dummy.vdb2"); + CPPUNIT_ASSERT_THROW(vdbfile.open(), openvdb::IoError); + CPPUNIT_ASSERT_THROW(vdbfile.inputStream(), openvdb::IoError); + + remove("dummy.vdb2"); +} + + +void +TestFile::testGetMetadata() +{ + using namespace openvdb; + + GridPtrVec grids; + MetaMap meta; + + meta.insertMeta("author", StringMetadata("Einstein")); + meta.insertMeta("year", Int32Metadata(2009)); + + // Adjust registry before writing. + Metadata::clearRegistry(); + StringMetadata::registerType(); + Int32Metadata::registerType(); + + // Write the vdb out to a file. + io::File vdbfile("something.vdb2"); + vdbfile.write(grids, meta); + + // Check if reading without opening the file + CPPUNIT_ASSERT_THROW(vdbfile.getMetadata(), openvdb::IoError); + + vdbfile.open(); + + MetaMap::Ptr meta2 = vdbfile.getMetadata(); + + CPPUNIT_ASSERT_EQUAL(2, int(meta2->metaCount())); + + CPPUNIT_ASSERT(meta2->metaValue("author") == "Einstein"); + CPPUNIT_ASSERT_EQUAL(2009, meta2->metaValue("year")); + + // Clear registry. + Metadata::clearRegistry(); + + remove("something.vdb2"); +} + + +void +TestFile::testReadAll() +{ + using namespace openvdb; + + using FloatGrid = openvdb::FloatGrid; + using IntGrid = openvdb::Int32Grid; + using FloatTree = FloatGrid::TreeType; + using IntTree = Int32Grid::TreeType; + + // Create a vdb to write. + + // Create grids + IntGrid::Ptr grid1 = createGrid(/*bg=*/1); + IntTree& tree = grid1->tree(); + grid1->setName("density"); + + FloatGrid::Ptr grid2 = createGrid(/*bg=*/2.0); + FloatTree& tree2 = grid2->tree(); + grid2->setName("temperature"); + + // Create transforms + math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1); + math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1); + grid1->setTransform(trans); + grid2->setTransform(trans2); + + // Set some values + tree.setValue(Coord(0, 0, 0), 5); + tree.setValue(Coord(100, 0, 0), 6); + tree2.setValue(Coord(0, 0, 0), 10); + tree2.setValue(Coord(0, 100, 0), 11); + + MetaMap meta; + meta.insertMeta("author", StringMetadata("Einstein")); + meta.insertMeta("year", Int32Metadata(2009)); + + GridPtrVec grids; + grids.push_back(grid1); + grids.push_back(grid2); + + // Register grid and transform. + openvdb::initialize(); + + // Write the vdb out to a file. + io::File vdbfile("something.vdb2"); + vdbfile.write(grids, meta); + + io::File vdbfile2("something.vdb2"); + CPPUNIT_ASSERT_THROW(vdbfile2.getGrids(), openvdb::IoError); + + vdbfile2.open(); + CPPUNIT_ASSERT(vdbfile2.isOpen()); + + GridPtrVecPtr grids2 = vdbfile2.getGrids(); + MetaMap::Ptr meta2 = vdbfile2.getMetadata(); + + // Ensure we have the metadata. + CPPUNIT_ASSERT_EQUAL(2, int(meta2->metaCount())); + CPPUNIT_ASSERT(meta2->metaValue("author") == "Einstein"); + CPPUNIT_ASSERT_EQUAL(2009, meta2->metaValue("year")); + + // Ensure we got the grids. + CPPUNIT_ASSERT_EQUAL(2, int(grids2->size())); + + GridBase::Ptr grid; + grid.reset(); + grid = findGridByName(*grids2, "density"); + CPPUNIT_ASSERT(grid.get() != nullptr); + IntTree::Ptr density = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(density.get() != nullptr); + + grid.reset(); + grid = findGridByName(*grids2, "temperature"); + CPPUNIT_ASSERT(grid.get() != nullptr); + FloatTree::Ptr temperature = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(temperature.get() != nullptr); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(5, density->getValue(Coord(0, 0, 0)), /*tolerance=*/0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6, density->getValue(Coord(100, 0, 0)), /*tolerance=*/0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10, temperature->getValue(Coord(0, 0, 0)), /*tolerance=*/0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(11, temperature->getValue(Coord(0, 100, 0)), /*tolerance=*/0); + + // Clear registries. + GridBase::clearRegistry(); + Metadata::clearRegistry(); + math::MapRegistry::clear(); + + vdbfile2.close(); + + remove("something.vdb2"); +} + + +void +TestFile::testWriteOpenFile() +{ + using namespace openvdb; + + MetaMap::Ptr meta(new MetaMap); + meta->insertMeta("author", StringMetadata("Einstein")); + meta->insertMeta("year", Int32Metadata(2009)); + + // Register metadata + Metadata::clearRegistry(); + StringMetadata::registerType(); + Int32Metadata::registerType(); + + // Write the metadata out to a file. + io::File vdbfile("something.vdb2"); + vdbfile.write(GridPtrVec(), *meta); + + io::File vdbfile2("something.vdb2"); + CPPUNIT_ASSERT_THROW(vdbfile2.getGrids(), openvdb::IoError); + + vdbfile2.open(); + CPPUNIT_ASSERT(vdbfile2.isOpen()); + + GridPtrVecPtr grids = vdbfile2.getGrids(); + meta = vdbfile2.getMetadata(); + + // Ensure we have the metadata. + CPPUNIT_ASSERT(meta.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(2, int(meta->metaCount())); + CPPUNIT_ASSERT(meta->metaValue("author") == "Einstein"); + CPPUNIT_ASSERT_EQUAL(2009, meta->metaValue("year")); + + // Ensure we got the grids. + CPPUNIT_ASSERT(grids.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(0, int(grids->size())); + + // Cannot write an open file. + CPPUNIT_ASSERT_THROW(vdbfile2.write(*grids), openvdb::IoError); + + vdbfile2.close(); + + CPPUNIT_ASSERT_NO_THROW(vdbfile2.write(*grids)); + + // Clear registries. + Metadata::clearRegistry(); + + remove("something.vdb2"); +} + + +void +TestFile::testReadGridMetadata() +{ + using namespace openvdb; + + openvdb::initialize(); + + const char* filename = "testReadGridMetadata.vdb2"; + SharedPtr scopedFile(filename, ::remove); + + // Create grids + Int32Grid::Ptr igrid = createGrid(/*bg=*/1); + FloatGrid::Ptr fgrid = createGrid(/*bg=*/2.0); + + // Add metadata. + igrid->setName("igrid"); + igrid->insertMeta("author", StringMetadata("Einstein")); + igrid->insertMeta("year", Int32Metadata(2012)); + + fgrid->setName("fgrid"); + fgrid->insertMeta("author", StringMetadata("Einstein")); + fgrid->insertMeta("year", Int32Metadata(2012)); + + // Add transforms. + math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1); + igrid->setTransform(trans); + fgrid->setTransform(trans); + + // Set some values. + igrid->tree().setValue(Coord(0, 0, 0), 5); + igrid->tree().setValue(Coord(100, 0, 0), 6); + fgrid->tree().setValue(Coord(0, 0, 0), 10); + fgrid->tree().setValue(Coord(0, 100, 0), 11); + + GridPtrVec srcGrids; + srcGrids.push_back(igrid); + srcGrids.push_back(fgrid); + std::map srcGridMap; + srcGridMap[igrid->getName()] = igrid; + srcGridMap[fgrid->getName()] = fgrid; + + enum { OUTPUT_TO_FILE = 0, OUTPUT_TO_STREAM = 1 }; + for (int outputMethod = OUTPUT_TO_FILE; outputMethod <= OUTPUT_TO_STREAM; ++outputMethod) + { + if (outputMethod == OUTPUT_TO_FILE) { + // Write the grids to a file. + io::File vdbfile(filename); + vdbfile.write(srcGrids); + } else { + // Stream the grids to a file (i.e., without file offsets). + std::ofstream ostrm(filename, std::ios_base::binary); + io::Stream(ostrm).write(srcGrids); + } + + // Read just the grid-level metadata from the file. + io::File vdbfile(filename); + + // Verify that reading from an unopened file generates an exception. + CPPUNIT_ASSERT_THROW(vdbfile.readGridMetadata("igrid"), openvdb::IoError); + CPPUNIT_ASSERT_THROW(vdbfile.readGridMetadata("noname"), openvdb::IoError); + CPPUNIT_ASSERT_THROW(vdbfile.readAllGridMetadata(), openvdb::IoError); + + vdbfile.open(); + + CPPUNIT_ASSERT(vdbfile.isOpen()); + + // Verify that reading a nonexistent grid generates an exception. + CPPUNIT_ASSERT_THROW(vdbfile.readGridMetadata("noname"), openvdb::KeyError); + + // Read all grids and store them in a list. + GridPtrVecPtr gridMetadata = vdbfile.readAllGridMetadata(); + CPPUNIT_ASSERT(gridMetadata.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(2, int(gridMetadata->size())); + + // Read individual grids and append them to the list. + GridBase::Ptr grid = vdbfile.readGridMetadata("igrid"); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(std::string("igrid"), grid->getName()); + gridMetadata->push_back(grid); + + grid = vdbfile.readGridMetadata("fgrid"); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(std::string("fgrid"), grid->getName()); + gridMetadata->push_back(grid); + + // Verify that the grids' metadata and transforms match the original grids'. + for (size_t i = 0, N = gridMetadata->size(); i < N; ++i) { + grid = (*gridMetadata)[i]; + + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT(grid->getName() == "igrid" || grid->getName() == "fgrid"); + CPPUNIT_ASSERT(grid->baseTreePtr().get() != nullptr); + + // Since we didn't read the grid's topology, the tree should be empty. + CPPUNIT_ASSERT_EQUAL(0, int(grid->constBaseTreePtr()->leafCount())); + CPPUNIT_ASSERT_EQUAL(0, int(grid->constBaseTreePtr()->activeVoxelCount())); + + // Retrieve the source grid of the same name. + GridBase::ConstPtr srcGrid = srcGridMap[grid->getName()]; + + // Compare grid types and transforms. + CPPUNIT_ASSERT_EQUAL(srcGrid->type(), grid->type()); + CPPUNIT_ASSERT_EQUAL(srcGrid->transform(), grid->transform()); + + // Compare metadata, ignoring fields that were added when the file was written. + MetaMap::Ptr + statsMetadata = grid->getStatsMetadata(), + otherMetadata = grid->copyMeta(); // shallow copy + CPPUNIT_ASSERT(statsMetadata->metaCount() != 0); + statsMetadata->insertMeta(GridBase::META_FILE_COMPRESSION, StringMetadata("")); + for (MetaMap::ConstMetaIterator it = grid->beginMeta(), end = grid->endMeta(); + it != end; ++it) + { + // Keep all fields that exist in the source grid. + if ((*srcGrid)[it->first]) continue; + // Remove any remaining grid statistics fields. + if ((*statsMetadata)[it->first]) { + otherMetadata->removeMeta(it->first); + } + // Remove delay load metadata if it exists. + if ((*otherMetadata)["file_delayed_load"]) { + otherMetadata->removeMeta("file_delayed_load"); + } + } + CPPUNIT_ASSERT_EQUAL(srcGrid->str(), otherMetadata->str()); + + const CoordBBox srcBBox = srcGrid->evalActiveVoxelBoundingBox(); + CPPUNIT_ASSERT_EQUAL(srcBBox.min().asVec3i(), grid->metaValue("file_bbox_min")); + CPPUNIT_ASSERT_EQUAL(srcBBox.max().asVec3i(), grid->metaValue("file_bbox_max")); + CPPUNIT_ASSERT_EQUAL(srcGrid->activeVoxelCount(), + Index64(grid->metaValue("file_voxel_count"))); + CPPUNIT_ASSERT_EQUAL(srcGrid->memUsage(), + Index64(grid->metaValue("file_mem_bytes"))); + } + } +} + + +void +TestFile::testReadGrid() +{ + using namespace openvdb; + + using FloatGrid = openvdb::FloatGrid; + using IntGrid = openvdb::Int32Grid; + using FloatTree = FloatGrid::TreeType; + using IntTree = Int32Grid::TreeType; + + // Create a vdb to write. + + // Create grids + IntGrid::Ptr grid = createGrid(/*bg=*/1); + IntTree& tree = grid->tree(); + grid->setName("density"); + + FloatGrid::Ptr grid2 = createGrid(/*bg=*/2.0); + FloatTree& tree2 = grid2->tree(); + grid2->setName("temperature"); + + // Create transforms + math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1); + math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1); + grid->setTransform(trans); + grid2->setTransform(trans2); + + // Set some values + tree.setValue(Coord(0, 0, 0), 5); + tree.setValue(Coord(100, 0, 0), 6); + tree2.setValue(Coord(0, 0, 0), 10); + tree2.setValue(Coord(0, 100, 0), 11); + + MetaMap meta; + meta.insertMeta("author", StringMetadata("Einstein")); + meta.insertMeta("year", Int32Metadata(2009)); + + GridPtrVec grids; + grids.push_back(grid); + grids.push_back(grid2); + + // Register grid and transform. + openvdb::initialize(); + + // Write the vdb out to a file. + io::File vdbfile("something.vdb2"); + vdbfile.write(grids, meta); + + io::File vdbfile2("something.vdb2"); + + vdbfile2.open(); + + CPPUNIT_ASSERT(vdbfile2.isOpen()); + + // Get Temperature + GridBase::Ptr temperature = vdbfile2.readGrid("temperature"); + + CPPUNIT_ASSERT(temperature.get() != nullptr); + + FloatTree::Ptr typedTemperature = gridPtrCast(temperature)->treePtr(); + + CPPUNIT_ASSERT(typedTemperature.get() != nullptr); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(10, typedTemperature->getValue(Coord(0, 0, 0)), 0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(11, typedTemperature->getValue(Coord(0, 100, 0)), 0); + + // Get Density + GridBase::Ptr density = vdbfile2.readGrid("density"); + + CPPUNIT_ASSERT(density.get() != nullptr); + + IntTree::Ptr typedDensity = gridPtrCast(density)->treePtr(); + + CPPUNIT_ASSERT(typedDensity.get() != nullptr); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(5,typedDensity->getValue(Coord(0, 0, 0)), /*tolerance=*/0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6,typedDensity->getValue(Coord(100, 0, 0)), /*tolerance=*/0); + + // Clear registries. + GridBase::clearRegistry(); + Metadata::clearRegistry(); + math::MapRegistry::clear(); + + vdbfile2.close(); + + remove("something.vdb2"); +} + + +//////////////////////////////////////// + + +template +void +validateClippedGrid(const GridT& clipped, const typename GridT::ValueType& fg) +{ + using namespace openvdb; + + using ValueT = typename GridT::ValueType; + + const CoordBBox bbox = clipped.evalActiveVoxelBoundingBox(); + CPPUNIT_ASSERT_EQUAL(4, bbox.min().x()); + CPPUNIT_ASSERT_EQUAL(4, bbox.min().y()); + CPPUNIT_ASSERT_EQUAL(-6, bbox.min().z()); + CPPUNIT_ASSERT_EQUAL(4, bbox.max().x()); + CPPUNIT_ASSERT_EQUAL(4, bbox.max().y()); + CPPUNIT_ASSERT_EQUAL(6, bbox.max().z()); + CPPUNIT_ASSERT_EQUAL(6 + 6 + 1, int(clipped.activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(2, int(clipped.constTree().leafCount())); + + typename GridT::ConstAccessor acc = clipped.getConstAccessor(); + const ValueT bg = clipped.background(); + Coord xyz; + int &x = xyz[0], &y = xyz[1], &z = xyz[2]; + for (x = -10; x <= 10; ++x) { + for (y = -10; y <= 10; ++y) { + for (z = -10; z <= 10; ++z) { + if (x == 4 && y == 4 && z >= -6 && z <= 6) { + CPPUNIT_ASSERT_EQUAL(fg, acc.getValue(Coord(4, 4, z))); + } else { + CPPUNIT_ASSERT_EQUAL(bg, acc.getValue(Coord(x, y, z))); + } + } + } + } +} + + +// See also TestGrid::testClipping() +void +TestFile::testReadClippedGrid() +{ + using namespace openvdb; + + // Register types. + openvdb::initialize(); + + // World-space clipping region + const BBoxd clipBox(Vec3d(4.0, 4.0, -6.0), Vec3d(4.9, 4.9, 6.0)); + + // Create grids of several types and fill a cubic region of each with a foreground value. + + const bool bfg = true; + BoolGrid::Ptr bgrid = BoolGrid::create(/*bg=*/zeroVal()); + bgrid->setName("bgrid"); + bgrid->fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/bfg, /*active=*/true); + + const float ffg = 5.f; + FloatGrid::Ptr fgrid = FloatGrid::create(/*bg=*/zeroVal()); + fgrid->setName("fgrid"); + fgrid->fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/ffg, /*active=*/true); + + const Vec3s vfg(1.f, -2.f, 3.f); + Vec3SGrid::Ptr vgrid = Vec3SGrid::create(/*bg=*/zeroVal()); + vgrid->setName("vgrid"); + vgrid->fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/vfg, /*active=*/true); + + GridPtrVec srcGrids; + srcGrids.push_back(bgrid); + srcGrids.push_back(fgrid); + srcGrids.push_back(vgrid); + + const char* filename = "testReadClippedGrid.vdb"; + SharedPtr scopedFile(filename, ::remove); + + enum { OUTPUT_TO_FILE = 0, OUTPUT_TO_STREAM = 1 }; + for (int outputMethod = OUTPUT_TO_FILE; outputMethod <= OUTPUT_TO_STREAM; ++outputMethod) + { + if (outputMethod == OUTPUT_TO_FILE) { + // Write the grids to a file. + io::File vdbfile(filename); + vdbfile.write(srcGrids); + } else { + // Stream the grids to a file (i.e., without file offsets). + std::ofstream ostrm(filename, std::ios_base::binary); + io::Stream(ostrm).write(srcGrids); + } + + // Open the file for reading. + io::File vdbfile(filename); + vdbfile.open(); + + GridBase::Ptr grid; + + // Read and clip each grid. + + CPPUNIT_ASSERT_NO_THROW(grid = vdbfile.readGrid("bgrid", clipBox)); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_NO_THROW(bgrid = gridPtrCast(grid)); + validateClippedGrid(*bgrid, bfg); + + CPPUNIT_ASSERT_NO_THROW(grid = vdbfile.readGrid("fgrid", clipBox)); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_NO_THROW(fgrid = gridPtrCast(grid)); + validateClippedGrid(*fgrid, ffg); + + CPPUNIT_ASSERT_NO_THROW(grid = vdbfile.readGrid("vgrid", clipBox)); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_NO_THROW(vgrid = gridPtrCast(grid)); + validateClippedGrid(*vgrid, vfg); + } +} + + +//////////////////////////////////////// + + +namespace { + +template struct MultiPassLeafNode; // forward declaration + +// Dummy value type +using MultiPassValue = openvdb::PointIndex; + +// Tree configured to match the default OpenVDB configuration +using MultiPassTree = openvdb::tree::Tree< + openvdb::tree::RootNode< + openvdb::tree::InternalNode< + openvdb::tree::InternalNode< + MultiPassLeafNode, 4>, 5>>>; + +using MultiPassGrid = openvdb::Grid; + + +template +struct MultiPassLeafNode: public openvdb::tree::LeafNode, openvdb::io::MultiPass +{ + // The following had to be copied from the LeafNode class + // to make the derived class compatible with the tree structure. + + using LeafNodeType = MultiPassLeafNode; + using Ptr = openvdb::SharedPtr; + using BaseLeaf = openvdb::tree::LeafNode; + using NodeMaskType = openvdb::util::NodeMask; + using ValueType = T; + using ValueOnCIter = typename BaseLeaf::template ValueIter; + using ChildOnIter = typename BaseLeaf::template ChildIter; + using ChildOnCIter = typename BaseLeaf::template ChildIter< + typename NodeMaskType::OnIterator, const MultiPassLeafNode, typename BaseLeaf::ChildOn>; + + MultiPassLeafNode(const openvdb::Coord& coords, const T& value, bool active = false) + : BaseLeaf(coords, value, active) {} + MultiPassLeafNode(openvdb::PartialCreate, const openvdb::Coord& coords, const T& value, + bool active = false): BaseLeaf(openvdb::PartialCreate(), coords, value, active) {} + MultiPassLeafNode(const MultiPassLeafNode& rhs): BaseLeaf(rhs) {} + + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(this->getValueMask().beginOn(),this); } + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(this->getValueMask().endOn(), this); } + ChildOnIter beginChildOn() { return ChildOnIter(this->getValueMask().endOn(), this); } + + // Methods in use for reading and writing multiple buffers + + void readBuffers(std::istream& is, const openvdb::CoordBBox&, bool fromHalf = false) + { + this->readBuffers(is, fromHalf); + } + + void readBuffers(std::istream& is, bool /*fromHalf*/ = false) + { + const openvdb::io::StreamMetadata::Ptr meta = openvdb::io::getStreamMetadataPtr(is); + if (!meta) { + OPENVDB_THROW(openvdb::IoError, + "Cannot write out a MultiBufferLeaf without StreamMetadata."); + } + + // clamp pass to 16-bit integer + const uint32_t pass(static_cast(meta->pass())); + + // Read in the stored pass number. + uint32_t readPass; + is.read(reinterpret_cast(&readPass), sizeof(uint32_t)); + CPPUNIT_ASSERT_EQUAL(pass, readPass); + // Record the pass number. + mReadPasses.push_back(readPass); + + if (pass == 0) { + // Read in the node's origin. + openvdb::Coord origin; + is.read(reinterpret_cast(&origin), sizeof(openvdb::Coord)); + CPPUNIT_ASSERT_EQUAL(origin, this->origin()); + } + } + + void writeBuffers(std::ostream& os, bool /*toHalf*/ = false) const + { + const openvdb::io::StreamMetadata::Ptr meta = openvdb::io::getStreamMetadataPtr(os); + if (!meta) { + OPENVDB_THROW(openvdb::IoError, + "Cannot read in a MultiBufferLeaf without StreamMetadata."); + } + + // clamp pass to 16-bit integer + const uint32_t pass(static_cast(meta->pass())); + + // Leaf traversal analysis deduces the number of passes to perform for this leaf + // then updates the leaf traversal value to ensure all passes will be written. + if (meta->countingPasses()) { + if (mNumPasses > pass) meta->setPass(mNumPasses); + return; + } + + // Record the pass number. + CPPUNIT_ASSERT(mWritePassesPtr); + const_cast&>(*mWritePassesPtr).push_back(pass); + + // Write out the pass number. + os.write(reinterpret_cast(&pass), sizeof(uint32_t)); + if (pass == 0) { + // Write out the node's origin and the pass number. + const auto origin = this->origin(); + os.write(reinterpret_cast(&origin), sizeof(openvdb::Coord)); + } + } + + + uint32_t mNumPasses = 0; + // Pointer to external vector in which to record passes as they are written + std::vector* mWritePassesPtr = nullptr; + // Vector in which to record passes as they are read + // (this needs to be internal, because leaf nodes are constructed as a grid is read) + std::vector mReadPasses; +}; // struct MultiPassLeafNode + +} // anonymous namespace + + +void +TestFile::testMultiPassIO() +{ + using namespace openvdb; + + openvdb::initialize(); + MultiPassGrid::registerGrid(); + + // Create a multi-buffer grid. + const MultiPassGrid::Ptr grid = openvdb::createGrid(); + grid->setName("test"); + grid->setTransform(math::Transform::createLinearTransform(1.0)); + MultiPassGrid::TreeType& tree = grid->tree(); + tree.setValue(Coord(0, 0, 0), 5); + tree.setValue(Coord(0, 10, 0), 5); + CPPUNIT_ASSERT_EQUAL(2, int(tree.leafCount())); + + const GridPtrVec grids{grid}; + + // Vector in which to record pass numbers (to ensure blocked ordering) + std::vector writePasses; + { + // Specify the required number of I/O passes for each leaf node. + MultiPassGrid::TreeType::LeafIter leafIter = tree.beginLeaf(); + leafIter->mNumPasses = 3; + leafIter->mWritePassesPtr = &writePasses; + ++leafIter; + leafIter->mNumPasses = 2; + leafIter->mWritePassesPtr = &writePasses; + } + + const char* filename = "testMultiPassIO.vdb"; + SharedPtr scopedFile(filename, ::remove); + { + // Verify that passes are written to a file in the correct order. + io::File(filename).write(grids); + CPPUNIT_ASSERT_EQUAL(6, int(writePasses.size())); + CPPUNIT_ASSERT_EQUAL(0, writePasses[0]); // leaf 0 + CPPUNIT_ASSERT_EQUAL(0, writePasses[1]); // leaf 1 + CPPUNIT_ASSERT_EQUAL(1, writePasses[2]); // leaf 0 + CPPUNIT_ASSERT_EQUAL(1, writePasses[3]); // leaf 1 + CPPUNIT_ASSERT_EQUAL(2, writePasses[4]); // leaf 0 + CPPUNIT_ASSERT_EQUAL(2, writePasses[5]); // leaf 1 + } + { + // Verify that passes are read in the correct order. + io::File file(filename); + file.open(); + const auto newGrid = GridBase::grid(file.readGrid("test")); + + auto leafIter = newGrid->tree().beginLeaf(); + CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size())); + CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]); + CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]); + CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]); + ++leafIter; + CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size())); + CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]); + CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]); + CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]); + } +#if OPENVDB_ABI_VERSION_NUMBER >= 4 + { + // Verify that when using multi-pass and bbox clipping that each leaf node + // is still being read before being clipped + io::File file(filename); + file.open(); + const auto newGrid = GridBase::grid( + file.readGrid("test", BBoxd(Vec3d(0), Vec3d(1)))); + CPPUNIT_ASSERT_EQUAL(Index32(1), newGrid->tree().leafCount()); + + auto leafIter = newGrid->tree().beginLeaf(); + CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size())); + CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]); + CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]); + CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]); + ++leafIter; + CPPUNIT_ASSERT(!leafIter); // second leaf node has now been clipped + } +#endif + + // Clear the pass data. + writePasses.clear(); + + { + // Verify that passes are written to and read from a non-seekable stream + // in the correct order. + std::ostringstream ostr(std::ios_base::binary); + io::Stream(ostr).write(grids); + + CPPUNIT_ASSERT_EQUAL(6, int(writePasses.size())); + CPPUNIT_ASSERT_EQUAL(0, writePasses[0]); // leaf 0 + CPPUNIT_ASSERT_EQUAL(0, writePasses[1]); // leaf 1 + CPPUNIT_ASSERT_EQUAL(1, writePasses[2]); // leaf 0 + CPPUNIT_ASSERT_EQUAL(1, writePasses[3]); // leaf 1 + CPPUNIT_ASSERT_EQUAL(2, writePasses[4]); // leaf 0 + CPPUNIT_ASSERT_EQUAL(2, writePasses[5]); // leaf 1 + + std::istringstream is(ostr.str(), std::ios_base::binary); + io::Stream strm(is); + const auto streamedGrids = strm.getGrids(); + CPPUNIT_ASSERT_EQUAL(1, int(streamedGrids->size())); + + const auto newGrid = gridPtrCast(*streamedGrids->begin()); + CPPUNIT_ASSERT(bool(newGrid)); + auto leafIter = newGrid->tree().beginLeaf(); + CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size())); + CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]); + CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]); + CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]); + ++leafIter; + CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size())); + CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]); + CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]); + CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]); + } +} + + +//////////////////////////////////////// + + +void +TestFile::testHasGrid() +{ + using namespace openvdb; + using namespace openvdb::io; + + using FloatGrid = openvdb::FloatGrid; + using IntGrid = openvdb::Int32Grid; + using FloatTree = FloatGrid::TreeType; + using IntTree = Int32Grid::TreeType; + + // Create a vdb to write. + + // Create grids + IntGrid::Ptr grid = createGrid(/*bg=*/1); + IntTree& tree = grid->tree(); + grid->setName("density"); + + FloatGrid::Ptr grid2 = createGrid(/*bg=*/2.0); + FloatTree& tree2 = grid2->tree(); + grid2->setName("temperature"); + + // Create transforms + math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1); + math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1); + grid->setTransform(trans); + grid2->setTransform(trans2); + + // Set some values + tree.setValue(Coord(0, 0, 0), 5); + tree.setValue(Coord(100, 0, 0), 6); + tree2.setValue(Coord(0, 0, 0), 10); + tree2.setValue(Coord(0, 100, 0), 11); + + MetaMap meta; + meta.insertMeta("author", StringMetadata("Einstein")); + meta.insertMeta("year", Int32Metadata(2009)); + + GridPtrVec grids; + grids.push_back(grid); + grids.push_back(grid2); + + // Register grid and transform. + GridBase::clearRegistry(); + IntGrid::registerGrid(); + FloatGrid::registerGrid(); + Metadata::clearRegistry(); + StringMetadata::registerType(); + Int32Metadata::registerType(); + // register maps + math::MapRegistry::clear(); + math::AffineMap::registerMap(); + math::ScaleMap::registerMap(); + math::UniformScaleMap::registerMap(); + math::TranslationMap::registerMap(); + math::ScaleTranslateMap::registerMap(); + math::UniformScaleTranslateMap::registerMap(); + math::NonlinearFrustumMap::registerMap(); + + // Write the vdb out to a file. + io::File vdbfile("something.vdb2"); + vdbfile.write(grids, meta); + + io::File vdbfile2("something.vdb2"); + + CPPUNIT_ASSERT_THROW(vdbfile2.hasGrid("density"), openvdb::IoError); + + vdbfile2.open(); + + CPPUNIT_ASSERT(vdbfile2.hasGrid("density")); + CPPUNIT_ASSERT(vdbfile2.hasGrid("temperature")); + CPPUNIT_ASSERT(!vdbfile2.hasGrid("Temperature")); + CPPUNIT_ASSERT(!vdbfile2.hasGrid("densitY")); + + // Clear registries. + GridBase::clearRegistry(); + Metadata::clearRegistry(); + math::MapRegistry::clear(); + + vdbfile2.close(); + + remove("something.vdb2"); +} + + +void +TestFile::testNameIterator() +{ + using namespace openvdb; + using namespace openvdb::io; + + using FloatGrid = openvdb::FloatGrid; + using FloatTree = FloatGrid::TreeType; + using IntTree = Int32Grid::TreeType; + + // Create trees. + IntTree::Ptr itree(new IntTree(1)); + itree->setValue(Coord(0, 0, 0), 5); + itree->setValue(Coord(100, 0, 0), 6); + FloatTree::Ptr ftree(new FloatTree(2.0)); + ftree->setValue(Coord(0, 0, 0), 10.0); + ftree->setValue(Coord(0, 100, 0), 11.0); + + // Create grids. + GridPtrVec grids; + GridBase::Ptr grid = createGrid(itree); + grid->setName("density"); + grids.push_back(grid); + + grid = createGrid(ftree); + grid->setName("temperature"); + grids.push_back(grid); + + // Create two unnamed grids. + grids.push_back(createGrid(ftree)); + grids.push_back(createGrid(ftree)); + + // Create two grids with the same name. + grid = createGrid(ftree); + grid->setName("level_set"); + grids.push_back(grid); + grid = createGrid(ftree); + grid->setName("level_set"); + grids.push_back(grid); + + // Register types. + openvdb::initialize(); + + const char* filename = "testNameIterator.vdb2"; + SharedPtr scopedFile(filename, ::remove); + + // Write the grids out to a file. + { + io::File vdbfile(filename); + vdbfile.write(grids); + } + + io::File vdbfile(filename); + + // Verify that name iteration fails if the file is not open. + CPPUNIT_ASSERT_THROW(vdbfile.beginName(), openvdb::IoError); + + vdbfile.open(); + + // Names should appear in lexicographic order. + Name names[6] = { "[0]", "[1]", "density", "level_set[0]", "level_set[1]", "temperature" }; + int count = 0; + for (io::File::NameIterator iter = vdbfile.beginName(); iter != vdbfile.endName(); ++iter) { + CPPUNIT_ASSERT_EQUAL(names[count], *iter); + CPPUNIT_ASSERT_EQUAL(names[count], iter.gridName()); + ++count; + grid = vdbfile.readGrid(*iter); + CPPUNIT_ASSERT(grid); + } + CPPUNIT_ASSERT_EQUAL(6, count); + + vdbfile.close(); +} + + +void +TestFile::testReadOldFileFormat() +{ + /// @todo Save some old-format (prior to OPENVDB_FILE_VERSION) .vdb2 files + /// to /work/rd/fx_tools/vdb_unittest/TestFile::testReadOldFileFormat/ + /// Verify that the files can still be read correctly. +} + + +void +TestFile::testCompression() +{ + using namespace openvdb; + using namespace openvdb::io; + + using IntGrid = openvdb::Int32Grid; + + // Register types. + openvdb::initialize(); + + // Create reference grids. + IntGrid::Ptr intGrid = IntGrid::create(/*background=*/0); + intGrid->fill(CoordBBox(Coord(0), Coord(49)), /*value=*/999, /*active=*/true); + intGrid->fill(CoordBBox(Coord(6), Coord(43)), /*value=*/0, /*active=*/false); + intGrid->fill(CoordBBox(Coord(21), Coord(22)), /*value=*/1, /*active=*/false); + intGrid->fill(CoordBBox(Coord(23), Coord(24)), /*value=*/2, /*active=*/false); + CPPUNIT_ASSERT_EQUAL(8, int(IntGrid::TreeType::LeafNodeType::DIM)); + + FloatGrid::Ptr lsGrid = createLevelSet(); + unittest_util::makeSphere(/*dim=*/Coord(100), /*ctr=*/Vec3f(50, 50, 50), /*r=*/20.0, + *lsGrid, unittest_util::SPHERE_SPARSE_NARROW_BAND); + CPPUNIT_ASSERT_EQUAL(int(GRID_LEVEL_SET), int(lsGrid->getGridClass())); + + FloatGrid::Ptr fogGrid = lsGrid->deepCopy(); + tools::sdfToFogVolume(*fogGrid); + CPPUNIT_ASSERT_EQUAL(int(GRID_FOG_VOLUME), int(fogGrid->getGridClass())); + + + GridPtrVec grids; + grids.push_back(intGrid); + grids.push_back(lsGrid); + grids.push_back(fogGrid); + + const char* filename = "testCompression.vdb2"; + SharedPtr scopedFile(filename, ::remove); + + size_t uncompressedSize = 0; + { + // Write the grids out to a file with compression disabled. + io::File vdbfile(filename); + vdbfile.setCompression(io::COMPRESS_NONE); + vdbfile.write(grids); + vdbfile.close(); + + // Get the size of the file in bytes. + struct stat buf; + buf.st_size = 0; + CPPUNIT_ASSERT_EQUAL(0, ::stat(filename, &buf)); + uncompressedSize = buf.st_size; + } + + // Write the grids out with various combinations of compression options + // and verify that they can be read back successfully. + // See io/Compression.h for the flag values. + +#ifdef OPENVDB_USE_BLOSC + std::vector validFlags{0x0,0x1,0x2,0x3,0x4,0x6}; +#else + std::vector validFlags{0x0,0x1,0x2,0x3}; +#endif + for (uint32_t flags : validFlags) { + + if (flags != io::COMPRESS_NONE) { + io::File vdbfile(filename); + vdbfile.setCompression(flags); + vdbfile.write(grids); + vdbfile.close(); + } + if (flags != io::COMPRESS_NONE) { + // Verify that the compressed file is significantly smaller than + // the uncompressed file. + size_t compressedSize = 0; + struct stat buf; + buf.st_size = 0; + CPPUNIT_ASSERT_EQUAL(0, ::stat(filename, &buf)); + compressedSize = buf.st_size; + CPPUNIT_ASSERT(compressedSize < size_t(0.75 * double(uncompressedSize))); + } + { + // Verify that the grids can be read back successfully. + + io::File vdbfile(filename); + vdbfile.open(); + + GridPtrVecPtr inGrids = vdbfile.getGrids(); + CPPUNIT_ASSERT_EQUAL(3, int(inGrids->size())); + + // Verify that the original and input grids are equal. + { + const IntGrid::Ptr grid = gridPtrCast((*inGrids)[0]); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(int(intGrid->getGridClass()), int(grid->getGridClass())); + + CPPUNIT_ASSERT(grid->tree().hasSameTopology(intGrid->tree())); + + CPPUNIT_ASSERT_EQUAL( + intGrid->tree().getValue(Coord(0)), + grid->tree().getValue(Coord(0))); + // Verify that leaf nodes with more than two distinct inactive values + // are handled correctly (FX-7085). + CPPUNIT_ASSERT_EQUAL( + intGrid->tree().getValue(Coord(6)), + grid->tree().getValue(Coord(6))); + CPPUNIT_ASSERT_EQUAL( + intGrid->tree().getValue(Coord(21)), + grid->tree().getValue(Coord(21))); + CPPUNIT_ASSERT_EQUAL( + intGrid->tree().getValue(Coord(23)), + grid->tree().getValue(Coord(23))); + + // Verify that the only active value in this grid is 999. + Int32 minVal = -1, maxVal = -1; + grid->evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(999, minVal); + CPPUNIT_ASSERT_EQUAL(999, maxVal); + } + for (int idx = 1; idx <= 2; ++idx) { + const FloatGrid::Ptr + grid = gridPtrCast((*inGrids)[idx]), + refGrid = gridPtrCast(grids[idx]); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT_EQUAL(int(refGrid->getGridClass()), int(grid->getGridClass())); + + CPPUNIT_ASSERT(grid->tree().hasSameTopology(refGrid->tree())); + + FloatGrid::ConstAccessor refAcc = refGrid->getConstAccessor(); + for (FloatGrid::ValueAllCIter it = grid->cbeginValueAll(); it; ++it) { + CPPUNIT_ASSERT_EQUAL(refAcc.getValue(it.getCoord()), *it); + } + } + } + } +} + + +//////////////////////////////////////// + + +namespace { + +using namespace openvdb; + +struct TestAsyncHelper +{ + std::set ids; + std::map filenames; + size_t refFileSize; + bool verbose; + + TestAsyncHelper(size_t _refFileSize): refFileSize(_refFileSize), verbose(false) {} + + ~TestAsyncHelper() + { + // Remove output files. + for (std::map::iterator it = filenames.begin(); + it != filenames.end(); ++it) + { + ::remove(it->second.c_str()); + } + filenames.clear(); + ids.clear(); + } + + io::Queue::Notifier notifier() + { + return std::bind(&TestAsyncHelper::validate, this, + std::placeholders::_1, std::placeholders::_2); + } + + void insert(io::Queue::Id id, const std::string& filename) + { + ids.insert(id); + filenames[id] = filename; + if (verbose) std::cerr << "queued " << filename << " as task " << id << "\n"; + } + + void validate(io::Queue::Id id, io::Queue::Status status) + { + if (verbose) { + std::ostringstream ostr; + ostr << "task " << id; + switch (status) { + case io::Queue::UNKNOWN: ostr << " is unknown"; break; + case io::Queue::PENDING: ostr << " is pending"; break; + case io::Queue::SUCCEEDED: ostr << " succeeded"; break; + case io::Queue::FAILED: ostr << " failed"; break; + } + std::cerr << ostr.str() << "\n"; + } + + if (status == io::Queue::SUCCEEDED) { + // If the task completed successfully, verify that the output file's + // size matches the reference file's size. + struct stat buf; + buf.st_size = 0; + CPPUNIT_ASSERT_EQUAL(0, ::stat(filenames[id].c_str(), &buf)); + CPPUNIT_ASSERT_EQUAL(Index64(refFileSize), Index64(buf.st_size)); + } + + if (status == io::Queue::SUCCEEDED || status == io::Queue::FAILED) { + ids.erase(id); + } + } +}; // struct TestAsyncHelper + +} // unnamed namespace + + +void +TestFile::testAsync() +{ + using namespace openvdb; + + // Register types. + openvdb::initialize(); + + // Create a grid. + FloatGrid::Ptr lsGrid = createLevelSet(); + unittest_util::makeSphere(/*dim=*/Coord(100), /*ctr=*/Vec3f(50, 50, 50), /*r=*/20.0, + *lsGrid, unittest_util::SPHERE_SPARSE_NARROW_BAND); + + MetaMap fileMetadata; + fileMetadata.insertMeta("author", StringMetadata("Einstein")); + fileMetadata.insertMeta("year", Int32Metadata(2013)); + + GridPtrVec grids; + grids.push_back(lsGrid); + grids.push_back(lsGrid->deepCopy()); + grids.push_back(lsGrid->deepCopy()); + + size_t refFileSize = 0; + { + // Write a reference file without using asynchronous I/O. + const char* filename = "testAsyncref.vdb"; + SharedPtr scopedFile(filename, ::remove); + io::File f(filename); + f.write(grids, fileMetadata); + + // Record the size of the reference file. + struct stat buf; + buf.st_size = 0; + CPPUNIT_ASSERT_EQUAL(0, ::stat(filename, &buf)); + refFileSize = buf.st_size; + } + + { + // Output multiple files using asynchronous I/O. + // Use polling to get the status of the I/O tasks. + + TestAsyncHelper helper(refFileSize); + + io::Queue queue; + for (int i = 1; i < 10; ++i) { + std::ostringstream ostr; + ostr << "testAsync." << i << ".vdb"; + const std::string filename = ostr.str(); + io::Queue::Id id = queue.write(grids, io::File(filename), fileMetadata); + helper.insert(id, filename); + } + + tbb::tick_count start = tbb::tick_count::now(); + while (!helper.ids.empty()) { + if ((tbb::tick_count::now() - start).seconds() > 60) break; // time out after 1 minute + + // Wait one second for tasks to complete. + tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1.0/*sec*/)); + + // Poll each task in the pending map. + std::set ids = helper.ids; // iterate over a copy + for (std::set::iterator it = ids.begin(); it != ids.end(); ++it) { + const io::Queue::Id id = *it; + const io::Queue::Status status = queue.status(id); + helper.validate(id, status); + } + } + CPPUNIT_ASSERT(helper.ids.empty()); + CPPUNIT_ASSERT(queue.empty()); + } + { + // Output multiple files using asynchronous I/O. + // Use notifications to get the status of the I/O tasks. + + TestAsyncHelper helper(refFileSize); + + io::Queue queue(/*capacity=*/2); + queue.addNotifier(helper.notifier()); + + for (int i = 1; i < 10; ++i) { + std::ostringstream ostr; + ostr << "testAsync" << i << ".vdb"; + const std::string filename = ostr.str(); + io::Queue::Id id = queue.write(grids, io::File(filename), fileMetadata); + helper.insert(id, filename); + } + while (!queue.empty()) { + tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1.0/*sec*/)); + } + } + { + // Test queue timeout. + + io::Queue queue(/*capacity=*/1); + queue.setTimeout(0/*sec*/); + + SharedPtr + scopedFile1("testAsyncIOa.vdb", ::remove), + scopedFile2("testAsyncIOb.vdb", ::remove); + std::ofstream + file1(scopedFile1.get()), + file2(scopedFile2.get()); + + queue.write(grids, io::Stream(file1)); + + // With the queue length restricted to 1 and the timeout to 0 seconds, + // the next write() call should time out immediately with an exception. + // (It is possible, though highly unlikely, for the previous task to complete + // in time for this write() to actually succeed.) + CPPUNIT_ASSERT_THROW(queue.write(grids, io::Stream(file2)), openvdb::RuntimeError); + + while (!queue.empty()) { + tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1.0/*sec*/)); + } + } +} + + +#ifdef OPENVDB_USE_BLOSC +// This tests for a data corruption bug that existed in versions of Blosc prior to 1.5.0 +// (see https://github.com/Blosc/c-blosc/pull/63). +void +TestFile::testBlosc() +{ + openvdb::initialize(); + + const unsigned char rawdata[] = { + 0x93, 0xb0, 0x49, 0xaf, 0x62, 0xad, 0xe3, 0xaa, 0xe4, 0xa5, 0x43, 0x20, 0x24, + 0x29, 0xc9, 0xaf, 0xee, 0xad, 0x0b, 0xac, 0x3d, 0xa8, 0x1f, 0x99, 0x53, 0x27, + 0xb6, 0x2b, 0x16, 0xb0, 0x5f, 0xae, 0x89, 0xac, 0x51, 0xa9, 0xfc, 0xa1, 0xc9, + 0x24, 0x59, 0x2a, 0x2f, 0x2d, 0xb4, 0xae, 0xeb, 0xac, 0x2f, 0xaa, 0xec, 0xa4, + 0x53, 0x21, 0x31, 0x29, 0x8f, 0x2c, 0x8e, 0x2e, 0x31, 0xad, 0xd6, 0xaa, 0x6d, + 0xa6, 0xad, 0x1b, 0x3e, 0x28, 0x0a, 0x2c, 0xfd, 0x2d, 0xf8, 0x2f, 0x45, 0xab, + 0x81, 0xa7, 0x1f, 0x95, 0x02, 0x27, 0x3d, 0x2b, 0x85, 0x2d, 0x75, 0x2f, 0xb6, + 0x30, 0x13, 0xa8, 0xb2, 0x9c, 0xf3, 0x25, 0x9c, 0x2a, 0x28, 0x2d, 0x0b, 0x2f, + 0x7b, 0x30, 0x68, 0x9e, 0x51, 0x25, 0x31, 0x2a, 0xe6, 0x2c, 0xbc, 0x2e, 0x4e, + 0x30, 0x5a, 0xb0, 0xe6, 0xae, 0x0e, 0xad, 0x59, 0xaa, 0x08, 0xa5, 0x89, 0x21, + 0x59, 0x29, 0xb0, 0x2c, 0x57, 0xaf, 0x8c, 0xad, 0x6f, 0xab, 0x65, 0xa7, 0xd3, + 0x12, 0xf5, 0x27, 0xeb, 0x2b, 0xf6, 0x2d, 0xee, 0xad, 0x27, 0xac, 0xab, 0xa8, + 0xb1, 0x9f, 0xa2, 0x25, 0xaa, 0x2a, 0x4a, 0x2d, 0x47, 0x2f, 0x7b, 0xac, 0x6d, + 0xa9, 0x45, 0xa3, 0x73, 0x23, 0x9d, 0x29, 0xb7, 0x2c, 0xa8, 0x2e, 0x51, 0x30, + 0xf7, 0xa9, 0xec, 0xa4, 0x79, 0x20, 0xc5, 0x28, 0x3f, 0x2c, 0x24, 0x2e, 0x09, + 0x30, 0xc8, 0xa5, 0xb1, 0x1c, 0x23, 0x28, 0xc3, 0x2b, 0xba, 0x2d, 0x9c, 0x2f, + 0xc3, 0x30, 0x44, 0x18, 0x6e, 0x27, 0x3d, 0x2b, 0x6b, 0x2d, 0x40, 0x2f, 0x8f, + 0x30, 0x02, 0x27, 0xed, 0x2a, 0x36, 0x2d, 0xfe, 0x2e, 0x68, 0x30, 0x66, 0xae, + 0x9e, 0xac, 0x96, 0xa9, 0x7c, 0xa3, 0xa9, 0x23, 0xc5, 0x29, 0xd8, 0x2c, 0xd7, + 0x2e, 0x0e, 0xad, 0x90, 0xaa, 0xe4, 0xa5, 0xf8, 0x1d, 0x82, 0x28, 0x2b, 0x2c, + 0x1e, 0x2e, 0x0c, 0x30, 0x53, 0xab, 0x9c, 0xa7, 0xd4, 0x96, 0xe7, 0x26, 0x30, + 0x2b, 0x7f, 0x2d, 0x6e, 0x2f, 0xb3, 0x30, 0x74, 0xa8, 0xb1, 0x9f, 0x36, 0x25, + 0x3e, 0x2a, 0xfa, 0x2c, 0xdd, 0x2e, 0x65, 0x30, 0xfc, 0xa1, 0xe0, 0x23, 0x82, + 0x29, 0x8f, 0x2c, 0x66, 0x2e, 0x23, 0x30, 0x2d, 0x22, 0xfb, 0x28, 0x3f, 0x2c, + 0x0a, 0x2e, 0xde, 0x2f, 0xaa, 0x28, 0x0a, 0x2c, 0xc8, 0x2d, 0x8f, 0x2f, 0xb0, + 0x30, 0xde, 0x2b, 0xa0, 0x2d, 0x5a, 0x2f, 0x8f, 0x30, 0x12, 0xac, 0x9d, 0xa8, + 0x0f, 0xa0, 0x51, 0x25, 0x66, 0x2a, 0x1b, 0x2d, 0x0b, 0x2f, 0x82, 0x30, 0x7b, + 0xa9, 0xea, 0xa3, 0x63, 0x22, 0x3f, 0x29, 0x7b, 0x2c, 0x60, 0x2e, 0x26, 0x30, + 0x76, 0xa5, 0xf8, 0x1d, 0x4c, 0x28, 0xeb, 0x2b, 0xce, 0x2d, 0xb0, 0x2f, 0xd3, + 0x12, 0x1d, 0x27, 0x15, 0x2b, 0x57, 0x2d, 0x2c, 0x2f, 0x85, 0x30, 0x0e, 0x26, + 0x74, 0x2a, 0xfa, 0x2c, 0xc3, 0x2e, 0x4a, 0x30, 0x08, 0x2a, 0xb7, 0x2c, 0x74, + 0x2e, 0x1d, 0x30, 0x8f, 0x2c, 0x3f, 0x2e, 0xf8, 0x2f, 0x24, 0x2e, 0xd0, 0x2f, + 0xc3, 0x30, 0xdb, 0xa6, 0xd3, 0x0e, 0x38, 0x27, 0x3d, 0x2b, 0x78, 0x2d, 0x5a, + 0x2f, 0xa3, 0x30, 0x68, 0x9e, 0x51, 0x25, 0x31, 0x2a, 0xe6, 0x2c, 0xbc, 0x2e, + 0x4e, 0x30, 0xa9, 0x23, 0x59, 0x29, 0x6e, 0x2c, 0x38, 0x2e, 0x06, 0x30, 0xb8, + 0x28, 0x10, 0x2c, 0xce, 0x2d, 0x95, 0x2f, 0xb3, 0x30, 0x9b, 0x2b, 0x7f, 0x2d, + 0x39, 0x2f, 0x7f, 0x30, 0x4a, 0x2d, 0xf8, 0x2e, 0x58, 0x30, 0xd0, 0x2e, 0x3d, + 0x30, 0x30, 0x30, 0x53, 0x21, 0xc5, 0x28, 0x24, 0x2c, 0xef, 0x2d, 0xc3, 0x2f, + 0xda, 0x27, 0x58, 0x2b, 0x6b, 0x2d, 0x33, 0x2f, 0x82, 0x30, 0x9c, 0x2a, 0x00, + 0x2d, 0xbc, 0x2e, 0x41, 0x30, 0xb0, 0x2c, 0x60, 0x2e, 0x0c, 0x30, 0x1e, 0x2e, + 0xca, 0x2f, 0xc0, 0x30, 0x95, 0x2f, 0x9f, 0x30, 0x8c, 0x30, 0x23, 0x2a, 0xc4, + 0x2c, 0x81, 0x2e, 0x23, 0x30, 0x5a, 0x2c, 0x0a, 0x2e, 0xc3, 0x2f, 0xc3, 0x30, + 0xad, 0x2d, 0x5a, 0x2f, 0x88, 0x30, 0x0b, 0x2f, 0x5b, 0x30, 0x3a, 0x30, 0x7f, + 0x2d, 0x2c, 0x2f, 0x72, 0x30, 0xc3, 0x2e, 0x37, 0x30, 0x09, 0x30, 0xb6, 0x30 + }; + + const char* indata = reinterpret_cast(rawdata); + size_t inbytes = sizeof(rawdata); + + const int + compbufbytes = int(inbytes + BLOSC_MAX_OVERHEAD), + decompbufbytes = int(inbytes + BLOSC_MAX_OVERHEAD); + + std::unique_ptr + compresseddata(new char[compbufbytes]), + outdata(new char[decompbufbytes]); + + for (int compcode = 0; compcode <= BLOSC_ZLIB; ++compcode) { + char* compname = nullptr; +#if BLOSC_VERSION_MAJOR > 1 || (BLOSC_VERSION_MAJOR == 1 && BLOSC_VERSION_MINOR >= 15) + if (0 > blosc_compcode_to_compname(compcode, const_cast(&compname))) +#else + if (0 > blosc_compcode_to_compname(compcode, &compname)) +#endif + continue; + /// @todo This changes the compressor setting globally. + if (blosc_set_compressor(compname) < 0) continue; + + for (int typesize = 1; typesize <= 4; ++typesize) { + + // Compress the data. + ::memset(compresseddata.get(), 0, compbufbytes); + int compressedbytes = blosc_compress( + /*clevel=*/9, + /*doshuffle=*/true, + typesize, + /*srcsize=*/inbytes, + /*src=*/indata, + /*dest=*/compresseddata.get(), + /*destsize=*/compbufbytes); + + CPPUNIT_ASSERT(compressedbytes > 0); + + // Decompress the data. + ::memset(outdata.get(), 0, decompbufbytes); + int outbytes = blosc_decompress( + compresseddata.get(), outdata.get(), decompbufbytes); + + CPPUNIT_ASSERT(outbytes > 0); + CPPUNIT_ASSERT_EQUAL(int(inbytes), outbytes); + + // Compare original and decompressed data. + int diff = 0; + for (size_t i = 0; i < inbytes; ++i) { + if (outdata[i] != indata[i]) ++diff; + } + if (diff > 0) { + const char* mesg = "Your version of the Blosc library is most likely" + " out of date; please install the latest version. " + "(Earlier versions have a bug that can cause data corruption.)"; + CPPUNIT_ASSERT_MESSAGE(mesg, diff == 0); + return; + } + } + } +} +#endif + + +void +TestFile::testDelayedLoadMetadata() +{ + openvdb::initialize(); + + using namespace openvdb; + + io::File file("something.vdb2"); + + // Create a level set grid. + auto lsGrid = createLevelSet(); + lsGrid->setName("sphere"); + unittest_util::makeSphere(/*dim=*/Coord(100), /*ctr=*/Vec3f(50, 50, 50), /*r=*/20.0, + *lsGrid, unittest_util::SPHERE_SPARSE_NARROW_BAND); + + // Write the VDB to a string stream. + std::ostringstream ostr(std::ios_base::binary); + + // Create the grid descriptor out of this grid. + io::GridDescriptor gd(Name("sphere"), lsGrid->type()); + + // Write out the grid. + file.writeGrid(gd, lsGrid, ostr, /*seekable=*/true); + + // Duplicate VDB string stream. + std::ostringstream ostr2(std::ios_base::binary); + + { // Read back in, clip and write out again to verify metadata is rebuilt. + std::istringstream istr(ostr.str(), std::ios_base::binary); + io::setVersion(istr, file.libraryVersion(), file.fileVersion()); + + io::GridDescriptor gd2; + GridBase::Ptr grid = gd2.read(istr); + gd2.seekToGrid(istr); + + const BBoxd clipBbox(Vec3d(-10.0,-10.0,-10.0), Vec3d(10.0,10.0,10.0)); + io::Archive::readGrid(grid, gd2, istr, clipBbox); + + // Verify clipping is working as expected. + CPPUNIT_ASSERT(grid->baseTreePtr()->leafCount() < lsGrid->tree().leafCount()); + + file.writeGrid(gd, grid, ostr2, /*seekable=*/true); + } + + // Since the input is only a fragment of a VDB file (in particular, + // it doesn't have a header), set the file format version number explicitly. + // On read, the delayed load metadata for OpenVDB library versions less than 6.1 + // should be removed to ensure correctness as it possible for the metadata to + // have been treated as unknown and blindly copied over when read and re-written + // using this library version resulting in out-of-sync metadata. + + // By default, DelayedLoadMetadata is dropped from the grid during read so + // as not to be exposed to the user. + + { // read using current library version + std::istringstream istr(ostr2.str(), std::ios_base::binary); + io::setVersion(istr, file.libraryVersion(), file.fileVersion()); + + io::GridDescriptor gd2; + GridBase::Ptr grid = gd2.read(istr); + gd2.seekToGrid(istr); + io::Archive::readGrid(grid, gd2, istr); + + CPPUNIT_ASSERT(!((*grid)[GridBase::META_FILE_DELAYED_LOAD])); + } + + // To test the version mechanism, a stream metadata object is created with + // a non-zero test value and set on the input stream. This disables the + // behaviour where the DelayedLoadMetadata is dropped from the grid. + + io::StreamMetadata::Ptr streamMetadata(new io::StreamMetadata); + streamMetadata->__setTest(uint32_t(1)); + + { // read using current library version + std::istringstream istr(ostr2.str(), std::ios_base::binary); + io::setVersion(istr, file.libraryVersion(), file.fileVersion()); + io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false); + + io::GridDescriptor gd2; + GridBase::Ptr grid = gd2.read(istr); + gd2.seekToGrid(istr); + io::Archive::readGrid(grid, gd2, istr); + + CPPUNIT_ASSERT(((*grid)[GridBase::META_FILE_DELAYED_LOAD])); + } + + { // read using library version of 5.0 + std::istringstream istr(ostr2.str(), std::ios_base::binary); + io::setVersion(istr, VersionId(5,0), file.fileVersion()); + io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false); + + io::GridDescriptor gd2; + GridBase::Ptr grid = gd2.read(istr); + gd2.seekToGrid(istr); + io::Archive::readGrid(grid, gd2, istr); + + CPPUNIT_ASSERT(!((*grid)[GridBase::META_FILE_DELAYED_LOAD])); + } + + { // read using library version of 4.9 + std::istringstream istr(ostr2.str(), std::ios_base::binary); + io::setVersion(istr, VersionId(4,9), file.fileVersion()); + io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false); + + io::GridDescriptor gd2; + GridBase::Ptr grid = gd2.read(istr); + gd2.seekToGrid(istr); + io::Archive::readGrid(grid, gd2, istr); + + CPPUNIT_ASSERT(!((*grid)[GridBase::META_FILE_DELAYED_LOAD])); + } + + { // read using library version of 6.1 + std::istringstream istr(ostr2.str(), std::ios_base::binary); + io::setVersion(istr, VersionId(6,1), file.fileVersion()); + io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false); + + io::GridDescriptor gd2; + GridBase::Ptr grid = gd2.read(istr); + gd2.seekToGrid(istr); + io::Archive::readGrid(grid, gd2, istr); + + CPPUNIT_ASSERT(!((*grid)[GridBase::META_FILE_DELAYED_LOAD])); + } + + { // read using library version of 6.2 + std::istringstream istr(ostr2.str(), std::ios_base::binary); + io::setVersion(istr, VersionId(6,2), file.fileVersion()); + io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false); + + io::GridDescriptor gd2; + GridBase::Ptr grid = gd2.read(istr); + gd2.seekToGrid(istr); + io::Archive::readGrid(grid, gd2, istr); + + CPPUNIT_ASSERT(((*grid)[GridBase::META_FILE_DELAYED_LOAD])); + } + + remove("something.vdb2"); +} diff --git a/openvdb/unittest/TestFindActiveValues.cc b/openvdb/unittest/TestFindActiveValues.cc new file mode 100644 index 00000000..94aa3022 --- /dev/null +++ b/openvdb/unittest/TestFindActiveValues.cc @@ -0,0 +1,305 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Ken Museth +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// + +#include // for remove() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + + +class TestFindActiveValues: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestFindActiveValues); + CPPUNIT_TEST(testBasic); + CPPUNIT_TEST(testSphere1); + CPPUNIT_TEST(testSphere2); + CPPUNIT_TEST(testSparseBox); + CPPUNIT_TEST(testDenseBox); + CPPUNIT_TEST(testBenchmarks); + CPPUNIT_TEST_SUITE_END(); + + void testBasic(); + void testSphere1(); + void testSphere2(); + void testSparseBox(); + void testDenseBox(); + void testBenchmarks(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestFindActiveValues); + +void +TestFindActiveValues::testBasic() +{ + const float background = 5.0f; + openvdb::FloatTree tree(background); + const openvdb::Coord min(-1,-2,30), max(20,30,55); + const openvdb::CoordBBox bbox(min[0], min[1], min[2], + max[0], max[1], max[2]); + + CPPUNIT_ASSERT(openvdb::tools::noActiveValues(tree, bbox)); + + tree.setValue(min.offsetBy(-1), 1.0f); + CPPUNIT_ASSERT(openvdb::tools::noActiveValues(tree, bbox)); + tree.setValue(max.offsetBy( 1), 1.0f); + CPPUNIT_ASSERT(openvdb::tools::noActiveValues(tree, bbox)); + tree.setValue(min, 1.0f); + CPPUNIT_ASSERT(openvdb::tools::anyActiveValues(tree, bbox)); + tree.setValue(max, 1.0f); + CPPUNIT_ASSERT(openvdb::tools::anyActiveValues(tree, bbox)); +} + +void +TestFindActiveValues::testSphere1() +{ + const openvdb::Vec3f center(0.5f, 0.5f, 0.5f); + const float radius = 0.3f; + const int dim = 100, half_width = 3; + const float voxel_size = 1.0f/dim; + + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(/*background=*/half_width*voxel_size); + const openvdb::FloatTree& tree = grid->tree(); + grid->setTransform(openvdb::math::Transform::createLinearTransform(/*voxel size=*/voxel_size)); + unittest_util::makeSphere( + openvdb::Coord(dim), center, radius, *grid, unittest_util::SPHERE_SPARSE_NARROW_BAND); + + const int c = int(0.5f/voxel_size); + const openvdb::CoordBBox a(openvdb::Coord(c), openvdb::Coord(c+ 8)); + CPPUNIT_ASSERT(!tree.isValueOn(openvdb::Coord(c))); + CPPUNIT_ASSERT(!openvdb::tools::anyActiveValues(tree, a)); + + const openvdb::Coord d(c + int(radius/voxel_size), c, c); + CPPUNIT_ASSERT(tree.isValueOn(d)); + const auto b = openvdb::CoordBBox::createCube(d, 4); + CPPUNIT_ASSERT(openvdb::tools::anyActiveValues(tree, b)); + + const openvdb::CoordBBox e(openvdb::Coord(0), openvdb::Coord(dim)); + CPPUNIT_ASSERT(openvdb::tools::anyActiveValues(tree, e)); +} + +void +TestFindActiveValues::testSphere2() +{ + const openvdb::Vec3f center(0.0f); + const float radius = 0.5f; + const int dim = 400, halfWidth = 3; + const float voxelSize = 2.0f/dim; + auto grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); + openvdb::FloatTree& tree = grid->tree(); + + {//test center + const openvdb::CoordBBox bbox(openvdb::Coord(0), openvdb::Coord(8)); + CPPUNIT_ASSERT(!tree.isValueOn(openvdb::Coord(0))); + //openvdb::util::CpuTimer timer("\ncenter"); + CPPUNIT_ASSERT(!openvdb::tools::anyActiveValues(tree, bbox)); + //timer.stop(); + } + {//test on sphere + const openvdb::Coord d(int(radius/voxelSize), 0, 0); + CPPUNIT_ASSERT(tree.isValueOn(d)); + const auto bbox = openvdb::CoordBBox::createCube(d, 4); + //openvdb::util::CpuTimer timer("\non sphere"); + CPPUNIT_ASSERT(openvdb::tools::anyActiveValues(tree, bbox)); + //timer.stop(); + } + {//test full domain + const openvdb::CoordBBox bbox(openvdb::Coord(-4000), openvdb::Coord(4000)); + //openvdb::util::CpuTimer timer("\nfull domain"); + CPPUNIT_ASSERT(openvdb::tools::anyActiveValues(tree, bbox)); + //timer.stop(); + openvdb::tools::FindActiveValues op(tree); + CPPUNIT_ASSERT(op.count(bbox) == tree.activeVoxelCount()); + } + {// find largest inscribed cube in index space containing NO active values + openvdb::tools::FindActiveValues op(tree); + auto bbox = openvdb::CoordBBox::createCube(openvdb::Coord(0), 1); + //openvdb::util::CpuTimer timer("\nInscribed cube (class)"); + int count = 0; + while(op.none(bbox)) { + ++count; + bbox.expand(1); + } + //const double t = timer.stop(); + //std::cerr << "Inscribed bbox = " << bbox << std::endl; + const int n = int(openvdb::math::Sqrt(openvdb::math::Pow2(radius-halfWidth*voxelSize)/3.0f)/voxelSize) + 1; + //std::cerr << "n=" << n << std::endl; + CPPUNIT_ASSERT( bbox.max() == openvdb::Coord( n)); + CPPUNIT_ASSERT( bbox.min() == openvdb::Coord(-n)); + //openvdb::util::printTime(std::cerr, t/count, "time per lookup ", "\n", true, 4, 3); + } + {// find largest inscribed cube in index space containing NO active values + auto bbox = openvdb::CoordBBox::createCube(openvdb::Coord(0), 1); + //openvdb::util::CpuTimer timer("\nInscribed cube (func)"); + int count = 0; + while(!openvdb::tools::anyActiveValues(tree, bbox)) { + bbox.expand(1); + ++count; + } + //const double t = timer.stop(); + //std::cerr << "Inscribed bbox = " << bbox << std::endl; + const int n = int(openvdb::math::Sqrt(openvdb::math::Pow2(radius-halfWidth*voxelSize)/3.0f)/voxelSize) + 1; + //std::cerr << "n=" << n << std::endl; + //openvdb::util::printTime(std::cerr, t/count, "time per lookup ", "\n", true, 4, 3); + CPPUNIT_ASSERT( bbox.max() == openvdb::Coord( n)); + CPPUNIT_ASSERT( bbox.min() == openvdb::Coord(-n)); + } +} + +void +TestFindActiveValues::testSparseBox() +{ + {//test active tiles in a sparsely filled box + const int half_dim = 256; + const openvdb::CoordBBox bbox(openvdb::Coord(-half_dim), openvdb::Coord(half_dim)); + openvdb::FloatTree tree; + CPPUNIT_ASSERT(tree.activeTileCount() == 0); + CPPUNIT_ASSERT(tree.getValueDepth(openvdb::Coord(0)) == -1);//background value + openvdb::tools::FindActiveValues op(tree); + tree.sparseFill(bbox, 1.0f, true); + op.update(tree);//tree was modified so op needs to be updated + CPPUNIT_ASSERT(tree.activeTileCount() > 0); + CPPUNIT_ASSERT(tree.getValueDepth(openvdb::Coord(0)) == 1);//upper internal tile value + for (int i=1; i op(tree); + CPPUNIT_ASSERT(tree.getValueDepth(openvdb::Coord(0)) == 3);// leaf value + for (int i=1; i op(tree); + //double t = 0.0; + //util::CpuTimer timer; + for (auto b = CoordBBox::createCube(Coord(-half_dim), bbox_size); true; b.translate(Coord(1))) { + //timer.restart(); + bool test = op.any(b); + //t = std::max(t, timer.restart()); + if (!test) break; + } + //std::cout << "\n*The slowest sparse test " << t << " milliseconds\n"; + CPPUNIT_ASSERT(op.count(bbox) == bbox.volume()); + } + {//benchmark test against active voxels in a densely filled box + using namespace openvdb; + const int half_dim = 256, bbox_size = 1; + const CoordBBox bbox(Coord(-half_dim), Coord(half_dim)); + FloatTree tree; + tree.denseFill(bbox, 1.0f, true); + tools::FindActiveValues op(tree); + //double t = 0.0; + //openvdb::util::CpuTimer timer; + for (auto b = CoordBBox::createCube(Coord(-half_dim), bbox_size); true; b.translate(Coord(1))) { + //timer.restart(); + bool test = op.any(b); + //t = std::max(t, timer.restart()); + if (!test) break; + } + //std::cout << "*The slowest dense test " << t << " milliseconds\n"; + CPPUNIT_ASSERT(op.count(bbox) == bbox.volume()); + } + {//benchmark test against active voxels in a densely filled box + using namespace openvdb; + FloatTree tree; + tree.denseFill(CoordBBox::createCube(Coord(0), 256), 1.0f, true); + tools::FindActiveValues op(tree); + //openvdb::util::CpuTimer timer("new test"); + CPPUNIT_ASSERT(op.none(CoordBBox::createCube(Coord(256), 1))); + //timer.stop(); + } +} + +// Copyright (c) Ken Museth +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/openvdb/unittest/TestFloatMetadata.cc b/openvdb/unittest/TestFloatMetadata.cc new file mode 100644 index 00000000..b6ff5a63 --- /dev/null +++ b/openvdb/unittest/TestFloatMetadata.cc @@ -0,0 +1,46 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + + +class TestFloatMetadata : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestFloatMetadata); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); + + void test(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestFloatMetadata); + +void +TestFloatMetadata::test() +{ + using namespace openvdb; + + Metadata::Ptr m(new FloatMetadata(1.0)); + Metadata::Ptr m2 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0); + + CPPUNIT_ASSERT(m->typeName().compare("float") == 0); + CPPUNIT_ASSERT(m2->typeName().compare("float") == 0); + + FloatMetadata *s = dynamic_cast(m.get()); + //CPPUNIT_ASSERT(s->value() == 1.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0f,s->value(),0); + s->value() = 2.0; + //CPPUNIT_ASSERT(s->value() == 2.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0f,s->value(),0); + m2->copy(*s); + + s = dynamic_cast(m2.get()); + //CPPUNIT_ASSERT(s->value() == 2.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0f,s->value(),0); +} diff --git a/openvdb/unittest/TestGradient.cc b/openvdb/unittest/TestGradient.cc new file mode 100644 index 00000000..3fc711a2 --- /dev/null +++ b/openvdb/unittest/TestGradient.cc @@ -0,0 +1,786 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() +#include +#include + + +class TestGradient: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestGradient); + CPPUNIT_TEST(testISGradient); // gradient in index space + CPPUNIT_TEST(testISGradientStencil); + CPPUNIT_TEST(testWSGradient); // gradient in world space + CPPUNIT_TEST(testWSGradientStencil); + CPPUNIT_TEST(testWSGradientStencilFrustum); + CPPUNIT_TEST(testWSGradientNormSqr); // gradient norm sqr (world space only) + CPPUNIT_TEST(testWSGradientNormSqrStencil); // gradient norm sqr (world space only) + CPPUNIT_TEST(testGradientTool); // gradient tool + CPPUNIT_TEST(testGradientMaskedTool); // gradient tool + CPPUNIT_TEST(testIntersectsIsoValue); // zero-crossing + CPPUNIT_TEST(testOldStyleStencils); // old stencil impl - deprecate + + CPPUNIT_TEST_SUITE_END(); + + void testISGradient(); + void testISGradientStencil(); + void testWSGradient(); + void testWSGradientStencilFrustum(); + void testWSGradientStencil(); + void testWSGradientNormSqr(); + void testWSGradientNormSqrStencil(); + void testGradientTool(); + void testGradientMaskedTool(); + void testIntersectsIsoValue(); + void testOldStyleStencils(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestGradient); + + +void +TestGradient::testISGradient() +{ + using namespace openvdb; + + using AccessorType = FloatGrid::ConstAccessor; + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f ,30.0f, 40.0f); + const float radius=10.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + const Coord xyz(10, 20, 30); + + + // Index Space Gradients: random access and stencil version + AccessorType inAccessor = grid->getConstAccessor(); + Vec3f result; + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.02); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.02); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); +} + + +void +TestGradient::testISGradientStencil() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f ,30.0f, 40.0f); + const float radius = 10.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + const Coord xyz(10, 20, 30); + + + // Index Space Gradients: stencil version + Vec3f result; + // this stencil is large enough for all thie different schemes used + // in this test + math::NineteenPointStencil stencil(*grid); + stencil.moveTo(xyz); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.02); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.02); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::ISGradient::result(stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); +} + + +void +TestGradient::testWSGradient() +{ + using namespace openvdb; + + using AccessorType = FloatGrid::ConstAccessor; + + double voxel_size = 0.5; + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(voxel_size)); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius = 10.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + const Coord xyz(11, 17, 26); + + AccessorType inAccessor = grid->getConstAccessor(); + // try with a map + + // Index Space Gradients: stencil version + Vec3f result; + math::MapBase::Ptr rotated_map; + { + math::UniformScaleMap map(voxel_size); + result = math::Gradient::result( + map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + rotated_map = map.preRotate(1.5, math::X_AXIS); + // verify the new map is an affine map + CPPUNIT_ASSERT(rotated_map->type() == math::AffineMap::mapType()); + math::AffineMap::Ptr affine_map = + StaticPtrCast(rotated_map); + // the gradient should have the same length even after rotation + result = math::Gradient::result( + *affine_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + result = math::Gradient::result( + *affine_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + math::UniformScaleTranslateMap map(voxel_size, Vec3d(0,0,0)); + result = math::Gradient::result( + map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + math::ScaleTranslateMap map(Vec3d(voxel_size, voxel_size, voxel_size), Vec3d(0,0,0)); + result = math::Gradient::result( + map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + + { + // this map has no scale, expect result/voxel_spaceing = 1 + math::TranslationMap map; + result = math::Gradient::result(map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(voxel_size, result.length(), /*tolerance=*/0.01); + } + + { + // test the GenericMap Grid interface + math::GenericMap generic_map(*grid); + result = math::Gradient::result( + generic_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + // test the GenericMap Transform interface + math::GenericMap generic_map(grid->transform()); + result = math::Gradient::result( + generic_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + // test the GenericMap Map interface + math::GenericMap generic_map(rotated_map); + result = math::Gradient::result( + generic_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + // test a map with non-uniform SCALING AND ROTATION + Vec3d voxel_sizes(0.25, 0.45, 0.75); + math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes)); + // apply rotation + rotated_map = base_map->preRotate(1.5, math::X_AXIS); + grid->setTransform(math::Transform::Ptr(new math::Transform(rotated_map))); + // remake the sphere + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + math::AffineMap::Ptr affine_map = + StaticPtrCast(rotated_map); + + // math::ScaleMap map(voxel_sizes); + result = math::Gradient::result( + *affine_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + // test a map with non-uniform SCALING + Vec3d voxel_sizes(0.25, 0.45, 0.75); + math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes)); + grid->setTransform(math::Transform::Ptr(new math::Transform(base_map))); + // remake the sphere + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + math::ScaleMap::Ptr scale_map = StaticPtrCast(base_map); + + // math::ScaleMap map(voxel_sizes); + result = math::Gradient::result(*scale_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } +} + +void +TestGradient::testWSGradientStencilFrustum() +{ + using namespace openvdb; + + // Construct a frustum that matches the one in TestMaps::testFrustum() + + openvdb::BBoxd bbox(Vec3d(0), Vec3d(100)); + math::NonlinearFrustumMap frustum(bbox, 1./6., 5); + /// frustum will have depth, far plane - near plane = 5 + /// the frustum has width 1 in the front and 6 in the back + + Vec3d trans(2,2,2); + math::NonlinearFrustumMap::Ptr map = + StaticPtrCast( + frustum.preScale(Vec3d(10,10,10))->postTranslate(trans)); + + + // Create a grid with this frustum + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/0.f); + math::Transform::Ptr transform = math::Transform::Ptr( new math::Transform(map)); + grid->setTransform(transform); + + FloatGrid::Accessor acc = grid->getAccessor(); + // Totally fill the interior of the frustum with word space distances + // from its center. + + + math::Vec3d isCenter(.5 * 101, .5 * 101, .5 * 101); + math::Vec3d wsCenter = map->applyMap(isCenter); + + math::Coord ijk; + + // convert to IntType + Vec3i min(bbox.min()); + Vec3i max = Vec3i(bbox.max()) + Vec3i(1, 1, 1); + + for (ijk[0] = min.x(); ijk[0] < max.x(); ++ijk[0]) { + for (ijk[1] = min.y(); ijk[1] < max.y(); ++ijk[1]) { + for (ijk[2] = min.z(); ijk[2] < max.z(); ++ijk[2]) { + const math::Vec3d wsLocation = transform->indexToWorld(ijk); + const float dis = float((wsLocation - wsCenter).length()); + + acc.setValue(ijk, dis); + } + } + } + + + { + // test at location 10, 10, 10 in index space + math::Coord xyz(10, 10, 10); + + math::Vec3s result = + math::Gradient::result(*map, acc, xyz); + + // The Gradient should be unit lenght for this case + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + math::Vec3d wsVec = transform->indexToWorld(xyz); + math::Vec3d direction = (wsVec - wsCenter); + direction.normalize(); + + // test the actual direction of the gradient + CPPUNIT_ASSERT(direction.eq(result, 0.01 /*tolerance*/)); + } + + { + // test at location 30, 30, 60 in index space + math::Coord xyz(30, 30, 60); + + math::Vec3s result = + math::Gradient::result(*map, acc, xyz); + + // The Gradient should be unit lenght for this case + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + math::Vec3d wsVec = transform->indexToWorld(xyz); + math::Vec3d direction = (wsVec - wsCenter); + direction.normalize(); + + // test the actual direction of the gradient + CPPUNIT_ASSERT(direction.eq(result, 0.01 /*tolerance*/)); + } +} + + + +void +TestGradient::testWSGradientStencil() +{ + using namespace openvdb; + + double voxel_size = 0.5; + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(voxel_size)); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f ,10.0f);//i.e. (12,16,20) in index space + const float radius = 10; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + const Coord xyz(11, 17, 26); + + // try with a map + math::SevenPointStencil stencil(*grid); + stencil.moveTo(xyz); + + math::SecondOrderDenseStencil dense_2ndOrder(*grid); + dense_2ndOrder.moveTo(xyz); + + math::FourthOrderDenseStencil dense_4thOrder(*grid); + dense_4thOrder.moveTo(xyz); + + Vec3f result; + math::MapBase::Ptr rotated_map; + { + math::UniformScaleMap map(voxel_size); + result = math::Gradient::result( + map, stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + rotated_map = map.preRotate(1.5, math::X_AXIS); + // verify the new map is an affine map + CPPUNIT_ASSERT(rotated_map->type() == math::AffineMap::mapType()); + math::AffineMap::Ptr affine_map = + StaticPtrCast(rotated_map); + // the gradient should have the same length even after rotation + + result = math::Gradient::result( + *affine_map, dense_2ndOrder); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + + result = math::Gradient::result( + *affine_map, dense_4thOrder); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + math::UniformScaleTranslateMap map(voxel_size, Vec3d(0,0,0)); + + result = math::Gradient::result(map, stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + math::ScaleTranslateMap map(Vec3d(voxel_size, voxel_size, voxel_size), Vec3d(0,0,0)); + result = math::Gradient::result(map, stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + math::TranslationMap map; + result = math::Gradient::result(map, stencil); + // value = 1 because the translation map assumes uniform spacing + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, result.length(), /*tolerance=*/0.01); + } + { + // test the GenericMap Grid interface + math::GenericMap generic_map(*grid); + result = math::Gradient::result( + generic_map, dense_2ndOrder); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + // test the GenericMap Transform interface + math::GenericMap generic_map(grid->transform()); + result = math::Gradient::result( + generic_map, dense_2ndOrder); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + // test the GenericMap Map interface + math::GenericMap generic_map(rotated_map); + result = math::Gradient::result( + generic_map, dense_2ndOrder); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + // test a map with non-uniform SCALING AND ROTATION + Vec3d voxel_sizes(0.25, 0.45, 0.75); + math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes)); + // apply rotation + rotated_map = base_map->preRotate(1.5, math::X_AXIS); + grid->setTransform(math::Transform::Ptr(new math::Transform(rotated_map))); + // remake the sphere + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + math::AffineMap::Ptr affine_map = + StaticPtrCast(rotated_map); + + stencil.moveTo(xyz); + result = math::Gradient::result(*affine_map, stencil); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } + { + // test a map with NON-UNIFORM SCALING + Vec3d voxel_sizes(0.5, 1.0, 0.75); + math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes)); + grid->setTransform(math::Transform::Ptr(new math::Transform(base_map))); + // remake the sphere + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + math::ScaleMap map(voxel_sizes); + dense_2ndOrder.moveTo(xyz); + + result = math::Gradient::result(map, dense_2ndOrder); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, result.length(), /*tolerance=*/0.01); + } +} + + +void +TestGradient::testWSGradientNormSqr() +{ + using namespace openvdb; + + using AccessorType = FloatGrid::ConstAccessor; + double voxel_size = 0.5; + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(voxel_size)); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f,8.0f,10.0f);//i.e. (12,16,20) in index space + const float radius = 10.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + const Coord xyz(11, 17, 26); + + AccessorType inAccessor = grid->getConstAccessor(); + + // test gradient in index and world space using the 7-pt stencil + math::UniformScaleMap uniform_scale(voxel_size); + FloatTree::ValueType normsqrd; + normsqrd = math::GradientNormSqrd::result( + uniform_scale, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, normsqrd, /*tolerance=*/0.07); + + // test world space using the 13pt stencil + normsqrd = math::GradientNormSqrd::result( + uniform_scale, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, normsqrd, /*tolerance=*/0.05); + + math::AffineMap affine(voxel_size*math::Mat3d::identity()); + normsqrd = math::GradientNormSqrd::result( + affine, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, normsqrd, /*tolerance=*/0.07); + + normsqrd = math::GradientNormSqrd::result( + uniform_scale, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, normsqrd, /*tolerance=*/0.05); +} + + +void +TestGradient::testWSGradientNormSqrStencil() +{ + using namespace openvdb; + + double voxel_size = 0.5; + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(voxel_size)); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius = 10.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + const Coord xyz(11, 17, 26); + + math::SevenPointStencil sevenpt(*grid); + sevenpt.moveTo(xyz); + + math::ThirteenPointStencil thirteenpt(*grid); + thirteenpt.moveTo(xyz); + + math::SecondOrderDenseStencil dense_2ndOrder(*grid); + dense_2ndOrder.moveTo(xyz); + + math::NineteenPointStencil nineteenpt(*grid); + nineteenpt.moveTo(xyz); + + // test gradient in index and world space using the 7-pt stencil + math::UniformScaleMap uniform_scale(voxel_size); + FloatTree::ValueType normsqrd; + normsqrd = math::GradientNormSqrd::result( + uniform_scale, sevenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, normsqrd, /*tolerance=*/0.07); + + + // test gradient in index and world space using the 13pt stencil + normsqrd = math::GradientNormSqrd::result( + uniform_scale, thirteenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, normsqrd, /*tolerance=*/0.05); + + math::AffineMap affine(voxel_size*math::Mat3d::identity()); + normsqrd = math::GradientNormSqrd::result( + affine, dense_2ndOrder); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, normsqrd, /*tolerance=*/0.07); + + normsqrd = math::GradientNormSqrd::result( + uniform_scale, nineteenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, normsqrd, /*tolerance=*/0.05); +} + + +void +TestGradient::testGradientTool() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64, 64, 64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f); + const float radius = 10.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + const Coord xyz(10, 20, 30); + + Vec3SGrid::Ptr grad = tools::gradient(*grid); + CPPUNIT_ASSERT_EQUAL(int(tree.activeVoxelCount()), int(grad->activeVoxelCount())); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, grad->getConstAccessor().getValue(xyz).length(), + /*tolerance=*/0.01); +} + + +void +TestGradient::testGradientMaskedTool() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64, 64, 64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f); + const float radius = 10.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + const openvdb::CoordBBox maskbbox(openvdb::Coord(35, 30, 30), openvdb::Coord(41, 41, 41)); + BoolGrid::Ptr maskGrid = BoolGrid::create(false); + maskGrid->fill(maskbbox, true/*value*/, true/*activate*/); + + Vec3SGrid::Ptr grad = tools::gradient(*grid, *maskGrid); + {// outside the masked region + const Coord xyz(10, 20, 30); + CPPUNIT_ASSERT(!maskbbox.isInside(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, grad->getConstAccessor().getValue(xyz).length(), + /*tolerance=*/0.01); + } + {// inside the masked region + const Coord xyz(38, 35, 33); + CPPUNIT_ASSERT(maskbbox.isInside(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, grad->getConstAccessor().getValue(xyz).length(), + /*tolerance=*/0.01); + } +} + + +void +TestGradient::testIntersectsIsoValue() +{ + using namespace openvdb; + + {// test zero crossing in -x + FloatGrid grid(/*backgroundValue=*/5.0); + FloatTree& tree = grid.tree(); + Coord xyz(2,-5,60); + tree.setValue(xyz, 1.3f); + tree.setValue(xyz.offsetBy(-1,0,0), -2.0f); + math::SevenPointStencil stencil(grid); + stencil.moveTo(xyz); + CPPUNIT_ASSERT( stencil.intersects( )); + CPPUNIT_ASSERT( stencil.intersects( 0.0f)); + CPPUNIT_ASSERT( stencil.intersects( 2.0f)); + CPPUNIT_ASSERT(!stencil.intersects( 5.5f)); + CPPUNIT_ASSERT(!stencil.intersects(-2.5f)); + } + {// test zero crossing in +x + FloatGrid grid(/*backgroundValue=*/5.0); + FloatTree& tree = grid.tree(); + Coord xyz(2,-5,60); + tree.setValue(xyz, 1.3f); + tree.setValue(xyz.offsetBy(1,0,0), -2.0f); + math::SevenPointStencil stencil(grid); + stencil.moveTo(xyz); + CPPUNIT_ASSERT(stencil.intersects()); + } + {// test zero crossing in -y + FloatGrid grid(/*backgroundValue=*/5.0); + FloatTree& tree = grid.tree(); + Coord xyz(2,-5,60); + tree.setValue(xyz, 1.3f); + tree.setValue(xyz.offsetBy(0,-1,0), -2.0f); + math::SevenPointStencil stencil(grid); + stencil.moveTo(xyz); + CPPUNIT_ASSERT(stencil.intersects()); + } + {// test zero crossing in y + FloatGrid grid(/*backgroundValue=*/5.0); + FloatTree& tree = grid.tree(); + Coord xyz(2,-5,60); + tree.setValue(xyz, 1.3f); + tree.setValue(xyz.offsetBy(0,1,0), -2.0f); + math::SevenPointStencil stencil(grid); + stencil.moveTo(xyz); + CPPUNIT_ASSERT(stencil.intersects()); + } + {// test zero crossing in -z + FloatGrid grid(/*backgroundValue=*/5.0); + FloatTree& tree = grid.tree(); + Coord xyz(2,-5,60); + tree.setValue(xyz, 1.3f); + tree.setValue(xyz.offsetBy(0,0,-1), -2.0f); + math::SevenPointStencil stencil(grid); + stencil.moveTo(xyz); + CPPUNIT_ASSERT(stencil.intersects()); + } + {// test zero crossing in z + FloatGrid grid(/*backgroundValue=*/5.0); + FloatTree& tree = grid.tree(); + Coord xyz(2,-5,60); + tree.setValue(xyz, 1.3f); + tree.setValue(xyz.offsetBy(0,0,1), -2.0f); + math::SevenPointStencil stencil(grid); + stencil.moveTo(xyz); + CPPUNIT_ASSERT(stencil.intersects()); + } + {// test zero crossing in -x & z + FloatGrid grid(/*backgroundValue=*/5.0); + FloatTree& tree = grid.tree(); + Coord xyz(2,-5,60); + tree.setValue(xyz, 1.3f); + tree.setValue(xyz.offsetBy(-1,0,1), -2.0f); + math::SevenPointStencil stencil(grid); + stencil.moveTo(xyz); + CPPUNIT_ASSERT(!stencil.intersects()); + } + {// test zero multiple crossings + FloatGrid grid(/*backgroundValue=*/5.0); + FloatTree& tree = grid.tree(); + Coord xyz(2,-5,60); + tree.setValue(xyz, 1.3f); + tree.setValue(xyz.offsetBy(-1, 0, 1), -1.0f); + tree.setValue(xyz.offsetBy( 0, 0, 1), -2.0f); + tree.setValue(xyz.offsetBy( 0, 1, 0), -3.0f); + tree.setValue(xyz.offsetBy( 0, 0,-1), -2.0f); + math::SevenPointStencil stencil(grid); + stencil.moveTo(xyz); + CPPUNIT_ASSERT(stencil.intersects()); + } +} + + +void +TestGradient::testOldStyleStencils() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(/*voxel size=*/0.5)); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f,8.0f,10.0f);//i.e. (12,16,20) in index space + const float radius=10.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + const Coord xyz(11, 17, 26); + + math::GradStencil gs(*grid); + gs.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, gs.gradient().length(), /*tolerance=*/0.01); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, gs.normSqGrad(), /*tolerance=*/0.10); + + math::WenoStencil ws(*grid); + ws.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, ws.gradient().length(), /*tolerance=*/0.01); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, ws.normSqGrad(), /*tolerance=*/0.01); + + math::CurvatureStencil cs(*grid); + cs.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, cs.gradient().length(), /*tolerance=*/0.01); +} diff --git a/openvdb/unittest/TestGrid.cc b/openvdb/unittest/TestGrid.cc new file mode 100644 index 00000000..608f7c8b --- /dev/null +++ b/openvdb/unittest/TestGrid.cc @@ -0,0 +1,523 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for std::make_unique + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + +class TestGrid: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestGrid); + CPPUNIT_TEST(testGridRegistry); + CPPUNIT_TEST(testConstPtr); + CPPUNIT_TEST(testGetGrid); + CPPUNIT_TEST(testIsType); + CPPUNIT_TEST(testTransform); + CPPUNIT_TEST(testCopyGrid); + CPPUNIT_TEST(testValueConversion); + CPPUNIT_TEST(testClipping); + CPPUNIT_TEST(testApply); + CPPUNIT_TEST_SUITE_END(); + + void testGridRegistry(); + void testConstPtr(); + void testGetGrid(); + void testIsType(); + void testTransform(); + void testCopyGrid(); + void testValueConversion(); + void testClipping(); + void testApply(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestGrid); + + +//////////////////////////////////////// + + +class ProxyTree: public openvdb::TreeBase +{ +public: + using ValueType = int; + using BuildType = int; + using LeafNodeType = void; + using ValueAllCIter = void; + using ValueAllIter = void; + using ValueOffCIter = void; + using ValueOffIter = void; + using ValueOnCIter = void; + using ValueOnIter = void; + using TreeBasePtr = openvdb::TreeBase::Ptr; + using Ptr = openvdb::SharedPtr; + using ConstPtr = openvdb::SharedPtr; + + static const openvdb::Index DEPTH; + static const ValueType backg; + + ProxyTree() {} + ProxyTree(const ValueType&) {} + ProxyTree(const ProxyTree&) = default; + ~ProxyTree() override = default; + + static const openvdb::Name& treeType() { static const openvdb::Name s("proxy"); return s; } + const openvdb::Name& type() const override { return treeType(); } + openvdb::Name valueType() const override { return "proxy"; } + const ValueType& background() const { return backg; } + + TreeBasePtr copy() const override { return TreeBasePtr(new ProxyTree(*this)); } + + void readTopology(std::istream& is, bool = false) override { is.seekg(0, std::ios::beg); } + void writeTopology(std::ostream& os, bool = false) const override { os.seekp(0); } + + void readBuffers(std::istream& is, + const openvdb::CoordBBox&, bool /*saveFloatAsHalf*/=false) override { is.seekg(0); } + void readNonresidentBuffers() const override {} + void readBuffers(std::istream& is, bool /*saveFloatAsHalf*/=false) override { is.seekg(0); } + void writeBuffers(std::ostream& os, bool /*saveFloatAsHalf*/=false) const override + { os.seekp(0, std::ios::beg); } + + bool empty() const { return true; } + void clear() {} + void prune(const ValueType& = 0) {} + void clip(const openvdb::CoordBBox&) {} + void clipUnallocatedNodes() override {} +#if OPENVDB_ABI_VERSION_NUMBER >= 4 + openvdb::Index32 unallocatedLeafCount() const override { return 0; } +#endif + + void getIndexRange(openvdb::CoordBBox&) const override {} + bool evalLeafBoundingBox(openvdb::CoordBBox& bbox) const override + { bbox.min() = bbox.max() = openvdb::Coord(0, 0, 0); return false; } + bool evalActiveVoxelBoundingBox(openvdb::CoordBBox& bbox) const override + { bbox.min() = bbox.max() = openvdb::Coord(0, 0, 0); return false; } + bool evalActiveVoxelDim(openvdb::Coord& dim) const override + { dim = openvdb::Coord(0, 0, 0); return false; } + bool evalLeafDim(openvdb::Coord& dim) const override + { dim = openvdb::Coord(0, 0, 0); return false; } + + openvdb::Index treeDepth() const override { return 0; } + openvdb::Index leafCount() const override { return 0; } +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + std::vector nodeCount() const override + { return std::vector(DEPTH, 0); } +#endif + openvdb::Index nonLeafCount() const override { return 0; } + openvdb::Index64 activeVoxelCount() const override { return 0UL; } + openvdb::Index64 inactiveVoxelCount() const override { return 0UL; } + openvdb::Index64 activeLeafVoxelCount() const override { return 0UL; } + openvdb::Index64 inactiveLeafVoxelCount() const override { return 0UL; } + openvdb::Index64 activeTileCount() const override { return 0UL; } +}; + +const openvdb::Index ProxyTree::DEPTH = 0; +const ProxyTree::ValueType ProxyTree::backg = 0; + +using ProxyGrid = openvdb::Grid; + + +//////////////////////////////////////// + +void +TestGrid::testGridRegistry() +{ + using namespace openvdb::tree; + + using TreeType = Tree, 2> > >; + using GridType = openvdb::Grid; + + openvdb::GridBase::clearRegistry(); + + CPPUNIT_ASSERT(!GridType::isRegistered()); + GridType::registerGrid(); + CPPUNIT_ASSERT(GridType::isRegistered()); + CPPUNIT_ASSERT_THROW(GridType::registerGrid(), openvdb::KeyError); + GridType::unregisterGrid(); + CPPUNIT_ASSERT(!GridType::isRegistered()); + CPPUNIT_ASSERT_NO_THROW(GridType::unregisterGrid()); + CPPUNIT_ASSERT(!GridType::isRegistered()); + CPPUNIT_ASSERT_NO_THROW(GridType::registerGrid()); + CPPUNIT_ASSERT(GridType::isRegistered()); + + openvdb::GridBase::clearRegistry(); +} + + +void +TestGrid::testConstPtr() +{ + using namespace openvdb; + + GridBase::ConstPtr constgrid = ProxyGrid::create(); + + CPPUNIT_ASSERT_EQUAL(Name("proxy"), constgrid->type()); +} + + +void +TestGrid::testGetGrid() +{ + using namespace openvdb; + + GridBase::Ptr grid = FloatGrid::create(/*bg=*/0.0); + GridBase::ConstPtr constGrid = grid; + + CPPUNIT_ASSERT(grid->baseTreePtr()); + + CPPUNIT_ASSERT(!gridPtrCast(grid)); + CPPUNIT_ASSERT(!gridPtrCast(grid)); + + CPPUNIT_ASSERT(gridConstPtrCast(constGrid)); + CPPUNIT_ASSERT(!gridConstPtrCast(constGrid)); +} + + +void +TestGrid::testIsType() +{ + using namespace openvdb; + + GridBase::Ptr grid = FloatGrid::create(); + CPPUNIT_ASSERT(grid->isType()); + CPPUNIT_ASSERT(!grid->isType()); +} + + +void +TestGrid::testTransform() +{ + ProxyGrid grid; + + // Verify that the grid has a valid default transform. + CPPUNIT_ASSERT(grid.transformPtr()); + + // Verify that a null transform pointer is not allowed. + CPPUNIT_ASSERT_THROW(grid.setTransform(openvdb::math::Transform::Ptr()), + openvdb::ValueError); + + grid.setTransform(openvdb::math::Transform::createLinearTransform()); + + CPPUNIT_ASSERT(grid.transformPtr()); + + // Verify that calling Transform-related Grid methods (Grid::voxelSize(), etc.) + // is the same as calling those methods on the Transform. + + CPPUNIT_ASSERT(grid.transform().voxelSize().eq(grid.voxelSize())); + CPPUNIT_ASSERT(grid.transform().voxelSize(openvdb::Vec3d(0.1, 0.2, 0.3)).eq( + grid.voxelSize(openvdb::Vec3d(0.1, 0.2, 0.3)))); + + CPPUNIT_ASSERT(grid.transform().indexToWorld(openvdb::Vec3d(0.1, 0.2, 0.3)).eq( + grid.indexToWorld(openvdb::Vec3d(0.1, 0.2, 0.3)))); + CPPUNIT_ASSERT(grid.transform().indexToWorld(openvdb::Coord(1, 2, 3)).eq( + grid.indexToWorld(openvdb::Coord(1, 2, 3)))); + CPPUNIT_ASSERT(grid.transform().worldToIndex(openvdb::Vec3d(0.1, 0.2, 0.3)).eq( + grid.worldToIndex(openvdb::Vec3d(0.1, 0.2, 0.3)))); +} + + +void +TestGrid::testCopyGrid() +{ + using namespace openvdb; + + // set up a grid + const float fillValue1=5.0f; + FloatGrid::Ptr grid1 = createGrid(/*bg=*/fillValue1); + FloatTree& tree1 = grid1->tree(); + tree1.setValue(Coord(-10,40,845), 3.456f); + tree1.setValue(Coord(1,-50,-8), 1.0f); + + // create a new grid, copying the first grid + GridBase::Ptr grid2 = grid1->deepCopy(); + + // cast down to the concrete type to query values + FloatTree& tree2 = gridPtrCast(grid2)->tree(); + + // compare topology + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + + // trees should be equal + ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue1, tree2.getValue(Coord(1,2,3))); + ASSERT_DOUBLES_EXACTLY_EQUAL(3.456f, tree2.getValue(Coord(-10,40,845))); + ASSERT_DOUBLES_EXACTLY_EQUAL(1.0f, tree2.getValue(Coord(1,-50,-8))); + + // change 1 value in tree2 + Coord changeCoord(1, -500, -8); + tree2.setValue(changeCoord, 1.0f); + + // topology should no longer match + CPPUNIT_ASSERT(!tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(!tree2.hasSameTopology(tree1)); + + // query changed value and make sure it's different between trees + ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue1, tree1.getValue(changeCoord)); + ASSERT_DOUBLES_EXACTLY_EQUAL(1.0f, tree2.getValue(changeCoord)); + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + // shallow-copy a const grid but supply a new transform and meta map + CPPUNIT_ASSERT_EQUAL(1.0, grid1->transform().voxelSize().x()); + CPPUNIT_ASSERT_EQUAL(size_t(0), grid1->metaCount()); + CPPUNIT_ASSERT_EQUAL(Index(2), grid1->tree().leafCount()); + + math::Transform::Ptr xform(math::Transform::createLinearTransform(/*voxelSize=*/0.25)); + MetaMap meta; + meta.insertMeta("test", Int32Metadata(4)); + + FloatGrid::ConstPtr constGrid1 = ConstPtrCast(grid1); + + GridBase::ConstPtr grid3 = constGrid1->copyGridReplacingMetadataAndTransform(meta, xform); + const FloatTree& tree3 = gridConstPtrCast(grid3)->tree(); + + CPPUNIT_ASSERT_EQUAL(0.25, grid3->transform().voxelSize().x()); + CPPUNIT_ASSERT_EQUAL(size_t(1), grid3->metaCount()); + CPPUNIT_ASSERT_EQUAL(Index(2), tree3.leafCount()); + CPPUNIT_ASSERT_EQUAL(long(3), constGrid1->constTreePtr().use_count()); +#endif +} + + +void +TestGrid::testValueConversion() +{ + using namespace openvdb; + + const Coord c0(-10, 40, 845), c1(1, -50, -8), c2(1, 2, 3); + const float fval0 = 3.25f, fval1 = 1.0f, fbkgd = 5.0f; + + // Create a FloatGrid. + FloatGrid fgrid(fbkgd); + FloatTree& ftree = fgrid.tree(); + ftree.setValue(c0, fval0); + ftree.setValue(c1, fval1); + + // Copy the FloatGrid to a DoubleGrid. + DoubleGrid dgrid(fgrid); + DoubleTree& dtree = dgrid.tree(); + // Compare topology. + CPPUNIT_ASSERT(dtree.hasSameTopology(ftree)); + CPPUNIT_ASSERT(ftree.hasSameTopology(dtree)); + // Compare values. + ASSERT_DOUBLES_EXACTLY_EQUAL(double(fbkgd), dtree.getValue(c2)); + ASSERT_DOUBLES_EXACTLY_EQUAL(double(fval0), dtree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(double(fval1), dtree.getValue(c1)); + + // Copy the FloatGrid to a BoolGrid. + BoolGrid bgrid(fgrid); + BoolTree& btree = bgrid.tree(); + // Compare topology. + CPPUNIT_ASSERT(btree.hasSameTopology(ftree)); + CPPUNIT_ASSERT(ftree.hasSameTopology(btree)); + // Compare values. + CPPUNIT_ASSERT_EQUAL(bool(fbkgd), btree.getValue(c2)); + CPPUNIT_ASSERT_EQUAL(bool(fval0), btree.getValue(c0)); + CPPUNIT_ASSERT_EQUAL(bool(fval1), btree.getValue(c1)); + + // Copy the FloatGrid to a Vec3SGrid. + Vec3SGrid vgrid(fgrid); + Vec3STree& vtree = vgrid.tree(); + // Compare topology. + CPPUNIT_ASSERT(vtree.hasSameTopology(ftree)); + CPPUNIT_ASSERT(ftree.hasSameTopology(vtree)); + // Compare values. + CPPUNIT_ASSERT_EQUAL(Vec3s(fbkgd), vtree.getValue(c2)); + CPPUNIT_ASSERT_EQUAL(Vec3s(fval0), vtree.getValue(c0)); + CPPUNIT_ASSERT_EQUAL(Vec3s(fval1), vtree.getValue(c1)); + + // Verify that a Vec3SGrid can't be copied to an Int32Grid + // (because an Int32 can't be constructed from a Vec3S). + CPPUNIT_ASSERT_THROW(Int32Grid igrid2(vgrid), openvdb::TypeError); + + // Verify that a grid can't be converted to another type with a different + // tree configuration. + using DTree23 = tree::Tree3::Type; + using DGrid23 = Grid; + CPPUNIT_ASSERT_THROW(DGrid23 d23grid(fgrid), openvdb::TypeError); +} + + +//////////////////////////////////////// + + +template +void +validateClippedGrid(const GridT& clipped, const typename GridT::ValueType& fg) +{ + using namespace openvdb; + + using ValueT = typename GridT::ValueType; + + const CoordBBox bbox = clipped.evalActiveVoxelBoundingBox(); + CPPUNIT_ASSERT_EQUAL(4, bbox.min().x()); + CPPUNIT_ASSERT_EQUAL(4, bbox.min().y()); + CPPUNIT_ASSERT_EQUAL(-6, bbox.min().z()); + CPPUNIT_ASSERT_EQUAL(4, bbox.max().x()); + CPPUNIT_ASSERT_EQUAL(4, bbox.max().y()); + CPPUNIT_ASSERT_EQUAL(6, bbox.max().z()); + CPPUNIT_ASSERT_EQUAL(6 + 6 + 1, int(clipped.activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(2, int(clipped.constTree().leafCount())); + + typename GridT::ConstAccessor acc = clipped.getConstAccessor(); + const ValueT bg = clipped.background(); + Coord xyz; + int &x = xyz[0], &y = xyz[1], &z = xyz[2]; + for (x = -10; x <= 10; ++x) { + for (y = -10; y <= 10; ++y) { + for (z = -10; z <= 10; ++z) { + if (x == 4 && y == 4 && z >= -6 && z <= 6) { + CPPUNIT_ASSERT_EQUAL(fg, acc.getValue(Coord(4, 4, z))); + } else { + CPPUNIT_ASSERT_EQUAL(bg, acc.getValue(Coord(x, y, z))); + } + } + } + } +} + + +// See also TestTools::testClipping() +void +TestGrid::testClipping() +{ + using namespace openvdb; + + const BBoxd clipBox(Vec3d(4.0, 4.0, -6.0), Vec3d(4.9, 4.9, 6.0)); + + { + const float fg = 5.f; + FloatGrid cube(0.f); + cube.fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/fg, /*active=*/true); + cube.clipGrid(clipBox); + validateClippedGrid(cube, fg); + } + { + const bool fg = true; + BoolGrid cube(false); + cube.fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/fg, /*active=*/true); + cube.clipGrid(clipBox); + validateClippedGrid(cube, fg); + } + { + const Vec3s fg(1.f, -2.f, 3.f); + Vec3SGrid cube(Vec3s(0.f)); + cube.fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/fg, /*active=*/true); + cube.clipGrid(clipBox); + validateClippedGrid(cube, fg); + } + /* + {// Benchmark multi-threaded copy construction + openvdb::util::CpuTimer timer; + openvdb::initialize(); + openvdb::io::File file("/usr/pic1/Data/OpenVDB/LevelSetModels/crawler.vdb"); + file.open(); + openvdb::GridBase::Ptr baseGrid = file.readGrid("ls_crawler"); + file.close(); + openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast(baseGrid); + //grid->tree().print(); + timer.start("\nCopy construction"); + openvdb::FloatTree fTree(grid->tree()); + timer.stop(); + + timer.start("\nBoolean topology copy construction"); + openvdb::BoolTree bTree(grid->tree(), false, openvdb::TopologyCopy()); + timer.stop(); + + timer.start("\nBoolean topology union"); + bTree.topologyUnion(fTree); + timer.stop(); + //bTree.print(); + } + */ +} + + +//////////////////////////////////////// + + +namespace { + +struct GridOp +{ + bool isConst = false; + template void operator()(const GridT&) { isConst = true; } + template void operator()(GridT&) { isConst = false; } +}; + +} // anonymous namespace + + +void +TestGrid::testApply() +{ + using namespace openvdb; + + const GridBase::Ptr + boolGrid = BoolGrid::create(), + floatGrid = FloatGrid::create(), + doubleGrid = DoubleGrid::create(), + intGrid = Int32Grid::create(); + + const GridBase::ConstPtr + boolCGrid = BoolGrid::create(), + floatCGrid = FloatGrid::create(), + doubleCGrid = DoubleGrid::create(), + intCGrid = Int32Grid::create(); + + { + using AllowedGridTypes = TypeList<>; + + // Verify that the functor is not applied to any of the grids. + GridOp op; + CPPUNIT_ASSERT(!boolGrid->apply(op)); + CPPUNIT_ASSERT(!boolCGrid->apply(op)); + CPPUNIT_ASSERT(!floatGrid->apply(op)); + CPPUNIT_ASSERT(!floatCGrid->apply(op)); + CPPUNIT_ASSERT(!doubleGrid->apply(op)); + CPPUNIT_ASSERT(!doubleCGrid->apply(op)); + CPPUNIT_ASSERT(!intGrid->apply(op)); + CPPUNIT_ASSERT(!intCGrid->apply(op)); + } + { + using AllowedGridTypes = TypeList; + + // Verify that the functor is applied only to grids of the allowed types + // and that their constness is respected. + GridOp op; + CPPUNIT_ASSERT(!boolGrid->apply(op)); + CPPUNIT_ASSERT(!intGrid->apply(op)); + CPPUNIT_ASSERT(floatGrid->apply(op)); CPPUNIT_ASSERT(!op.isConst); + CPPUNIT_ASSERT(doubleGrid->apply(op)); CPPUNIT_ASSERT(!op.isConst); + + CPPUNIT_ASSERT(!boolCGrid->apply(op)); + CPPUNIT_ASSERT(!intCGrid->apply(op)); + CPPUNIT_ASSERT(floatCGrid->apply(op)); CPPUNIT_ASSERT(op.isConst); + CPPUNIT_ASSERT(doubleCGrid->apply(op)); CPPUNIT_ASSERT(op.isConst); + } + { + using AllowedGridTypes = TypeList; + + // Verify that rvalue functors are supported. + int n = 0; + CPPUNIT_ASSERT( !boolGrid->apply([&n](GridBase&) { ++n; })); + CPPUNIT_ASSERT( !intGrid->apply([&n](GridBase&) { ++n; })); + CPPUNIT_ASSERT( floatGrid->apply([&n](GridBase&) { ++n; })); + CPPUNIT_ASSERT( doubleGrid->apply([&n](GridBase&) { ++n; })); + CPPUNIT_ASSERT( !boolCGrid->apply([&n](const GridBase&) { ++n; })); + CPPUNIT_ASSERT( !intCGrid->apply([&n](const GridBase&) { ++n; })); + CPPUNIT_ASSERT( floatCGrid->apply([&n](const GridBase&) { ++n; })); + CPPUNIT_ASSERT(doubleCGrid->apply([&n](const GridBase&) { ++n; })); + CPPUNIT_ASSERT_EQUAL(4, n); + } +} diff --git a/openvdb/unittest/TestGridBbox.cc b/openvdb/unittest/TestGridBbox.cc new file mode 100644 index 00000000..e1f67e21 --- /dev/null +++ b/openvdb/unittest/TestGridBbox.cc @@ -0,0 +1,83 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include +#include +#include +#include + + +class TestGridBbox: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestGridBbox); + CPPUNIT_TEST(testLeafBbox); + CPPUNIT_TEST(testGridBbox); + CPPUNIT_TEST_SUITE_END(); + + void testLeafBbox(); + void testGridBbox(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestGridBbox); + + +//////////////////////////////////////// + + +void +TestGridBbox::testLeafBbox() +{ + openvdb::FloatTree tree(/*fillValue=*/256.0f); + + openvdb::CoordBBox bbox; + CPPUNIT_ASSERT(!tree.evalLeafBoundingBox(bbox)); + + // Add values to buffer zero. + tree.setValue(openvdb::Coord( 0, 9, 9), 2.0); + tree.setValue(openvdb::Coord(100, 35, 800), 2.5); + + // Coordinates in CoordBBox are inclusive! + CPPUNIT_ASSERT(tree.evalLeafBoundingBox(bbox)); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 8, 8), bbox.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(104-1, 40-1, 808-1), bbox.max()); + + // Test negative coordinates. + tree.setValue(openvdb::Coord(-100, -35, -800), 2.5); + + CPPUNIT_ASSERT(tree.evalLeafBoundingBox(bbox)); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(-104, -40, -800), bbox.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(104-1, 40-1, 808-1), bbox.max()); +} + + +void +TestGridBbox::testGridBbox() +{ + openvdb::FloatTree tree(/*fillValue=*/256.0f); + + openvdb::CoordBBox bbox; + CPPUNIT_ASSERT(!tree.evalActiveVoxelBoundingBox(bbox)); + + // Add values to buffer zero. + tree.setValue(openvdb::Coord( 1, 0, 0), 1.5); + tree.setValue(openvdb::Coord( 0, 12, 8), 2.0); + tree.setValue(openvdb::Coord( 1, 35, 800), 2.5); + tree.setValue(openvdb::Coord(100, 0, 16), 3.0); + tree.setValue(openvdb::Coord( 1, 0, 16), 3.5); + + // Coordinates in CoordBBox are inclusive! + CPPUNIT_ASSERT(tree.evalActiveVoxelBoundingBox(bbox)); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord( 0, 0, 0), bbox.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(100, 35, 800), bbox.max()); + + // Test negative coordinates. + tree.setValue(openvdb::Coord(-100, -35, -800), 2.5); + + CPPUNIT_ASSERT(tree.evalActiveVoxelBoundingBox(bbox)); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(-100, -35, -800), bbox.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(100, 35, 800), bbox.max()); +} diff --git a/openvdb/unittest/TestGridDescriptor.cc b/openvdb/unittest/TestGridDescriptor.cc new file mode 100644 index 00000000..a5c0df79 --- /dev/null +++ b/openvdb/unittest/TestGridDescriptor.cc @@ -0,0 +1,159 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + + +class TestGridDescriptor: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestGridDescriptor); + CPPUNIT_TEST(testIO); + CPPUNIT_TEST(testCopy); + CPPUNIT_TEST(testName); + CPPUNIT_TEST_SUITE_END(); + + void testIO(); + void testCopy(); + void testName(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestGridDescriptor); + + +void +TestGridDescriptor::testIO() +{ + using namespace openvdb::io; + using namespace openvdb; + + typedef FloatGrid GridType; + + GridDescriptor gd(GridDescriptor::addSuffix("temperature", 2), GridType::gridType()); + gd.setInstanceParentName("temperature_32bit"); + + gd.setGridPos(123); + gd.setBlockPos(234); + gd.setEndPos(567); + + // write out the gd. + std::ostringstream ostr(std::ios_base::binary); + + gd.writeHeader(ostr); + gd.writeStreamPos(ostr); + + // Read in the gd. + std::istringstream istr(ostr.str(), std::ios_base::binary); + + // Since the input is only a fragment of a VDB file (in particular, + // it doesn't have a header), set the file format version number explicitly. + io::setCurrentVersion(istr); + + GridDescriptor gd2; + + CPPUNIT_ASSERT_THROW(gd2.read(istr), openvdb::LookupError); + + // Register the grid. + GridBase::clearRegistry(); + GridType::registerGrid(); + + // seek back and read again. + istr.seekg(0, std::ios_base::beg); + GridBase::Ptr grid; + CPPUNIT_ASSERT_NO_THROW(grid = gd2.read(istr)); + + CPPUNIT_ASSERT_EQUAL(gd.gridName(), gd2.gridName()); + CPPUNIT_ASSERT_EQUAL(gd.uniqueName(), gd2.uniqueName()); + CPPUNIT_ASSERT_EQUAL(gd.gridType(), gd2.gridType()); + CPPUNIT_ASSERT_EQUAL(gd.instanceParentName(), gd2.instanceParentName()); + CPPUNIT_ASSERT(grid.get() != NULL); + CPPUNIT_ASSERT_EQUAL(GridType::gridType(), grid->type()); + CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), gd2.getGridPos()); + CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), gd2.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), gd2.getEndPos()); + + // Clear the registry when we are done. + GridBase::clearRegistry(); +} + + +void +TestGridDescriptor::testCopy() +{ + using namespace openvdb::io; + using namespace openvdb; + + typedef FloatGrid GridType; + + GridDescriptor gd("temperature", GridType::gridType()); + gd.setInstanceParentName("temperature_32bit"); + + gd.setGridPos(123); + gd.setBlockPos(234); + gd.setEndPos(567); + + GridDescriptor gd2; + + // do the copy + gd2 = gd; + + CPPUNIT_ASSERT_EQUAL(gd.gridName(), gd2.gridName()); + CPPUNIT_ASSERT_EQUAL(gd.uniqueName(), gd2.uniqueName()); + CPPUNIT_ASSERT_EQUAL(gd.gridType(), gd2.gridType()); + CPPUNIT_ASSERT_EQUAL(gd.instanceParentName(), gd2.instanceParentName()); + CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), gd2.getGridPos()); + CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), gd2.getBlockPos()); + CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), gd2.getEndPos()); +} + + +void +TestGridDescriptor::testName() +{ + using openvdb::Name; + using openvdb::io::GridDescriptor; + + const std::string typ = openvdb::FloatGrid::gridType(); + + Name name("test"); + GridDescriptor gd(name, typ); + + // Verify that the grid name and the unique name are equivalent + // when the unique name has no suffix. + CPPUNIT_ASSERT_EQUAL(name, gd.gridName()); + CPPUNIT_ASSERT_EQUAL(name, gd.uniqueName()); + CPPUNIT_ASSERT_EQUAL(name, GridDescriptor::nameAsString(name)); + CPPUNIT_ASSERT_EQUAL(name, GridDescriptor::stripSuffix(name)); + + // Add a suffix. + name = GridDescriptor::addSuffix("test", 2); + gd = GridDescriptor(name, typ); + + // Verify that the grid name and the unique name differ + // when the unique name has a suffix. + CPPUNIT_ASSERT_EQUAL(name, gd.uniqueName()); + CPPUNIT_ASSERT(gd.gridName() != gd.uniqueName()); + CPPUNIT_ASSERT_EQUAL(GridDescriptor::stripSuffix(name), gd.gridName()); + CPPUNIT_ASSERT_EQUAL(Name("test[2]"), GridDescriptor::nameAsString(name)); + + // As above, but with a longer suffix + name = GridDescriptor::addSuffix("test", 13); + gd = GridDescriptor(name, typ); + + CPPUNIT_ASSERT_EQUAL(name, gd.uniqueName()); + CPPUNIT_ASSERT(gd.gridName() != gd.uniqueName()); + CPPUNIT_ASSERT_EQUAL(GridDescriptor::stripSuffix(name), gd.gridName()); + CPPUNIT_ASSERT_EQUAL(Name("test[13]"), GridDescriptor::nameAsString(name)); + + // Multiple suffixes aren't supported, but verify that + // they behave reasonably, at least. + name = GridDescriptor::addSuffix(name, 4); + gd = GridDescriptor(name, typ); + + CPPUNIT_ASSERT_EQUAL(name, gd.uniqueName()); + CPPUNIT_ASSERT(gd.gridName() != gd.uniqueName()); + CPPUNIT_ASSERT_EQUAL(GridDescriptor::stripSuffix(name), gd.gridName()); +} diff --git a/openvdb/unittest/TestGridIO.cc b/openvdb/unittest/TestGridIO.cc new file mode 100644 index 00000000..5384deaa --- /dev/null +++ b/openvdb/unittest/TestGridIO.cc @@ -0,0 +1,205 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include // for remove() + + +class TestGridIO: public CppUnit::TestCase +{ +public: + typedef openvdb::tree::Tree< + openvdb::tree::RootNode< + openvdb::tree::InternalNode< + openvdb::tree::InternalNode< + openvdb::tree::InternalNode< + openvdb::tree::LeafNode, 3>, 4>, 5> > > + Float5432Tree; + typedef openvdb::Grid Float5432Grid; + + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestGridIO); + CPPUNIT_TEST(testReadAllBool); + CPPUNIT_TEST(testReadAllMask); + CPPUNIT_TEST(testReadAllFloat); + CPPUNIT_TEST(testReadAllVec3S); + CPPUNIT_TEST(testReadAllFloat5432); + CPPUNIT_TEST_SUITE_END(); + + void testReadAllBool() { readAllTest(); } + void testReadAllMask() { readAllTest(); } + void testReadAllFloat() { readAllTest(); } + void testReadAllVec3S() { readAllTest(); } + void testReadAllFloat5432() { Float5432Grid::registerGrid(); readAllTest(); } +private: + template void readAllTest(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestGridIO); + + +//////////////////////////////////////// + + +template +void +TestGridIO::readAllTest() +{ + using namespace openvdb; + + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::Ptr TreePtr; + typedef typename TreeType::ValueType ValueT; + typedef typename TreeType::NodeCIter NodeCIter; + const ValueT zero = zeroVal(); + + // For each level of the tree, compute a bit mask for use in converting + // global coordinates to node origins for nodes at that level. + // That is, node_origin = global_coordinates & mask[node_level]. + std::vector mask; + TreeType::getNodeLog2Dims(mask); + const size_t height = mask.size(); + for (size_t i = 0; i < height; ++i) { + Index dim = 0; + for (size_t j = i; j < height; ++j) dim += mask[j]; + mask[i] = ~((1 << dim) - 1); + } + const Index childDim = 1 + ~(mask[0]); + + // Choose sample coordinate pairs (coord0, coord1) and (coord0, coord2) + // that are guaranteed to lie in different children of the root node + // (because they are separated by more than the child node dimension). + const Coord + coord0(0, 0, 0), + coord1(int(1.1 * childDim), 0, 0), + coord2(0, int(1.1 * childDim), 0); + + // Create trees. + TreePtr + tree1(new TreeType(zero + 1)), + tree2(new TreeType(zero + 2)); + + // Set some values. + tree1->setValue(coord0, zero + 5); + tree1->setValue(coord1, zero + 6); + tree2->setValue(coord0, zero + 10); + tree2->setValue(coord2, zero + 11); + + // Create grids with trees and assign transforms. + math::Transform::Ptr trans1(math::Transform::createLinearTransform(0.1)), + trans2(math::Transform::createLinearTransform(0.1)); + GridBase::Ptr grid1 = createGrid(tree1), grid2 = createGrid(tree2); + grid1->setTransform(trans1); + grid1->setName("density"); + grid2->setTransform(trans2); + grid2->setName("temperature"); + + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + CPPUNIT_ASSERT_EQUAL(ValueT(zero + 5), tree1->getValue(coord0)); + CPPUNIT_ASSERT_EQUAL(ValueT(zero + 6), tree1->getValue(coord1)); + CPPUNIT_ASSERT_EQUAL(ValueT(zero + 10), tree2->getValue(coord0)); + CPPUNIT_ASSERT_EQUAL(ValueT(zero + 11), tree2->getValue(coord2)); + OPENVDB_NO_FP_EQUALITY_WARNING_END + + // count[d] is the number of nodes already visited at depth d. + // There should be exactly two nodes at each depth (apart from the root). + std::vector count(height, 0); + + // Verify that tree1 has correct node origins. + for (NodeCIter iter = tree1->cbeginNode(); iter; ++iter) { + const Index depth = iter.getDepth(); + const Coord expected[2] = { + coord0 & mask[depth], // origin of the first node at this depth + coord1 & mask[depth] // origin of the second node at this depth + }; + CPPUNIT_ASSERT_EQUAL(expected[count[depth]], iter.getCoord()); + ++count[depth]; + } + // Verify that tree2 has correct node origins. + count.assign(height, 0); // reset node counts + for (NodeCIter iter = tree2->cbeginNode(); iter; ++iter) { + const Index depth = iter.getDepth(); + const Coord expected[2] = { coord0 & mask[depth], coord2 & mask[depth] }; + CPPUNIT_ASSERT_EQUAL(expected[count[depth]], iter.getCoord()); + ++count[depth]; + } + + MetaMap::Ptr meta(new MetaMap); + meta->insertMeta("author", StringMetadata("Einstein")); + meta->insertMeta("year", Int32Metadata(2009)); + + GridPtrVecPtr grids(new GridPtrVec); + grids->push_back(grid1); + grids->push_back(grid2); + + // Write grids and metadata out to a file. + { + io::File vdbfile("something.vdb2"); + vdbfile.write(*grids, *meta); + } + meta.reset(); + grids.reset(); + + io::File vdbfile("something.vdb2"); + CPPUNIT_ASSERT_THROW(vdbfile.getGrids(), openvdb::IoError); // file has not been opened + + // Read the grids back in. + vdbfile.open(); + CPPUNIT_ASSERT(vdbfile.isOpen()); + + grids = vdbfile.getGrids(); + meta = vdbfile.getMetadata(); + + // Ensure we have the metadata. + CPPUNIT_ASSERT(meta.get() != NULL); + CPPUNIT_ASSERT_EQUAL(2, int(meta->metaCount())); + CPPUNIT_ASSERT_EQUAL(std::string("Einstein"), meta->metaValue("author")); + CPPUNIT_ASSERT_EQUAL(2009, meta->metaValue("year")); + + // Ensure we got both grids. + CPPUNIT_ASSERT(grids.get() != NULL); + CPPUNIT_ASSERT_EQUAL(2, int(grids->size())); + + grid1.reset(); + grid1 = findGridByName(*grids, "density"); + CPPUNIT_ASSERT(grid1.get() != NULL); + TreePtr density = gridPtrCast(grid1)->treePtr(); + CPPUNIT_ASSERT(density.get() != NULL); + + grid2.reset(); + grid2 = findGridByName(*grids, "temperature"); + CPPUNIT_ASSERT(grid2.get() != NULL); + TreePtr temperature = gridPtrCast(grid2)->treePtr(); + CPPUNIT_ASSERT(temperature.get() != NULL); + + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + CPPUNIT_ASSERT_EQUAL(ValueT(zero + 5), density->getValue(coord0)); + CPPUNIT_ASSERT_EQUAL(ValueT(zero + 6), density->getValue(coord1)); + CPPUNIT_ASSERT_EQUAL(ValueT(zero + 10), temperature->getValue(coord0)); + CPPUNIT_ASSERT_EQUAL(ValueT(zero + 11), temperature->getValue(coord2)); + OPENVDB_NO_FP_EQUALITY_WARNING_END + + // Check if we got the correct node origins. + count.assign(height, 0); + for (NodeCIter iter = density->cbeginNode(); iter; ++iter) { + const Index depth = iter.getDepth(); + const Coord expected[2] = { coord0 & mask[depth], coord1 & mask[depth] }; + CPPUNIT_ASSERT_EQUAL(expected[count[depth]], iter.getCoord()); + ++count[depth]; + } + count.assign(height, 0); + for (NodeCIter iter = temperature->cbeginNode(); iter; ++iter) { + const Index depth = iter.getDepth(); + const Coord expected[2] = { coord0 & mask[depth], coord2 & mask[depth] }; + CPPUNIT_ASSERT_EQUAL(expected[count[depth]], iter.getCoord()); + ++count[depth]; + } + + vdbfile.close(); + + ::remove("something.vdb2"); +} diff --git a/openvdb/unittest/TestGridTransformer.cc b/openvdb/unittest/TestGridTransformer.cc new file mode 100644 index 00000000..74685144 --- /dev/null +++ b/openvdb/unittest/TestGridTransformer.cc @@ -0,0 +1,239 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + +class TestGridTransformer: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestGridTransformer); + CPPUNIT_TEST(testTransformBoolPoint); + CPPUNIT_TEST(testTransformFloatPoint); + CPPUNIT_TEST(testTransformFloatBox); + CPPUNIT_TEST(testTransformFloatQuadratic); + CPPUNIT_TEST(testTransformDoubleBox); + CPPUNIT_TEST(testTransformInt32Box); + CPPUNIT_TEST(testTransformInt64Box); + CPPUNIT_TEST(testTransformVec3SPoint); + CPPUNIT_TEST(testTransformVec3DBox); + CPPUNIT_TEST(testResampleToMatch); + CPPUNIT_TEST_SUITE_END(); + + void testTransformBoolPoint() + { transformGrid(); } + void testTransformFloatPoint() + { transformGrid(); } + void testTransformFloatBox() + { transformGrid(); } + void testTransformFloatQuadratic() + { transformGrid(); } + void testTransformDoubleBox() + { transformGrid(); } + void testTransformInt32Box() + { transformGrid(); } + void testTransformInt64Box() + { transformGrid(); } + void testTransformVec3SPoint() + { transformGrid(); } + void testTransformVec3DBox() + { transformGrid(); } + + void testResampleToMatch(); + +private: + template void transformGrid(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestGridTransformer); + + +//////////////////////////////////////// + + +template +void +TestGridTransformer::transformGrid() +{ + using openvdb::Coord; + using openvdb::CoordBBox; + using openvdb::Vec3R; + + typedef typename GridType::ValueType ValueT; + + const int radius = Sampler::radius(); + const openvdb::Vec3R zeroVec(0, 0, 0), oneVec(1, 1, 1); + const ValueT + zero = openvdb::zeroVal(), + one = zero + 1, + two = one + 1, + background = one; + const bool transformTiles = true; + + // Create a sparse test grid comprising the eight corners of a 20 x 20 x 20 cube. + typename GridType::Ptr inGrid = GridType::create(background); + typename GridType::Accessor inAcc = inGrid->getAccessor(); + inAcc.setValue(Coord( 0, 0, 0), /*value=*/zero); + inAcc.setValue(Coord(20, 0, 0), zero); + inAcc.setValue(Coord( 0, 20, 0), zero); + inAcc.setValue(Coord( 0, 0, 20), zero); + inAcc.setValue(Coord(20, 0, 20), zero); + inAcc.setValue(Coord( 0, 20, 20), zero); + inAcc.setValue(Coord(20, 20, 0), zero); + inAcc.setValue(Coord(20, 20, 20), zero); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(8), inGrid->activeVoxelCount()); + + // For various combinations of scaling, rotation and translation... + for (int i = 0; i < 8; ++i) { + const openvdb::Vec3R + scale = i & 1 ? openvdb::Vec3R(10, 4, 7.5) : oneVec, + rotate = (i & 2 ? openvdb::Vec3R(30, 230, -190) : zeroVec) * (M_PI / 180), + translate = i & 4 ? openvdb::Vec3R(-5, 0, 10) : zeroVec, + pivot = i & 8 ? openvdb::Vec3R(0.5, 4, -3.3) : zeroVec; + openvdb::tools::GridTransformer transformer(pivot, scale, rotate, translate); + transformer.setTransformTiles(transformTiles); + + // Add a tile (either active or inactive) in the interior of the cube. + const bool tileIsActive = (i % 2); + inGrid->fill(CoordBBox(Coord(8), Coord(15)), two, tileIsActive); + if (tileIsActive) { + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(512 + 8), inGrid->activeVoxelCount()); + } else { + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(8), inGrid->activeVoxelCount()); + } + // Verify that a voxel outside the cube has the background value. + CPPUNIT_ASSERT(openvdb::math::isExactlyEqual(inAcc.getValue(Coord(21, 0, 0)), background)); + CPPUNIT_ASSERT_EQUAL(false, inAcc.isValueOn(Coord(21, 0, 0))); + // Verify that a voxel inside the cube has value two. + CPPUNIT_ASSERT(openvdb::math::isExactlyEqual(inAcc.getValue(Coord(12)), two)); + CPPUNIT_ASSERT_EQUAL(tileIsActive, inAcc.isValueOn(Coord(12))); + + // Verify that the bounding box of all active values is 20 x 20 x 20. + CoordBBox activeVoxelBBox = inGrid->evalActiveVoxelBoundingBox(); + CPPUNIT_ASSERT(!activeVoxelBBox.empty()); + const Coord imin = activeVoxelBBox.min(), imax = activeVoxelBBox.max(); + CPPUNIT_ASSERT_EQUAL(Coord(0), imin); + CPPUNIT_ASSERT_EQUAL(Coord(20), imax); + + // Transform the corners of the input grid's bounding box + // and compute the enclosing bounding box in the output grid. + const openvdb::Mat4R xform = transformer.getTransform(); + const Vec3R + inRMin(imin.x(), imin.y(), imin.z()), + inRMax(imax.x(), imax.y(), imax.z()); + Vec3R outRMin, outRMax; + outRMin = outRMax = inRMin * xform; + for (int j = 0; j < 8; ++j) { + Vec3R corner( + j & 1 ? inRMax.x() : inRMin.x(), + j & 2 ? inRMax.y() : inRMin.y(), + j & 4 ? inRMax.z() : inRMin.z()); + outRMin = openvdb::math::minComponent(outRMin, corner * xform); + outRMax = openvdb::math::maxComponent(outRMax, corner * xform); + } + + CoordBBox bbox( + Coord(openvdb::tools::local_util::floorVec3(outRMin) - radius), + Coord(openvdb::tools::local_util::ceilVec3(outRMax) + radius)); + + // Transform the test grid. + typename GridType::Ptr outGrid = GridType::create(background); + transformer.transformGrid(*inGrid, *outGrid); + openvdb::tools::prune(outGrid->tree()); + + // Verify that the bounding box of the transformed grid + // matches the transformed bounding box of the original grid. + + activeVoxelBBox = outGrid->evalActiveVoxelBoundingBox(); + CPPUNIT_ASSERT(!activeVoxelBBox.empty()); + const openvdb::Vec3i + omin = activeVoxelBBox.min().asVec3i(), + omax = activeVoxelBBox.max().asVec3i(); + const int bboxTolerance = 1; // allow for rounding +#if 0 + if (!omin.eq(bbox.min().asVec3i(), bboxTolerance) || + !omax.eq(bbox.max().asVec3i(), bboxTolerance)) + { + std::cerr << "\nS = " << scale << ", R = " << rotate + << ", T = " << translate << ", P = " << pivot << "\n" + << xform.transpose() << "\n" << "computed bbox = " << bbox + << "\nactual bbox = " << omin << " -> " << omax << "\n"; + } +#endif + CPPUNIT_ASSERT(omin.eq(bbox.min().asVec3i(), bboxTolerance)); + CPPUNIT_ASSERT(omax.eq(bbox.max().asVec3i(), bboxTolerance)); + + // Verify that (a voxel in) the interior of the cube was + // transformed correctly. + const Coord center = Coord::round(Vec3R(12) * xform); + const typename GridType::TreeType& outTree = outGrid->tree(); + CPPUNIT_ASSERT(openvdb::math::isExactlyEqual(transformTiles ? two : background, + outTree.getValue(center))); + if (transformTiles && tileIsActive) CPPUNIT_ASSERT(outTree.isValueOn(center)); + else CPPUNIT_ASSERT(!outTree.isValueOn(center)); + } +} + + +//////////////////////////////////////// + + +void +TestGridTransformer::testResampleToMatch() +{ + using namespace openvdb; + + // Create an input grid with an identity transform. + FloatGrid inGrid; + // Populate it with a 20 x 20 x 20 cube. + inGrid.fill(CoordBBox(Coord(5), Coord(24)), /*value=*/1.0); + CPPUNIT_ASSERT_EQUAL(8000, int(inGrid.activeVoxelCount())); + CPPUNIT_ASSERT(inGrid.tree().activeTileCount() > 0); + + {//test identity transform + FloatGrid outGrid; + CPPUNIT_ASSERT(outGrid.transform() == inGrid.transform()); + // Resample the input grid into the output grid using point sampling. + tools::resampleToMatch(inGrid, outGrid); + CPPUNIT_ASSERT_EQUAL(int(inGrid.activeVoxelCount()), int(outGrid.activeVoxelCount())); + for (openvdb::FloatTree::ValueOnCIter iter = inGrid.tree().cbeginValueOn(); iter; ++iter) { + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter,outGrid.tree().getValue(iter.getCoord())); + } + // The output grid's transform should not have changed. + CPPUNIT_ASSERT(outGrid.transform() == inGrid.transform()); + } + + {//test nontrivial transform + // Create an output grid with a different transform. + math::Transform::Ptr xform = math::Transform::createLinearTransform(); + xform->preScale(Vec3d(0.5, 0.5, 1.0)); + FloatGrid outGrid; + outGrid.setTransform(xform); + CPPUNIT_ASSERT(outGrid.transform() != inGrid.transform()); + + // Resample the input grid into the output grid using point sampling. + tools::resampleToMatch(inGrid, outGrid); + + // The output grid's transform should not have changed. + CPPUNIT_ASSERT_EQUAL(*xform, outGrid.transform()); + + // The output grid should have double the resolution of the input grid + // in x and y and the same resolution in z. + CPPUNIT_ASSERT_EQUAL(32000, int(outGrid.activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(Coord(40, 40, 20), outGrid.evalActiveVoxelDim()); + CPPUNIT_ASSERT_EQUAL(CoordBBox(Coord(9, 9, 5), Coord(48, 48, 24)), + outGrid.evalActiveVoxelBoundingBox()); + for (auto it = outGrid.tree().cbeginValueOn(); it; ++it) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, *it, 1.0e-6); + } + } +} diff --git a/openvdb/unittest/TestIndexFilter.cc b/openvdb/unittest/TestIndexFilter.cc new file mode 100644 index 00000000..951c4145 --- /dev/null +++ b/openvdb/unittest/TestIndexFilter.cc @@ -0,0 +1,1084 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace openvdb; +using namespace openvdb::points; + +class TestIndexFilter: public CppUnit::TestCase +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestIndexFilter); + CPPUNIT_TEST(testActiveFilter); + CPPUNIT_TEST(testMultiGroupFilter); + CPPUNIT_TEST(testRandomLeafFilter); + CPPUNIT_TEST(testAttributeHashFilter); + CPPUNIT_TEST(testLevelSetFilter); + CPPUNIT_TEST(testBBoxFilter); + CPPUNIT_TEST(testBinaryFilter); + CPPUNIT_TEST_SUITE_END(); + + void testActiveFilter(); + void testMultiGroupFilter(); + void testRandomLeafFilter(); + void testAttributeHashFilter(); + void testLevelSetFilter(); + void testBBoxFilter(); + void testBinaryFilter(); +}; // class TestIndexFilter + +CPPUNIT_TEST_SUITE_REGISTRATION(TestIndexFilter); + + +//////////////////////////////////////// + + +struct OriginLeaf +{ + OriginLeaf(const openvdb::Coord& _leafOrigin, const size_t _size = size_t(0)): + leafOrigin(_leafOrigin), size(_size) { } + openvdb::Coord origin() const { return leafOrigin; } + size_t pointCount() const { return size; } + const openvdb::Coord leafOrigin; + const size_t size; +}; + + +struct SimpleIter +{ + SimpleIter() : i(0) { } + int operator*() const { return i; } + void operator++() { i++; } + openvdb::Coord getCoord() const { return coord; } + int i; + openvdb::Coord coord; +}; + + +template +class ThresholdFilter +{ +public: + ThresholdFilter(const int threshold) + : mThreshold(threshold) { } + + bool isPositiveInteger() const { return mThreshold > 0; } + bool isMax() const { return mThreshold == std::numeric_limits::max(); } + + static bool initialized() { return true; } + inline index::State state() const + { + if (LessThan) { + if (isMax()) return index::ALL; + else if (!isPositiveInteger()) return index::NONE; + } + else { + if (isMax()) return index::NONE; + else if (!isPositiveInteger()) return index::ALL; + } + return index::PARTIAL; + } + + template + static index::State state(const LeafT&) { return index::PARTIAL; } + + template + void reset(const LeafT&) { } + + template + bool valid(const IterT& iter) const { + return LessThan ? *iter < mThreshold : *iter > mThreshold; + } + +private: + const int mThreshold; +}; // class ThresholdFilter + + +/// @brief Generates the signed distance to a sphere located at @a center +/// and with a specified @a radius (both in world coordinates). Only voxels +/// in the domain [0,0,0] -> @a dim are considered. Also note that the +/// level set is either dense, dense narrow-band or sparse narrow-band. +/// +/// @note This method is VERY SLOW and should only be used for debugging purposes! +/// However it works for any transform and even with open level sets. +/// A faster approch for closed narrow band generation is to only set voxels +/// sparsely and then use grid::signedFloodFill to define the sign +/// of the background values and tiles! This is implemented in openvdb/tools/LevelSetSphere.h +template +inline void +makeSphere(const openvdb::Coord& dim, const openvdb::Vec3f& center, float radius, GridType& grid) +{ + using ValueT = typename GridType::ValueType; + const ValueT zero = openvdb::zeroVal(); + + typename GridType::Accessor acc = grid.getAccessor(); + openvdb::Coord xyz; + for (xyz[0]=0; xyz[0] +bool +multiGroupMatches( const LeafT& leaf, const Index32 size, + const std::vector& include, const std::vector& exclude, + const std::vector& indices) +{ + using IndexGroupIter = IndexIter; + ValueVoxelCIter indexIter(0, size); + MultiGroupFilter filter(include, exclude, leaf.attributeSet()); + filter.reset(leaf); + IndexGroupIter iter(indexIter, filter); + for (unsigned i = 0; i < indices.size(); ++i, ++iter) { + if (!iter) return false; + if (*iter != Index32(indices[i])) return false; + } + return !iter; +} + + +void +TestIndexFilter::testActiveFilter() +{ + // create a point grid, three points are stored in two leafs + + PointDataGrid::Ptr points; + std::vector positions{{1, 1, 1}, {1, 2, 1}, {10.1f, 10, 1}}; + + const double voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + points = createPointDataGrid(positions, *transform); + + // check there are two leafs + + CPPUNIT_ASSERT_EQUAL(Index32(2), points->tree().leafCount()); + + ActiveFilter activeFilter; + InactiveFilter inActiveFilter; + + CPPUNIT_ASSERT_EQUAL(index::PARTIAL, activeFilter.state()); + CPPUNIT_ASSERT_EQUAL(index::PARTIAL, inActiveFilter.state()); + + { // test default active / inactive values + auto leafIter = points->tree().cbeginLeaf(); + + CPPUNIT_ASSERT_EQUAL(index::PARTIAL, activeFilter.state(*leafIter)); + CPPUNIT_ASSERT_EQUAL(index::PARTIAL, inActiveFilter.state(*leafIter)); + + auto indexIter = leafIter->beginIndexAll(); + activeFilter.reset(*leafIter); + inActiveFilter.reset(*leafIter); + + CPPUNIT_ASSERT(activeFilter.valid(indexIter)); + CPPUNIT_ASSERT(!inActiveFilter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(activeFilter.valid(indexIter)); + CPPUNIT_ASSERT(!inActiveFilter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + ++leafIter; + + indexIter = leafIter->beginIndexAll(); + activeFilter.reset(*leafIter); + inActiveFilter.reset(*leafIter); + + CPPUNIT_ASSERT(activeFilter.valid(indexIter)); + CPPUNIT_ASSERT(!inActiveFilter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + } + + auto firstLeaf = points->tree().beginLeaf(); + + { // set all voxels to be inactive in the first leaf + firstLeaf->getValueMask().set(false); + + auto leafIter = points->tree().cbeginLeaf(); + + CPPUNIT_ASSERT_EQUAL(index::NONE, activeFilter.state(*leafIter)); + CPPUNIT_ASSERT_EQUAL(index::ALL, inActiveFilter.state(*leafIter)); + + auto indexIter = leafIter->beginIndexAll(); + activeFilter.reset(*leafIter); + inActiveFilter.reset(*leafIter); + + CPPUNIT_ASSERT(!activeFilter.valid(indexIter)); + CPPUNIT_ASSERT(inActiveFilter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!activeFilter.valid(indexIter)); + CPPUNIT_ASSERT(inActiveFilter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + ++leafIter; + + indexIter = leafIter->beginIndexAll(); + activeFilter.reset(*leafIter); + inActiveFilter.reset(*leafIter); + + CPPUNIT_ASSERT(activeFilter.valid(indexIter)); + CPPUNIT_ASSERT(!inActiveFilter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + } + + { // set all voxels to be active in the first leaf + firstLeaf->getValueMask().set(true); + + auto leafIter = points->tree().cbeginLeaf(); + + CPPUNIT_ASSERT_EQUAL(index::ALL, activeFilter.state(*leafIter)); + CPPUNIT_ASSERT_EQUAL(index::NONE, inActiveFilter.state(*leafIter)); + + auto indexIter = leafIter->beginIndexAll(); + activeFilter.reset(*leafIter); + inActiveFilter.reset(*leafIter); + + CPPUNIT_ASSERT(activeFilter.valid(indexIter)); + CPPUNIT_ASSERT(!inActiveFilter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(activeFilter.valid(indexIter)); + CPPUNIT_ASSERT(!inActiveFilter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + ++leafIter; + + indexIter = leafIter->beginIndexAll(); + activeFilter.reset(*leafIter); + inActiveFilter.reset(*leafIter); + + CPPUNIT_ASSERT(activeFilter.valid(indexIter)); + CPPUNIT_ASSERT(!inActiveFilter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + } +} + +void +TestIndexFilter::testMultiGroupFilter() +{ + using LeafNode = PointDataTree::LeafNodeType; + using AttributeVec3f = TypedAttributeArray; + + PointDataTree tree; + LeafNode* leaf = tree.touchLeaf(openvdb::Coord(0, 0, 0)); + + using Descriptor = AttributeSet::Descriptor; + Descriptor::Ptr descriptor = Descriptor::create(AttributeVec3f::attributeType()); + + const Index size = 5; + + leaf->initializeAttributes(descriptor, size); + + appendGroup(tree, "even"); + appendGroup(tree, "odd"); + appendGroup(tree, "all"); + appendGroup(tree, "first"); + + { // construction, copy construction + std::vector includeGroups; + std::vector excludeGroups; + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + CPPUNIT_ASSERT(!filter.initialized()); + MultiGroupFilter filter2 = filter; + CPPUNIT_ASSERT(!filter2.initialized()); + + filter.reset(*leaf); + CPPUNIT_ASSERT(filter.initialized()); + MultiGroupFilter filter3 = filter; + CPPUNIT_ASSERT(filter3.initialized()); + } + + // group population + + { // even + GroupWriteHandle groupHandle = leaf->groupWriteHandle("even"); + groupHandle.set(0, true); + groupHandle.set(2, true); + groupHandle.set(4, true); + } + + { // odd + GroupWriteHandle groupHandle = leaf->groupWriteHandle("odd"); + groupHandle.set(1, true); + groupHandle.set(3, true); + } + + setGroup(tree, "all", true); + + { // first + GroupWriteHandle groupHandle = leaf->groupWriteHandle("first"); + groupHandle.set(0, true); + } + + { // test state() + std::vector include; + std::vector exclude; + MultiGroupFilter filter(include, exclude, leaf->attributeSet()); + CPPUNIT_ASSERT_EQUAL(filter.state(), index::ALL); + include.push_back("all"); + MultiGroupFilter filter2(include, exclude, leaf->attributeSet()); + CPPUNIT_ASSERT_EQUAL(filter2.state(), index::PARTIAL); + } + + // test multi group iteration + + { // all (implicit, no include or exclude) + std::vector include; + std::vector exclude; + std::vector indices{0, 1, 2, 3, 4}; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // all include + std::vector include{"all"}; + std::vector exclude; + std::vector indices{0, 1, 2, 3, 4}; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // all exclude + std::vector include; + std::vector exclude{"all"}; + std::vector indices; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // all include and exclude + std::vector include{"all"}; + std::vector exclude{"all"}; + std::vector indices; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // even include + std::vector include{"even"}; + std::vector exclude; + std::vector indices{0, 2, 4}; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // odd include + std::vector include{"odd"}; + std::vector exclude; + std::vector indices{1, 3}; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // odd include and exclude + std::vector include{"odd"}; + std::vector exclude{"odd"}; + std::vector indices; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // odd and first include + std::vector include{"odd", "first"}; + std::vector exclude; + std::vector indices{0, 1, 3}; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // even include, first exclude + std::vector include{"even"}; + std::vector exclude{"first"}; + std::vector indices{2, 4}; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // all include, first and odd exclude + std::vector include{"all"}; + std::vector exclude{"first", "odd"}; + std::vector indices{2, 4}; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } + + { // odd and first include, even exclude + std::vector include{"odd", "first"}; + std::vector exclude{"even"}; + std::vector indices{1, 3}; + CPPUNIT_ASSERT(multiGroupMatches(*leaf, size, include, exclude, indices)); + } +} + + +void +TestIndexFilter::testRandomLeafFilter() +{ + { // generateRandomSubset + std::vector values = index_filter_internal::generateRandomSubset( + /*seed*/unsigned(0), 1, 20); + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(1)); + + // different seed + + std::vector values2 = index_filter_internal::generateRandomSubset( + /*seed*/unsigned(1), 1, 20); + + CPPUNIT_ASSERT_EQUAL(values2.size(), size_t(1)); + CPPUNIT_ASSERT(values[0] != values2[0]); + + // different integer type + + std::vector values3 = index_filter_internal::generateRandomSubset( + /*seed*/unsigned(0), 1, 20); + + CPPUNIT_ASSERT_EQUAL(values3.size(), size_t(1)); + CPPUNIT_ASSERT(values[0] == values3[0]); + + // different random number generator + + values = index_filter_internal::generateRandomSubset( + /*seed*/unsigned(1), 1, 20); + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(1)); + CPPUNIT_ASSERT(values[0] != values2[0]); + + // no values + + values = index_filter_internal::generateRandomSubset( + /*seed*/unsigned(0), 0, 20); + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(0)); + + // all values + + values = index_filter_internal::generateRandomSubset( + /*seed*/unsigned(0), 1000, 1000); + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(1000)); + + // ensure all numbers are represented + + std::sort(values.begin(), values.end()); + + for (int i = 0; i < 1000; i++) { + CPPUNIT_ASSERT_EQUAL(values[i], i); + } + } + + { // RandomLeafFilter + using RandFilter = RandomLeafFilter; + + PointDataTree tree; + + RandFilter filter(tree, 0); + + CPPUNIT_ASSERT(filter.state() == index::PARTIAL); + + filter.mLeafMap[Coord(0, 0, 0)] = std::make_pair(0, 10); + filter.mLeafMap[Coord(0, 0, 8)] = std::make_pair(1, 1); + filter.mLeafMap[Coord(0, 8, 0)] = std::make_pair(2, 50); + + { // construction, copy construction + CPPUNIT_ASSERT(filter.initialized()); + RandFilter filter2 = filter; + CPPUNIT_ASSERT(filter2.initialized()); + + filter.reset(OriginLeaf(Coord(0, 0, 0), 10)); + CPPUNIT_ASSERT(filter.initialized()); + RandFilter filter3 = filter; + CPPUNIT_ASSERT(filter3.initialized()); + } + + { // all 10 values + filter.reset(OriginLeaf(Coord(0, 0, 0), 10)); + std::vector values; + + for (SimpleIter iter; *iter < 100; ++iter) { + if (filter.valid(iter)) values.push_back(*iter); + } + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(10)); + + for (int i = 0; i < 10; i++) { + CPPUNIT_ASSERT_EQUAL(values[i], i); + } + } + + { // 50 of 100 + filter.reset(OriginLeaf(Coord(0, 8, 0), 100)); + std::vector values; + + for (SimpleIter iter; *iter < 100; ++iter) { + if (filter.valid(iter)) values.push_back(*iter); + } + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(50)); + + // ensure no duplicates + + std::sort(values.begin(), values.end()); + auto it = std::adjacent_find(values.begin(), values.end()); + + CPPUNIT_ASSERT(it == values.end()); + } + } +} + + +inline void +setId(PointDataTree& tree, const size_t index, const std::vector& ids) +{ + int offset = 0; + for (auto leafIter = tree.beginLeaf(); leafIter; ++leafIter) { + auto id = AttributeWriteHandle::create(leafIter->attributeArray(index)); + + for (auto iter = leafIter->beginIndexAll(); iter; ++iter) { + if (offset >= int(ids.size())) throw std::runtime_error("Out of range"); + + id->set(*iter, ids[offset++]); + } + } +} + + +void +TestIndexFilter::testAttributeHashFilter() +{ + std::vector positions{{1, 1, 1}, {2, 2, 2}, {11, 11, 11}, {12, 12, 12}}; + + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + PointDataGrid::Ptr grid = createPointDataGrid(positions, *transform); + PointDataTree& tree = grid->tree(); + + // four points, two leafs + + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), Index32(2)); + + appendAttribute(tree, "id"); + + const size_t index = tree.cbeginLeaf()->attributeSet().descriptor().find("id"); + + // ascending integers, block one + std::vector ids{1, 2, 3, 4}; + setId(tree, index, ids); + + using HashFilter = AttributeHashFilter; + + { // construction, copy construction + HashFilter filter(index, 0.0f); + CPPUNIT_ASSERT(filter.state() == index::PARTIAL); + CPPUNIT_ASSERT(!filter.initialized()); + HashFilter filter2 = filter; + CPPUNIT_ASSERT(!filter2.initialized()); + + filter.reset(*tree.cbeginLeaf()); + CPPUNIT_ASSERT(filter.initialized()); + HashFilter filter3 = filter; + CPPUNIT_ASSERT(filter3.initialized()); + } + + { // zero percent + HashFilter filter(index, 0.0f); + + auto leafIter = tree.cbeginLeaf(); + + auto indexIter = leafIter->beginIndexAll(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(!filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + ++leafIter; + + indexIter = leafIter->beginIndexAll(); + filter.reset(*leafIter); + CPPUNIT_ASSERT(!filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + } + + { // one hundred percent + HashFilter filter(index, 100.0f); + + auto leafIter = tree.cbeginLeaf(); + + auto indexIter = leafIter->beginIndexAll(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + ++leafIter; + + indexIter = leafIter->beginIndexAll(); + filter.reset(*leafIter); + CPPUNIT_ASSERT(filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + } + + { // fifty percent + HashFilter filter(index, 50.0f); + + auto leafIter = tree.cbeginLeaf(); + + auto indexIter = leafIter->beginIndexAll(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(!filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + ++leafIter; + + indexIter = leafIter->beginIndexAll(); + filter.reset(*leafIter); + CPPUNIT_ASSERT(filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + } + + { // fifty percent, new seed + HashFilter filter(index, 50.0f, /*seed=*/100); + + auto leafIter = tree.cbeginLeaf(); + + auto indexIter = leafIter->beginIndexAll(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(!filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + ++leafIter; + + indexIter = leafIter->beginIndexAll(); + filter.reset(*leafIter); + CPPUNIT_ASSERT(filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(filter.valid(indexIter)); + ++indexIter; + CPPUNIT_ASSERT(!indexIter); + } +} + + +void +TestIndexFilter::testLevelSetFilter() +{ + // create a point grid + + PointDataGrid::Ptr points; + + { + std::vector positions{{1, 1, 1}, {1, 2, 1}, {10.1f, 10, 1}}; + + const double voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + points = createPointDataGrid(positions, *transform); + } + + // create a sphere levelset + + FloatGrid::Ptr sphere; + + { + double voxelSize = 0.5; + sphere = FloatGrid::create(/*backgroundValue=*/5.0); + sphere->setTransform(math::Transform::createLinearTransform(voxelSize)); + + const openvdb::Coord dim(10, 10, 10); + const openvdb::Vec3f center(0.0f, 0.0f, 0.0f); + const float radius = 2; + makeSphere(dim, center, radius, *sphere); + } + + using LSFilter = LevelSetFilter; + + { // construction, copy construction + LSFilter filter(*sphere, points->transform(), -4.0f, 4.0f); + CPPUNIT_ASSERT(filter.state() == index::PARTIAL); + CPPUNIT_ASSERT(!filter.initialized()); + LSFilter filter2 = filter; + CPPUNIT_ASSERT(!filter2.initialized()); + + filter.reset(* points->tree().cbeginLeaf()); + CPPUNIT_ASSERT(filter.initialized()); + LSFilter filter3 = filter; + CPPUNIT_ASSERT(filter3.initialized()); + } + + { // capture both points near origin + LSFilter filter(*sphere, points->transform(), -4.0f, 4.0f); + auto leafIter = points->tree().cbeginLeaf(); + auto iter = leafIter->beginIndexOn(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!iter); + + ++leafIter; + iter = leafIter->beginIndexOn(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT(!filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!iter); + } + + { // capture just the inner-most point + LSFilter filter(*sphere, points->transform(), -0.3f, -0.25f); + auto leafIter = points->tree().cbeginLeaf(); + auto iter = leafIter->beginIndexOn(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!iter); + + ++leafIter; + iter = leafIter->beginIndexOn(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT(!filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!iter); + } + + { // capture everything but the second point (min > max) + LSFilter filter(*sphere, points->transform(), -0.25f, -0.3f); + auto leafIter = points->tree().cbeginLeaf(); + auto iter = leafIter->beginIndexOn(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(!filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!iter); + + ++leafIter; + iter = leafIter->beginIndexOn(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT(filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!iter); + } + + { + std::vector positions{{1, 1, 1}, {1, 2, 1}, {10.1f, 10, 1}}; + + const double voxelSize(0.25); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + points = createPointDataGrid(positions, *transform); + } + + { + double voxelSize = 1.0; + sphere = FloatGrid::create(/*backgroundValue=*/5.0); + sphere->setTransform(math::Transform::createLinearTransform(voxelSize)); + + const openvdb::Coord dim(40, 40, 40); + const openvdb::Vec3f center(10.0f, 10.0f, 0.1f); + const float radius = 0.2f; + makeSphere(dim, center, radius, *sphere); + } + + { // capture only the last point using a different transform and a new sphere + LSFilter filter(*sphere, points->transform(), 0.5f, 1.0f); + auto leafIter = points->tree().cbeginLeaf(); + auto iter = leafIter->beginIndexOn(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(!filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!iter); + + ++leafIter; + iter = leafIter->beginIndexOn(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(!filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!iter); + + ++leafIter; + iter = leafIter->beginIndexOn(); + filter.reset(*leafIter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT(filter.valid(iter)); + ++iter; + CPPUNIT_ASSERT(!iter); + } +} + + +void +TestIndexFilter::testBBoxFilter() +{ + std::vector positions{{1, 1, 1}, {1, 2, 1}, {10.1f, 10, 1}}; + + const float voxelSize(0.5); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + PointDataGrid::Ptr grid = createPointDataGrid(positions, *transform); + PointDataTree& tree = grid->tree(); + + // check one leaf per point + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), Index32(2)); + + // build some bounding box filters to test + + BBoxFilter filter1(*transform, BBoxd({0.5, 0.5, 0.5}, {1.5, 1.5, 1.5})); + BBoxFilter filter2(*transform, BBoxd({0.5, 0.5, 0.5}, {1.5, 2.01, 1.5})); + BBoxFilter filter3(*transform, BBoxd({0.5, 0.5, 0.5}, {11, 11, 1.5})); + BBoxFilter filter4(*transform, BBoxd({-10, 0, 0}, {11, 1.2, 1.2})); + + { // construction, copy construction + CPPUNIT_ASSERT(!filter1.initialized()); + BBoxFilter filter5 = filter1; + CPPUNIT_ASSERT(!filter5.initialized()); + + filter1.reset(*tree.cbeginLeaf()); + CPPUNIT_ASSERT(filter1.initialized()); + BBoxFilter filter6 = filter1; + CPPUNIT_ASSERT(filter6.initialized()); + } + + // leaf 1 + + auto leafIter = tree.cbeginLeaf(); + + { + auto iter(leafIter->beginIndexOn()); + + // point 1 + + filter1.reset(*leafIter); + CPPUNIT_ASSERT(filter1.valid(iter)); + filter2.reset(*leafIter); + CPPUNIT_ASSERT(filter2.valid(iter)); + filter3.reset(*leafIter); + CPPUNIT_ASSERT(filter3.valid(iter)); + filter4.reset(*leafIter); + CPPUNIT_ASSERT(filter4.valid(iter)); + + ++iter; + + // point 2 + + filter1.reset(*leafIter); + CPPUNIT_ASSERT(!filter1.valid(iter)); + filter2.reset(*leafIter); + CPPUNIT_ASSERT(filter2.valid(iter)); + filter3.reset(*leafIter); + CPPUNIT_ASSERT(filter3.valid(iter)); + filter4.reset(*leafIter); + CPPUNIT_ASSERT(!filter4.valid(iter)); + + ++iter; + CPPUNIT_ASSERT(!iter); + } + + ++leafIter; + + // leaf 2 + + { + auto iter(leafIter->beginIndexOn()); + + // point 3 + + filter1.reset(*leafIter); + CPPUNIT_ASSERT(!filter1.valid(iter)); + filter2.reset(*leafIter); + CPPUNIT_ASSERT(!filter2.valid(iter)); + filter3.reset(*leafIter); + CPPUNIT_ASSERT(filter3.valid(iter)); + filter4.reset(*leafIter); + CPPUNIT_ASSERT(!filter4.valid(iter)); + + ++iter; + CPPUNIT_ASSERT(!iter); + } +} + + +struct NeedsInitializeFilter +{ + inline bool initialized() const { return mInitialized; } + static index::State state() { return index::PARTIAL; } + template + inline index::State state(const LeafT&) { return index::PARTIAL; } + template + void reset(const LeafT&) { mInitialized = true; } +private: + bool mInitialized = false; +}; + + +void +TestIndexFilter::testBinaryFilter() +{ + const int intMax = std::numeric_limits::max(); + + { // construction, copy construction + using InitializeBinaryFilter = BinaryFilter; + + NeedsInitializeFilter needs1; + NeedsInitializeFilter needs2; + InitializeBinaryFilter filter(needs1, needs2); + CPPUNIT_ASSERT(filter.state() == index::PARTIAL); + CPPUNIT_ASSERT(!filter.initialized()); + InitializeBinaryFilter filter2 = filter; + CPPUNIT_ASSERT(!filter2.initialized()); + + filter.reset(OriginLeaf(Coord(0, 0, 0))); + CPPUNIT_ASSERT(filter.initialized()); + InitializeBinaryFilter filter3 = filter; + CPPUNIT_ASSERT(filter3.initialized()); + } + + using LessThanFilter = ThresholdFilter; + using GreaterThanFilter = ThresholdFilter; + + { // less than + LessThanFilter zeroFilter(0); // all invalid + CPPUNIT_ASSERT(zeroFilter.state() == index::NONE); + LessThanFilter maxFilter(intMax); // all valid + CPPUNIT_ASSERT(maxFilter.state() == index::ALL); + + LessThanFilter filter(5); + filter.reset(OriginLeaf(Coord(0, 0, 0))); + std::vector values; + + for (SimpleIter iter; *iter < 100; ++iter) { + if (filter.valid(iter)) values.push_back(*iter); + } + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(5)); + + for (int i = 0; i < 5; i++) { + CPPUNIT_ASSERT_EQUAL(values[i], i); + } + } + + { // greater than + GreaterThanFilter zeroFilter(0); // all valid + CPPUNIT_ASSERT(zeroFilter.state() == index::ALL); + GreaterThanFilter maxFilter(intMax); // all invalid + CPPUNIT_ASSERT(maxFilter.state() == index::NONE); + + GreaterThanFilter filter(94); + filter.reset(OriginLeaf(Coord(0, 0, 0))); + std::vector values; + + for (SimpleIter iter; *iter < 100; ++iter) { + if (filter.valid(iter)) values.push_back(*iter); + } + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(5)); + + int offset = 0; + for (int i = 95; i < 100; i++) { + CPPUNIT_ASSERT_EQUAL(values[offset++], i); + } + } + + { // binary and + using RangeFilter = BinaryFilter; + + RangeFilter zeroFilter(LessThanFilter(0), GreaterThanFilter(10)); // all invalid + CPPUNIT_ASSERT(zeroFilter.state() == index::NONE); + RangeFilter maxFilter(LessThanFilter(intMax), GreaterThanFilter(0)); // all valid + CPPUNIT_ASSERT(maxFilter.state() == index::ALL); + + RangeFilter filter(LessThanFilter(55), GreaterThanFilter(45)); + CPPUNIT_ASSERT(filter.state() == index::PARTIAL); + + filter.reset(OriginLeaf(Coord(0, 0, 0))); + + std::vector values; + + for (SimpleIter iter; *iter < 100; ++iter) { + if (filter.valid(iter)) values.push_back(*iter); + } + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(9)); + + int offset = 0; + for (int i = 46; i < 55; i++) { + CPPUNIT_ASSERT_EQUAL(values[offset++], i); + } + } + + { // binary or + using HeadTailFilter = BinaryFilter; + + HeadTailFilter zeroFilter(LessThanFilter(0), GreaterThanFilter(10)); // some valid + CPPUNIT_ASSERT(zeroFilter.state() == index::PARTIAL); + HeadTailFilter maxFilter(LessThanFilter(intMax), GreaterThanFilter(0)); // all valid + CPPUNIT_ASSERT(maxFilter.state() == index::ALL); + + HeadTailFilter filter(LessThanFilter(5), GreaterThanFilter(95)); + filter.reset(OriginLeaf(Coord(0, 0, 0))); + + std::vector values; + + for (SimpleIter iter; *iter < 100; ++iter) { + if (filter.valid(iter)) values.push_back(*iter); + } + + CPPUNIT_ASSERT_EQUAL(values.size(), size_t(9)); + + int offset = 0; + for (int i = 0; i < 5; i++) { + CPPUNIT_ASSERT_EQUAL(values[offset++], i); + } + for (int i = 96; i < 100; i++) { + CPPUNIT_ASSERT_EQUAL(values[offset++], i); + } + } +} diff --git a/openvdb/unittest/TestIndexIterator.cc b/openvdb/unittest/TestIndexIterator.cc new file mode 100644 index 00000000..00c7a66e --- /dev/null +++ b/openvdb/unittest/TestIndexIterator.cc @@ -0,0 +1,396 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + +#include +#include + +#include +#include +#include +#include //for setprecision + +using namespace openvdb; +using namespace openvdb::points; + +class TestIndexIterator: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestIndexIterator); + CPPUNIT_TEST(testNullFilter); + CPPUNIT_TEST(testValueIndexIterator); + CPPUNIT_TEST(testFilterIndexIterator); + CPPUNIT_TEST(testProfile); + + CPPUNIT_TEST_SUITE_END(); + + void testNullFilter(); + void testValueIndexIterator(); + void testFilterIndexIterator(); + void testProfile(); +}; // class TestIndexIterator + +CPPUNIT_TEST_SUITE_REGISTRATION(TestIndexIterator); + + +//////////////////////////////////////// + + +/// @brief Functionality similar to openvdb::util::CpuTimer except with prefix padding and no decimals. +/// +/// @code +/// ProfileTimer timer("algorithm 1"); +/// // code to be timed goes here +/// timer.stop(); +/// @endcode +class ProfileTimer +{ +public: + /// @brief Prints message and starts timer. + /// + /// @note Should normally be followed by a call to stop() + ProfileTimer(const std::string& msg) + { + (void)msg; +#ifdef PROFILE + // padd string to 50 characters + std::string newMsg(msg); + if (newMsg.size() < 50) newMsg.insert(newMsg.end(), 50 - newMsg.size(), ' '); + std::cerr << newMsg << " ... "; +#endif + mT0 = tbb::tick_count::now(); + } + + ~ProfileTimer() { this->stop(); } + + /// Return Time diference in milliseconds since construction or start was called. + inline double delta() const + { + tbb::tick_count::interval_t dt = tbb::tick_count::now() - mT0; + return 1000.0*dt.seconds(); + } + + /// @brief Print time in milliseconds since construction or start was called. + inline void stop() const + { +#ifdef PROFILE + std::stringstream ss; + ss << std::setw(6) << ::round(this->delta()); + std::cerr << "completed in " << ss.str() << " ms\n"; +#endif + } + +private: + tbb::tick_count mT0; +};// ProfileTimer + + +//////////////////////////////////////// + + +void +TestIndexIterator::testNullFilter() +{ + NullFilter filter; + CPPUNIT_ASSERT(filter.initialized()); + CPPUNIT_ASSERT(filter.state() == index::ALL); + int a; + CPPUNIT_ASSERT(filter.valid(a)); +} + + +void +TestIndexIterator::testValueIndexIterator() +{ + using namespace openvdb::tree; + + using LeafNode = LeafNode; + using ValueOnIter = LeafNode::ValueOnIter; + + const int size = LeafNode::SIZE; + + { // one per voxel offset, all active + LeafNode leafNode; + + for (int i = 0; i < size; i++) { + leafNode.setValueOn(i, i+1); + } + + ValueOnIter valueIter = leafNode.beginValueOn(); + + IndexIter::ValueIndexIter iter(valueIter); + + CPPUNIT_ASSERT(iter); + + CPPUNIT_ASSERT_EQUAL(iterCount(iter), Index64(size)); + + // check assignment operator + auto iter2 = iter; + CPPUNIT_ASSERT_EQUAL(iterCount(iter2), Index64(size)); + + ++iter; + + // check coord value + Coord xyz; + iter.getCoord(xyz); + CPPUNIT_ASSERT_EQUAL(xyz, openvdb::Coord(0, 0, 1)); + CPPUNIT_ASSERT_EQUAL(iter.getCoord(), openvdb::Coord(0, 0, 1)); + + // check iterators retrieval + CPPUNIT_ASSERT_EQUAL(iter.valueIter().getCoord(), openvdb::Coord(0, 0, 1)); + CPPUNIT_ASSERT_EQUAL(iter.end(), Index32(2)); + + ++iter; + + // check coord value + iter.getCoord(xyz); + CPPUNIT_ASSERT_EQUAL(xyz, openvdb::Coord(0, 1, 0)); + CPPUNIT_ASSERT_EQUAL(iter.getCoord(), openvdb::Coord(0, 1, 0)); + + // check iterators retrieval + CPPUNIT_ASSERT_EQUAL(iter.valueIter().getCoord(), openvdb::Coord(0, 1, 0)); + CPPUNIT_ASSERT_EQUAL(iter.end(), Index32(3)); + } + + { // one per even voxel offsets, only these active + LeafNode leafNode; + + int offset = 0; + + for (int i = 0; i < size; i++) + { + if ((i % 2) == 0) { + leafNode.setValueOn(i, ++offset); + } + else { + leafNode.setValueOff(i, offset); + } + } + + { + ValueOnIter valueIter = leafNode.beginValueOn(); + + IndexIter::ValueIndexIter iter(valueIter); + + CPPUNIT_ASSERT(iter); + + CPPUNIT_ASSERT_EQUAL(iterCount(iter), Index64(size/2)); + } + } + + { // one per odd voxel offsets, all active + LeafNode leafNode; + + int offset = 0; + + for (int i = 0; i < size; i++) + { + if ((i % 2) == 1) { + leafNode.setValueOn(i, offset++); + } + else { + leafNode.setValueOn(i, offset); + } + } + + { + ValueOnIter valueIter = leafNode.beginValueOn(); + + IndexIter::ValueIndexIter iter(valueIter); + + CPPUNIT_ASSERT(iter); + + CPPUNIT_ASSERT_EQUAL(iterCount(iter), Index64(3)); + } + } + + { // one per even voxel offsets, all active + LeafNode leafNode; + + int offset = 0; + + for (int i = 0; i < size; i++) + { + if ((i % 2) == 0) { + leafNode.setValueOn(i, offset++); + } + else { + leafNode.setValueOn(i, offset); + } + } + + { + ValueOnIter valueIter = leafNode.beginValueOn(); + + IndexIter::ValueIndexIter iter(valueIter); + + CPPUNIT_ASSERT(iter); + + CPPUNIT_ASSERT_EQUAL(iterCount(iter), Index64(size/2)); + } + } + + { // one per voxel offset, none active + LeafNode leafNode; + + for (int i = 0; i < size; i++) { + leafNode.setValueOff(i, i); + } + + ValueOnIter valueIter = leafNode.beginValueOn(); + + IndexIter::ValueIndexIter iter(valueIter); + + CPPUNIT_ASSERT(!iter); + + CPPUNIT_ASSERT_EQUAL(iterCount(iter), Index64(0)); + } +} + + +struct EvenIndexFilter +{ + static bool initialized() { return true; } + static bool all() { return false; } + static bool none() { return false; } + template + bool valid(const IterT& iter) const { + return ((*iter) % 2) == 0; + } +}; + + +struct OddIndexFilter +{ + static bool initialized() { return true; } + static bool all() { return false; } + static bool none() { return false; } + OddIndexFilter() : mFilter() { } + template + bool valid(const IterT& iter) const { + return !mFilter.valid(iter); + } +private: + EvenIndexFilter mFilter; +}; + + +struct ConstantIter +{ + ConstantIter(const int _value) : value(_value) { } + int operator*() const { return value; } + const int value; +}; + + +void +TestIndexIterator::testFilterIndexIterator() +{ + { // index iterator with even filter + EvenIndexFilter filter; + ValueVoxelCIter indexIter(0, 5); + IndexIter iter(indexIter, filter); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(0)); + + CPPUNIT_ASSERT(iter.next()); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(2)); + + CPPUNIT_ASSERT(iter.next()); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(4)); + + CPPUNIT_ASSERT(!iter.next()); + + CPPUNIT_ASSERT_EQUAL(iter.end(), Index32(5)); + CPPUNIT_ASSERT_EQUAL(filter.valid(ConstantIter(1)), iter.filter().valid(ConstantIter(1))); + CPPUNIT_ASSERT_EQUAL(filter.valid(ConstantIter(2)), iter.filter().valid(ConstantIter(2))); + } + + { // index iterator with odd filter + OddIndexFilter filter; + ValueVoxelCIter indexIter(0, 5); + IndexIter iter(indexIter, filter); + + CPPUNIT_ASSERT_EQUAL(*iter, Index32(1)); + + CPPUNIT_ASSERT(iter.next()); + CPPUNIT_ASSERT_EQUAL(*iter, Index32(3)); + + CPPUNIT_ASSERT(!iter.next()); + } +} + +void +TestIndexIterator::testProfile() +{ + using namespace openvdb::util; + using namespace openvdb::math; + using namespace openvdb::tree; + +#ifdef PROFILE + const int elements(1000 * 1000 * 1000); + + std::cerr << std::endl; +#else + const int elements(10 * 1000 * 1000); +#endif + + { // for loop + ProfileTimer timer("ForLoop: sum"); + volatile int sum = 0; + for (int i = 0; i < elements; i++) { + sum += i; + } + CPPUNIT_ASSERT(sum); + } + + { // index iterator + ProfileTimer timer("IndexIter: sum"); + volatile int sum = 0; + ValueVoxelCIter iter(0, elements); + for (; iter; ++iter) { + sum += *iter; + } + CPPUNIT_ASSERT(sum); + } + + using LeafNode = LeafNode; + LeafNode leafNode; + + const int size = LeafNode::SIZE; + + for (int i = 0; i < size - 1; i++) { + leafNode.setValueOn(i, (elements / size) * i); + } + leafNode.setValueOn(size - 1, elements); + + { // manual value iteration + ProfileTimer timer("ValueIteratorManual: sum"); + volatile int sum = 0; + auto indexIter(leafNode.cbeginValueOn()); + int offset = 0; + for (; indexIter; ++indexIter) { + int start = offset > 0 ? leafNode.getValue(offset - 1) : 0; + int end = leafNode.getValue(offset); + for (int i = start; i < end; i++) { + sum += i; + } + offset++; + } + CPPUNIT_ASSERT(sum); + } + + { // value on iterator (all on) + ProfileTimer timer("ValueIndexIter: sum"); + volatile int sum = 0; + auto indexIter(leafNode.cbeginValueAll()); + IndexIter::ValueIndexIter iter(indexIter); + for (; iter; ++iter) { + sum += *iter; + } + CPPUNIT_ASSERT(sum); + } +} diff --git a/openvdb/unittest/TestInit.cc b/openvdb/unittest/TestInit.cc new file mode 100644 index 00000000..fa02fb2b --- /dev/null +++ b/openvdb/unittest/TestInit.cc @@ -0,0 +1,91 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + + +class TestInit: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestInit); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); + + void test(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestInit); + + +void +TestInit::test() +{ + using namespace openvdb; + + initialize(); + + // data types + CPPUNIT_ASSERT(DoubleMetadata::isRegisteredType()); + CPPUNIT_ASSERT(FloatMetadata::isRegisteredType()); + CPPUNIT_ASSERT(Int32Metadata::isRegisteredType()); + CPPUNIT_ASSERT(Int64Metadata::isRegisteredType()); + CPPUNIT_ASSERT(StringMetadata::isRegisteredType()); + CPPUNIT_ASSERT(Vec2IMetadata::isRegisteredType()); + CPPUNIT_ASSERT(Vec2SMetadata::isRegisteredType()); + CPPUNIT_ASSERT(Vec2DMetadata::isRegisteredType()); + CPPUNIT_ASSERT(Vec3IMetadata::isRegisteredType()); + CPPUNIT_ASSERT(Vec3SMetadata::isRegisteredType()); + CPPUNIT_ASSERT(Vec3DMetadata::isRegisteredType()); + + // map types + CPPUNIT_ASSERT(math::AffineMap::isRegistered()); + CPPUNIT_ASSERT(math::UnitaryMap::isRegistered()); + CPPUNIT_ASSERT(math::ScaleMap::isRegistered()); + CPPUNIT_ASSERT(math::TranslationMap::isRegistered()); + CPPUNIT_ASSERT(math::ScaleTranslateMap::isRegistered()); + CPPUNIT_ASSERT(math::NonlinearFrustumMap::isRegistered()); + + // grid types + CPPUNIT_ASSERT(BoolGrid::isRegistered()); + CPPUNIT_ASSERT(FloatGrid::isRegistered()); + CPPUNIT_ASSERT(DoubleGrid::isRegistered()); + CPPUNIT_ASSERT(Int32Grid::isRegistered()); + CPPUNIT_ASSERT(Int64Grid::isRegistered()); + CPPUNIT_ASSERT(StringGrid::isRegistered()); + CPPUNIT_ASSERT(Vec3IGrid::isRegistered()); + CPPUNIT_ASSERT(Vec3SGrid::isRegistered()); + CPPUNIT_ASSERT(Vec3DGrid::isRegistered()); + + uninitialize(); + + CPPUNIT_ASSERT(!DoubleMetadata::isRegisteredType()); + CPPUNIT_ASSERT(!FloatMetadata::isRegisteredType()); + CPPUNIT_ASSERT(!Int32Metadata::isRegisteredType()); + CPPUNIT_ASSERT(!Int64Metadata::isRegisteredType()); + CPPUNIT_ASSERT(!StringMetadata::isRegisteredType()); + CPPUNIT_ASSERT(!Vec2IMetadata::isRegisteredType()); + CPPUNIT_ASSERT(!Vec2SMetadata::isRegisteredType()); + CPPUNIT_ASSERT(!Vec2DMetadata::isRegisteredType()); + CPPUNIT_ASSERT(!Vec3IMetadata::isRegisteredType()); + CPPUNIT_ASSERT(!Vec3SMetadata::isRegisteredType()); + CPPUNIT_ASSERT(!Vec3DMetadata::isRegisteredType()); + + CPPUNIT_ASSERT(!math::AffineMap::isRegistered()); + CPPUNIT_ASSERT(!math::UnitaryMap::isRegistered()); + CPPUNIT_ASSERT(!math::ScaleMap::isRegistered()); + CPPUNIT_ASSERT(!math::TranslationMap::isRegistered()); + CPPUNIT_ASSERT(!math::ScaleTranslateMap::isRegistered()); + CPPUNIT_ASSERT(!math::NonlinearFrustumMap::isRegistered()); + + CPPUNIT_ASSERT(!BoolGrid::isRegistered()); + CPPUNIT_ASSERT(!FloatGrid::isRegistered()); + CPPUNIT_ASSERT(!DoubleGrid::isRegistered()); + CPPUNIT_ASSERT(!Int32Grid::isRegistered()); + CPPUNIT_ASSERT(!Int64Grid::isRegistered()); + CPPUNIT_ASSERT(!StringGrid::isRegistered()); + CPPUNIT_ASSERT(!Vec3IGrid::isRegistered()); + CPPUNIT_ASSERT(!Vec3SGrid::isRegistered()); + CPPUNIT_ASSERT(!Vec3DGrid::isRegistered()); +} diff --git a/openvdb/unittest/TestInt32Metadata.cc b/openvdb/unittest/TestInt32Metadata.cc new file mode 100644 index 00000000..ae0f77e3 --- /dev/null +++ b/openvdb/unittest/TestInt32Metadata.cc @@ -0,0 +1,43 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + +class TestInt32Metadata : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestInt32Metadata); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); + + void test(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestInt32Metadata); + +void +TestInt32Metadata::test() +{ + using namespace openvdb; + + Metadata::Ptr m(new Int32Metadata(123)); + Metadata::Ptr m2 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0); + + CPPUNIT_ASSERT(m->typeName().compare("int32") == 0); + CPPUNIT_ASSERT(m2->typeName().compare("int32") == 0); + + Int32Metadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == 123); + s->value() = 456; + CPPUNIT_ASSERT(s->value() == 456); + + m2->copy(*s); + + s = dynamic_cast(m2.get()); + CPPUNIT_ASSERT(s->value() == 456); +} diff --git a/openvdb/unittest/TestInt64Metadata.cc b/openvdb/unittest/TestInt64Metadata.cc new file mode 100644 index 00000000..460ee9c6 --- /dev/null +++ b/openvdb/unittest/TestInt64Metadata.cc @@ -0,0 +1,43 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + +class TestInt64Metadata : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestInt64Metadata); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); + + void test(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestInt64Metadata); + +void +TestInt64Metadata::test() +{ + using namespace openvdb; + + Metadata::Ptr m(new Int64Metadata(123)); + Metadata::Ptr m2 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0); + + CPPUNIT_ASSERT(m->typeName().compare("int64") == 0); + CPPUNIT_ASSERT(m2->typeName().compare("int64") == 0); + + Int64Metadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == 123); + s->value() = 456; + CPPUNIT_ASSERT(s->value() == 456); + + m2->copy(*s); + + s = dynamic_cast(m2.get()); + CPPUNIT_ASSERT(s->value() == 456); +} diff --git a/openvdb/unittest/TestInternalOrigin.cc b/openvdb/unittest/TestInternalOrigin.cc new file mode 100644 index 00000000..f757fca7 --- /dev/null +++ b/openvdb/unittest/TestInternalOrigin.cc @@ -0,0 +1,75 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + +class TestInternalOrigin: public CppUnit::TestCase +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestInternalOrigin); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); + + void test(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestInternalOrigin); + +void +TestInternalOrigin::test() +{ + std::set indices; + indices.insert(openvdb::Coord( 0, 0, 0)); + indices.insert(openvdb::Coord( 1, 0, 0)); + indices.insert(openvdb::Coord( 0,100, 8)); + indices.insert(openvdb::Coord(-9, 0, 8)); + indices.insert(openvdb::Coord(32, 0, 16)); + indices.insert(openvdb::Coord(33, -5, 16)); + indices.insert(openvdb::Coord(42,707,-35)); + indices.insert(openvdb::Coord(43, 17, 64)); + + typedef openvdb::tree::Tree4::Type FloatTree4; + FloatTree4 tree(0.0f); + std::set::iterator iter=indices.begin(); + for (int n = 0; iter != indices.end(); ++n, ++iter) { + tree.setValue(*iter, float(1.0 + double(n) * 0.5)); + } + + openvdb::Coord C3, G; + typedef FloatTree4::RootNodeType Node0; + typedef Node0::ChildNodeType Node1; + typedef Node1::ChildNodeType Node2; + typedef Node2::LeafNodeType Node3; + for (Node0::ChildOnCIter iter0=tree.root().cbeginChildOn(); iter0; ++iter0) {//internal 1 + openvdb::Coord C0=iter0->origin(); + iter0.getCoord(G); + CPPUNIT_ASSERT_EQUAL(C0,G); + for (Node1::ChildOnCIter iter1=iter0->cbeginChildOn(); iter1; ++iter1) {//internal 2 + openvdb::Coord C1=iter1->origin(); + iter1.getCoord(G); + CPPUNIT_ASSERT_EQUAL(C1,G); + CPPUNIT_ASSERT(C0 <= C1); + CPPUNIT_ASSERT(C1 <= C0 + openvdb::Coord(Node1::DIM,Node1::DIM,Node1::DIM)); + for (Node2::ChildOnCIter iter2=iter1->cbeginChildOn(); iter2; ++iter2) {//leafs + openvdb::Coord C2=iter2->origin(); + iter2.getCoord(G); + CPPUNIT_ASSERT_EQUAL(C2,G); + CPPUNIT_ASSERT(C1 <= C2); + CPPUNIT_ASSERT(C2 <= C1 + openvdb::Coord(Node2::DIM,Node2::DIM,Node2::DIM)); + for (Node3::ValueOnCIter iter3=iter2->cbeginValueOn(); iter3; ++iter3) {//leaf voxels + iter3.getCoord(G); + iter = indices.find(G); + CPPUNIT_ASSERT(iter != indices.end()); + indices.erase(iter); + } + } + } + } + CPPUNIT_ASSERT(indices.size() == 0); +} diff --git a/openvdb/unittest/TestLaplacian.cc b/openvdb/unittest/TestLaplacian.cc new file mode 100644 index 00000000..31db23d6 --- /dev/null +++ b/openvdb/unittest/TestLaplacian.cc @@ -0,0 +1,462 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() +#include +#include + + +class TestLaplacian: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestLaplacian); + CPPUNIT_TEST(testISLaplacian); // Laplacian in Index Space + CPPUNIT_TEST(testISLaplacianStencil); + CPPUNIT_TEST(testWSLaplacian); // Laplacian in World Space + CPPUNIT_TEST(testWSLaplacianFrustum); // Laplacian in World Space + CPPUNIT_TEST(testWSLaplacianStencil); + CPPUNIT_TEST(testLaplacianTool); // Laplacian tool + CPPUNIT_TEST(testLaplacianMaskedTool); // Laplacian tool + CPPUNIT_TEST(testOldStyleStencils); // old stencil impl + CPPUNIT_TEST_SUITE_END(); + + void testISLaplacian(); + void testISLaplacianStencil(); + void testWSLaplacian(); + void testWSLaplacianFrustum(); + void testWSLaplacianStencil(); + void testLaplacianTool(); + void testLaplacianMaskedTool(); + void testOldStyleStencils(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLaplacian); + +void +TestLaplacian::testISLaplacian() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Coord dim(64,64,64); + const Coord c(35,30,40); + const openvdb::Vec3f + center(static_cast(c[0]), static_cast(c[1]), static_cast(c[2])); + const float radius=0.0f;//point at {35,30,40} + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + Coord xyz(35,10,40); + + // Index Space Laplacian random access + FloatGrid::ConstAccessor inAccessor = grid->getConstAccessor(); + FloatGrid::ValueType result; + result = math::ISLaplacian::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20.0, result, /*tolerance=*/0.01); + + result = math::ISLaplacian::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20.0, result, /*tolerance=*/0.01); + + result = math::ISLaplacian::result(inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20.0, result, /*tolerance=*/0.01); +} + + +void +TestLaplacian::testISLaplacianStencil() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Coord dim(64,64,64); + const Coord c(35,30,40); + const openvdb::Vec3f + center(static_cast(c[0]), static_cast(c[1]), static_cast(c[2])); + const float radius=0;//point at {35,30,40} + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + Coord xyz(35,10,40); + + // Index Space Laplacian stencil access + FloatGrid::ValueType result; + + math::SevenPointStencil sevenpt(*grid); + sevenpt.moveTo(xyz); + result = math::ISLaplacian::result(sevenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20.0, result, /*tolerance=*/0.01); + + math::ThirteenPointStencil thirteenpt(*grid); + thirteenpt.moveTo(xyz); + result = math::ISLaplacian::result(thirteenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20.0, result, /*tolerance=*/0.01); + + math::NineteenPointStencil nineteenpt(*grid); + nineteenpt.moveTo(xyz); + result = math::ISLaplacian::result(nineteenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20.0, result, /*tolerance=*/0.01); +} + + +void +TestLaplacian::testWSLaplacian() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Coord dim(64,64,64); + const Coord c(35,30,40); + const openvdb::Vec3f + center(static_cast(c[0]), static_cast(c[1]), static_cast(c[2])); + const float radius=0.0f;//point at {35,30,40} + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + Coord xyz(35,10,40); + + FloatGrid::ValueType result; + FloatGrid::ConstAccessor inAccessor = grid->getConstAccessor(); + + // try with a map + math::UniformScaleMap map; + math::MapBase::Ptr rotated_map = map.preRotate(1.5, math::X_AXIS); + // verify the new map is an affine map + CPPUNIT_ASSERT(rotated_map->type() == math::AffineMap::mapType()); + math::AffineMap::Ptr affine_map = StaticPtrCast(rotated_map); + + // the laplacian is invariant to rotation + result = math::Laplacian::result( + *affine_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + result = math::Laplacian::result( + *affine_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + result = math::Laplacian::result( + *affine_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + + // test uniform map + math::UniformScaleMap uniform; + + result = math::Laplacian::result( + uniform, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + result = math::Laplacian::result( + uniform, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + result = math::Laplacian::result( + uniform, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + + // test the GenericMap Grid interface + { + math::GenericMap generic_map(*grid); + result = math::Laplacian::result( + generic_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + + result = math::Laplacian::result( + generic_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + } + { + // test the GenericMap Transform interface + math::GenericMap generic_map(grid->transform()); + result = math::Laplacian::result( + generic_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + + } + { + // test the GenericMap Map interface + math::GenericMap generic_map(rotated_map); + result = math::Laplacian::result( + generic_map, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + } + +} + + +void +TestLaplacian::testWSLaplacianFrustum() +{ + using namespace openvdb; + + // Create a Frustum Map: + + openvdb::BBoxd bbox(Vec3d(0), Vec3d(100)); + math::NonlinearFrustumMap frustum(bbox, 1./6., 5); + /// frustum will have depth, far plane - near plane = 5 + /// the frustum has width 1 in the front and 6 in the back + + math::Vec3d trans(2,2,2); + math::NonlinearFrustumMap::Ptr map = + StaticPtrCast( + frustum.preScale(Vec3d(10,10,10))->postTranslate(trans)); + + CPPUNIT_ASSERT(!map->hasUniformScale()); + + math::Vec3d result; + result = map->voxelSize(); + + CPPUNIT_ASSERT( math::isApproxEqual(result.x(), 0.1)); + CPPUNIT_ASSERT( math::isApproxEqual(result.y(), 0.1)); + CPPUNIT_ASSERT( math::isApproxEqual(result.z(), 0.5, 0.0001)); + + + // Create a tree + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/0.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + // Load cos(x)sin(y)cos(z) + Coord ijk(10,10,10); + for (Int32& i=ijk.x(); i < 20; ++i) { + for (Int32& j=ijk.y(); j < 20; ++j) { + for (Int32& k=ijk.z(); k < 20; ++k) { + // world space image of the ijk coord + const Vec3d ws = map->applyMap(ijk.asVec3d()); + const float value = float(cos(ws.x() ) * sin( ws.y()) * cos(ws.z())); + tree.setValue(ijk, value); + } + } + } + + const Coord testloc(16,16,16); + float test_result = math::Laplacian::result( + *map, tree, testloc); + float expected_result = -3.f * tree.getValue(testloc); + + // The exact solution of Laplacian( cos(x)sin(y)cos(z) ) = -3 cos(x) sin(y) cos(z) + + CPPUNIT_ASSERT( math::isApproxEqual(test_result, expected_result, /*tolerance=*/0.02f) ); +} + + +void +TestLaplacian::testWSLaplacianStencil() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Coord dim(64,64,64); + const Coord c(35,30,40); + const openvdb::Vec3f + center(static_cast(c[0]), static_cast(c[1]), static_cast(c[2])); + const float radius=0.0f;//point at {35,30,40} + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + Coord xyz(35,10,40); + + FloatGrid::ValueType result; + + // try with a map + math::UniformScaleMap map; + math::MapBase::Ptr rotated_map = map.preRotate(1.5, math::X_AXIS); + // verify the new map is an affine map + CPPUNIT_ASSERT(rotated_map->type() == math::AffineMap::mapType()); + math::AffineMap::Ptr affine_map = StaticPtrCast(rotated_map); + + // the laplacian is invariant to rotation + math::SevenPointStencil sevenpt(*grid); + math::ThirteenPointStencil thirteenpt(*grid); + math::NineteenPointStencil nineteenpt(*grid); + math::SecondOrderDenseStencil dense_2nd(*grid); + math::FourthOrderDenseStencil dense_4th(*grid); + math::SixthOrderDenseStencil dense_6th(*grid); + sevenpt.moveTo(xyz); + thirteenpt.moveTo(xyz); + nineteenpt.moveTo(xyz); + dense_2nd.moveTo(xyz); + dense_4th.moveTo(xyz); + dense_6th.moveTo(xyz); + + result = math::Laplacian::result(*affine_map, dense_2nd); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + result = math::Laplacian::result(*affine_map, dense_4th); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + result = math::Laplacian::result(*affine_map, dense_6th); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + + // test uniform map + math::UniformScaleMap uniform; + + result = math::Laplacian::result(uniform, sevenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + result = math::Laplacian::result(uniform, thirteenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + result = math::Laplacian::result(uniform, nineteenpt); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + + // test the GenericMap Grid interface + { + math::GenericMap generic_map(*grid); + result = math::Laplacian::result(generic_map, dense_2nd); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + + result = math::Laplacian::result(generic_map, dense_4th); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + } + { + // test the GenericMap Transform interface + math::GenericMap generic_map(grid->transform()); + result = math::Laplacian::result(generic_map, dense_2nd); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + + } + { + // test the GenericMap Map interface + math::GenericMap generic_map(rotated_map); + result = math::Laplacian::result(generic_map, dense_2nd); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/20., result, /*tolerance=*/0.01); + } +} + + +void +TestLaplacian::testOldStyleStencils() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(/*voxel size=*/0.5)); + CPPUNIT_ASSERT(grid->empty()); + + const Coord dim(32, 32, 32); + const Coord c(35,30,40); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + + math::GradStencil gs(*grid); + math::WenoStencil ws(*grid); + math::CurvatureStencil cs(*grid); + + Coord xyz(20,16,20);//i.e. 8 voxel or 4 world units away from the center + gs.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/4.0, gs.laplacian(), 0.01);// 2/distance from center + + ws.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/4.0, ws.laplacian(), 0.01);// 2/distance from center + + cs.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/4.0, cs.laplacian(), 0.01);// 2/distance from center + + xyz.reset(12,16,10);//i.e. 10 voxel or 5 world units away from the center + gs.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/5.0, gs.laplacian(), 0.01);// 2/distance from center + + ws.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/5.0, ws.laplacian(), 0.01);// 2/distance from center + + cs.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0/5.0, cs.laplacian(), 0.01);// 2/distance from center +} + + +void +TestLaplacian::testLaplacianTool() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Coord dim(64, 64, 64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f); + const float radius=0.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + FloatGrid::Ptr lap = tools::laplacian(*grid); + CPPUNIT_ASSERT_EQUAL(int(tree.activeVoxelCount()), int(lap->activeVoxelCount())); + + Coord xyz(35,30,30); + + CPPUNIT_ASSERT_DOUBLES_EQUAL( + 2.0/10.0, lap->getConstAccessor().getValue(xyz), 0.01);// 2/distance from center + + xyz.reset(35,10,40); + + CPPUNIT_ASSERT_DOUBLES_EQUAL( + 2.0/20.0, lap->getConstAccessor().getValue(xyz),0.01);// 2/distance from center +} + +void +TestLaplacian::testLaplacianMaskedTool() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Coord dim(64, 64, 64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f); + const float radius=0.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + const openvdb::CoordBBox maskbbox(openvdb::Coord(35, 30, 30), openvdb::Coord(41, 41, 41)); + BoolGrid::Ptr maskGrid = BoolGrid::create(false); + maskGrid->fill(maskbbox, true/*value*/, true/*activate*/); + + + FloatGrid::Ptr lap = tools::laplacian(*grid, *maskGrid); + + {// outside the masked region + Coord xyz(34,30,30); + + CPPUNIT_ASSERT(!maskbbox.isInside(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL( + 0, lap->getConstAccessor().getValue(xyz), 0.01);// 2/distance from center + + xyz.reset(35,10,40); + + CPPUNIT_ASSERT_DOUBLES_EQUAL( + 0, lap->getConstAccessor().getValue(xyz),0.01);// 2/distance from center + } + + {// inside the masked region + Coord xyz(35,30,30); + + CPPUNIT_ASSERT(maskbbox.isInside(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL( + 2.0/10.0, lap->getConstAccessor().getValue(xyz), 0.01);// 2/distance from center + + } +} diff --git a/openvdb/unittest/TestLeaf.cc b/openvdb/unittest/TestLeaf.cc new file mode 100644 index 00000000..6688c695 --- /dev/null +++ b/openvdb/unittest/TestLeaf.cc @@ -0,0 +1,535 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include // for math::Random01(), math::Pow3() + +class TestLeaf: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestLeaf); + CPPUNIT_TEST(testBuffer); + CPPUNIT_TEST(testGetValue); + CPPUNIT_TEST(testSetValue); + CPPUNIT_TEST(testIsValueSet); + CPPUNIT_TEST(testProbeValue); + CPPUNIT_TEST(testIterators); + CPPUNIT_TEST(testEquivalence); + CPPUNIT_TEST(testGetOrigin); + CPPUNIT_TEST(testIteratorGetCoord); + CPPUNIT_TEST(testNegativeIndexing); + CPPUNIT_TEST(testIsConstant); + CPPUNIT_TEST(testMedian); + CPPUNIT_TEST(testFill); + CPPUNIT_TEST_SUITE_END(); + + void testBuffer(); + void testGetValue(); + void testSetValue(); + void testIsValueSet(); + void testProbeValue(); + void testIterators(); + void testEquivalence(); + void testGetOrigin(); + void testIteratorGetCoord(); + void testNegativeIndexing(); + void testIsConstant(); + void testMedian(); + void testFill(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeaf); + +typedef openvdb::tree::LeafNode LeafType; +typedef LeafType::Buffer BufferType; +using openvdb::Index; + +void +TestLeaf::testBuffer() +{ + {// access + BufferType buf; + + for (Index i = 0; i < BufferType::size(); ++i) { + buf.mData[i] = i; + CPPUNIT_ASSERT(buf[i] == buf.mData[i]); + } + for (Index i = 0; i < BufferType::size(); ++i) { + buf[i] = i; + CPPUNIT_ASSERT_EQUAL(int(i), buf[i]); + } + } + + {// swap + BufferType buf0, buf1, buf2; + + int *buf0Data = buf0.mData; + int *buf1Data = buf1.mData; + + for (Index i = 0; i < BufferType::size(); ++i) { + buf0[i] = i; + buf1[i] = i * 2; + } + + buf0.swap(buf1); + + CPPUNIT_ASSERT(buf0.mData == buf1Data); + CPPUNIT_ASSERT(buf1.mData == buf0Data); + + buf1.swap(buf0); + + CPPUNIT_ASSERT(buf0.mData == buf0Data); + CPPUNIT_ASSERT(buf1.mData == buf1Data); + + buf0.swap(buf2); + + CPPUNIT_ASSERT(buf2.mData == buf0Data); + + buf2.swap(buf0); + + CPPUNIT_ASSERT(buf0.mData == buf0Data); + } + +} + +void +TestLeaf::testGetValue() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + + leaf.mBuffer[0] = 2; + leaf.mBuffer[1] = 3; + leaf.mBuffer[2] = 4; + leaf.mBuffer[65] = 10; + + CPPUNIT_ASSERT_EQUAL(2, leaf.getValue(openvdb::Coord(0, 0, 0))); + CPPUNIT_ASSERT_EQUAL(3, leaf.getValue(openvdb::Coord(0, 0, 1))); + CPPUNIT_ASSERT_EQUAL(4, leaf.getValue(openvdb::Coord(0, 0, 2))); + + CPPUNIT_ASSERT_EQUAL(10, leaf.getValue(openvdb::Coord(1, 0, 1))); +} + +void +TestLeaf::testSetValue() +{ + LeafType leaf(openvdb::Coord(0, 0, 0), 3); + + openvdb::Coord xyz(0, 0, 0); + leaf.setValueOn(xyz, 10); + CPPUNIT_ASSERT_EQUAL(10, leaf.getValue(xyz)); + + xyz.reset(7, 7, 7); + leaf.setValueOn(xyz, 7); + CPPUNIT_ASSERT_EQUAL(7, leaf.getValue(xyz)); + leaf.setValueOnly(xyz, 10); + CPPUNIT_ASSERT_EQUAL(10, leaf.getValue(xyz)); + + xyz.reset(2, 3, 6); + leaf.setValueOn(xyz, 236); + CPPUNIT_ASSERT_EQUAL(236, leaf.getValue(xyz)); + + leaf.setValueOff(xyz, 1); + CPPUNIT_ASSERT_EQUAL(1, leaf.getValue(xyz)); + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); +} + +void +TestLeaf::testIsValueSet() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.setValueOn(openvdb::Coord(1, 5, 7), 10); + + CPPUNIT_ASSERT(leaf.isValueOn(openvdb::Coord(1, 5, 7))); + + CPPUNIT_ASSERT(!leaf.isValueOn(openvdb::Coord(0, 5, 7))); + CPPUNIT_ASSERT(!leaf.isValueOn(openvdb::Coord(1, 6, 7))); + CPPUNIT_ASSERT(!leaf.isValueOn(openvdb::Coord(0, 5, 6))); +} + +void +TestLeaf::testProbeValue() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.setValueOn(openvdb::Coord(1, 6, 5), 10); + + LeafType::ValueType val; + CPPUNIT_ASSERT(leaf.probeValue(openvdb::Coord(1, 6, 5), val)); + CPPUNIT_ASSERT(!leaf.probeValue(openvdb::Coord(1, 6, 4), val)); +} + +void +TestLeaf::testIterators() +{ + LeafType leaf(openvdb::Coord(0, 0, 0), 2); + leaf.setValueOn(openvdb::Coord(1, 2, 3), -3); + leaf.setValueOn(openvdb::Coord(5, 2, 3), 4); + LeafType::ValueType sum = 0; + for (LeafType::ValueOnIter iter = leaf.beginValueOn(); iter; ++iter) sum += *iter; + CPPUNIT_ASSERT_EQUAL((-3 + 4), sum); +} + +void +TestLeaf::testEquivalence() +{ + LeafType leaf( openvdb::Coord(0, 0, 0), 2); + LeafType leaf2(openvdb::Coord(0, 0, 0), 3); + + CPPUNIT_ASSERT(leaf != leaf2); + + for(openvdb::Index32 i = 0; i < LeafType::size(); ++i) { + leaf.setValueOnly(i, i); + leaf2.setValueOnly(i, i); + } + CPPUNIT_ASSERT(leaf == leaf2); + + // set some values. + leaf.setValueOn(openvdb::Coord(0, 0, 0), 1); + leaf.setValueOn(openvdb::Coord(0, 1, 0), 1); + leaf.setValueOn(openvdb::Coord(1, 1, 0), 1); + leaf.setValueOn(openvdb::Coord(1, 1, 2), 1); + + leaf2.setValueOn(openvdb::Coord(0, 0, 0), 1); + leaf2.setValueOn(openvdb::Coord(0, 1, 0), 1); + leaf2.setValueOn(openvdb::Coord(1, 1, 0), 1); + leaf2.setValueOn(openvdb::Coord(1, 1, 2), 1); + + CPPUNIT_ASSERT(leaf == leaf2); + + leaf2.setValueOn(openvdb::Coord(0, 0, 1), 1); + + CPPUNIT_ASSERT(leaf != leaf2); + + leaf2.setValueOff(openvdb::Coord(0, 0, 1), 1); + + CPPUNIT_ASSERT(leaf == leaf2); +} + +void +TestLeaf::testGetOrigin() +{ + { + LeafType leaf(openvdb::Coord(1, 0, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(0, 0, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(8, 0, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(8, 1, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(1024, 1, 3), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(128*8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(1023, 1, 3), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(127*8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(512, 512, 512), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(512, 512, 512), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(2, 52, 515), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 48, 512), leaf.origin()); + } +} + +void +TestLeaf::testIteratorGetCoord() +{ + using namespace openvdb; + + LeafType leaf(openvdb::Coord(8, 8, 0), 2); + + CPPUNIT_ASSERT_EQUAL(Coord(8, 8, 0), leaf.origin()); + + leaf.setValueOn(Coord(1, 2, 3), -3); + leaf.setValueOn(Coord(5, 2, 3), 4); + + LeafType::ValueOnIter iter = leaf.beginValueOn(); + Coord xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(9, 10, 3), xyz); + + ++iter; + xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(13, 10, 3), xyz); +} + +void +TestLeaf::testNegativeIndexing() +{ + using namespace openvdb; + + LeafType leaf(openvdb::Coord(-9, -2, -8), 1); + + CPPUNIT_ASSERT_EQUAL(Coord(-16, -8, -8), leaf.origin()); + + leaf.setValueOn(Coord(1, 2, 3), -3); + leaf.setValueOn(Coord(5, 2, 3), 4); + + CPPUNIT_ASSERT_EQUAL(-3, leaf.getValue(Coord(1, 2, 3))); + CPPUNIT_ASSERT_EQUAL(4, leaf.getValue(Coord(5, 2, 3))); + + LeafType::ValueOnIter iter = leaf.beginValueOn(); + Coord xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(-15, -6, -5), xyz); + + ++iter; + xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(-11, -6, -5), xyz); +} + +void +TestLeaf::testIsConstant() +{ + using namespace openvdb; + const Coord origin(-9, -2, -8); + + {// check old version (v3.0 and older) with float + // Acceptable range: first-value +/- tolerance + const float val = 1.0f, tol = 0.01f; + tree::LeafNode leaf(origin, val, true); + float v = 0.0f; + bool stat = false; + CPPUNIT_ASSERT(leaf.isConstant(v, stat, tol)); + CPPUNIT_ASSERT(stat); + CPPUNIT_ASSERT_EQUAL(val, v); + + leaf.setValueOff(0); + CPPUNIT_ASSERT(!leaf.isConstant(v, stat, tol)); + + leaf.setValueOn(0); + CPPUNIT_ASSERT(leaf.isConstant(v, stat, tol)); + + leaf.setValueOn(0, val + 0.99f*tol); + CPPUNIT_ASSERT(leaf.isConstant(v, stat, tol)); + CPPUNIT_ASSERT(stat); + CPPUNIT_ASSERT_EQUAL(val + 0.99f*tol, v); + + leaf.setValueOn(0, val + 1.01f*tol); + CPPUNIT_ASSERT(!leaf.isConstant(v, stat, tol)); + } + {// check old version (v3.0 and older) with double + // Acceptable range: first-value +/- tolerance + const double val = 1.0, tol = 0.00001; + tree::LeafNode leaf(origin, val, true); + double v = 0.0; + bool stat = false; + CPPUNIT_ASSERT(leaf.isConstant(v, stat, tol)); + CPPUNIT_ASSERT(stat); + CPPUNIT_ASSERT_EQUAL(val, v); + + leaf.setValueOff(0); + CPPUNIT_ASSERT(!leaf.isConstant(v, stat, tol)); + + leaf.setValueOn(0); + CPPUNIT_ASSERT(leaf.isConstant(v, stat, tol)); + + leaf.setValueOn(0, val + 0.99*tol); + CPPUNIT_ASSERT(leaf.isConstant(v, stat, tol)); + CPPUNIT_ASSERT(stat); + CPPUNIT_ASSERT_EQUAL(val + 0.99*tol, v); + + leaf.setValueOn(0, val + 1.01*tol); + CPPUNIT_ASSERT(!leaf.isConstant(v, stat, tol)); + } + {// check newer version (v3.2 and newer) with float + // Acceptable range: max - min <= tolerance + const float val = 1.0, tol = 0.01f; + tree::LeafNode leaf(origin, val, true); + float vmin = 0.0f, vmax = 0.0f; + bool stat = false; + + CPPUNIT_ASSERT(leaf.isConstant(vmin, vmax, stat, tol)); + CPPUNIT_ASSERT(stat); + CPPUNIT_ASSERT_EQUAL(val, vmin); + CPPUNIT_ASSERT_EQUAL(val, vmax); + + leaf.setValueOff(0); + CPPUNIT_ASSERT(!leaf.isConstant(vmin, vmax, stat, tol)); + + leaf.setValueOn(0); + CPPUNIT_ASSERT(leaf.isConstant(vmin, vmax, stat, tol)); + + leaf.setValueOn(0, val + tol); + CPPUNIT_ASSERT(leaf.isConstant(vmin, vmax, stat, tol)); + CPPUNIT_ASSERT_EQUAL(val, vmin); + CPPUNIT_ASSERT_EQUAL(val + tol, vmax); + + leaf.setValueOn(0, val + 1.01f*tol); + CPPUNIT_ASSERT(!leaf.isConstant(vmin, vmax, stat, tol)); + } + {// check newer version (v3.2 and newer) with double + // Acceptable range: (max- min) <= tolerance + const double val = 1.0, tol = 0.000001; + tree::LeafNode leaf(origin, val, true); + double vmin = 0.0, vmax = 0.0; + bool stat = false; + CPPUNIT_ASSERT(leaf.isConstant(vmin, vmax, stat, tol)); + CPPUNIT_ASSERT(stat); + CPPUNIT_ASSERT_EQUAL(val, vmin); + CPPUNIT_ASSERT_EQUAL(val, vmax); + + leaf.setValueOff(0); + CPPUNIT_ASSERT(!leaf.isConstant(vmin, vmax, stat, tol)); + + leaf.setValueOn(0); + CPPUNIT_ASSERT(leaf.isConstant(vmin, vmax, stat, tol)); + + leaf.setValueOn(0, val + tol); + CPPUNIT_ASSERT(leaf.isConstant(vmin, vmax, stat, tol)); + CPPUNIT_ASSERT_EQUAL(val, vmin); + CPPUNIT_ASSERT_EQUAL(val + tol, vmax); + + leaf.setValueOn(0, val + 1.01*tol); + CPPUNIT_ASSERT(!leaf.isConstant(vmin, vmax, stat, tol)); + } + {// check newer version (v3.2 and newer) with float and random values + typedef tree::LeafNode LeafNodeT; + const float val = 1.0, tol = 1.0f; + LeafNodeT leaf(origin, val, true); + float min = 2.0f, max = -min; + math::Random01 r(145);// random values in the range [0,1] + for (Index i=0; i max) max = v; + leaf.setValueOnly(i, v); + } + float vmin = 0.0f, vmax = 0.0f; + bool stat = false; + CPPUNIT_ASSERT(leaf.isConstant(vmin, vmax, stat, tol)); + CPPUNIT_ASSERT(stat); + CPPUNIT_ASSERT(math::isApproxEqual(min, vmin)); + CPPUNIT_ASSERT(math::isApproxEqual(max, vmax)); + } +} + +void +TestLeaf::testMedian() +{ + using namespace openvdb; + const Coord origin(-9, -2, -8); + std::vector v{5, 6, 4, 3, 2, 6, 7, 9, 3}; + tree::LeafNode leaf(origin, 1.0f, false); + + float val = 0.0f; + CPPUNIT_ASSERT_EQUAL(Index(0), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(0.0f, val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.setValue(Coord(0,0,0), v[0]); + CPPUNIT_ASSERT_EQUAL(Index(1), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(v[0], val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-1, leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.setValue(Coord(0,0,1), v[1]); + CPPUNIT_ASSERT_EQUAL(Index(2), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(v[0], val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-2, leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.setValue(Coord(0,2,1), v[2]); + CPPUNIT_ASSERT_EQUAL(Index(3), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(v[0], val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-3, leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.setValue(Coord(1,2,1), v[3]); + CPPUNIT_ASSERT_EQUAL(Index(4), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(v[2], val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-4, leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.setValue(Coord(1,2,3), v[4]); + CPPUNIT_ASSERT_EQUAL(Index(5), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(v[2], val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-5, leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.setValue(Coord(2,2,1), v[5]); + CPPUNIT_ASSERT_EQUAL(Index(6), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(v[2], val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-6, leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.setValue(Coord(2,4,1), v[6]); + CPPUNIT_ASSERT_EQUAL(Index(7), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(v[0], val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-7, leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.setValue(Coord(2,6,1), v[7]); + CPPUNIT_ASSERT_EQUAL(Index(8), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(v[0], val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-8, leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.setValue(Coord(7,2,1), v[8]); + CPPUNIT_ASSERT_EQUAL(Index(9), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(v[0], val); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-9, leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(1.0f, val); + CPPUNIT_ASSERT_EQUAL(1.0f, leaf.medianAll()); + + leaf.fill(2.0f, true); + + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), leaf.medianOn(val)); + CPPUNIT_ASSERT_EQUAL(2.0f, val); + CPPUNIT_ASSERT_EQUAL(Index(0), leaf.medianOff(val)); + CPPUNIT_ASSERT_EQUAL(2.0f, val); + CPPUNIT_ASSERT_EQUAL(2.0f, leaf.medianAll()); +} + +void +TestLeaf::testFill() +{ + using namespace openvdb; + const Coord origin(-9, -2, -8); + + const float bg = 0.0f, fg = 1.0f; + tree::LeafNode leaf(origin, bg, false); + + const int bboxDim = 1 + int(leaf.dim() >> 1); + auto bbox = CoordBBox::createCube(leaf.origin(), bboxDim); + CPPUNIT_ASSERT_EQUAL(math::Pow3(bboxDim), int(bbox.volume())); + + bbox = leaf.getNodeBoundingBox(); + leaf.fill(bbox, bg, false); + CPPUNIT_ASSERT(leaf.isEmpty()); + leaf.fill(bbox, fg, true); + CPPUNIT_ASSERT(leaf.isDense()); + + leaf.fill(bbox, bg, false); + CPPUNIT_ASSERT(leaf.isEmpty()); + + // Fill a region that is larger than the node but that doesn't completely enclose it. + bbox.max() = bbox.min() + (bbox.dim() >> 1); + bbox.expand(bbox.min() - Coord{10}); + leaf.fill(bbox, fg, true); + + // Verify that fill() correctly clips the fill region to the node. + auto clippedBBox = leaf.getNodeBoundingBox(); + clippedBBox.intersect(bbox); + CPPUNIT_ASSERT_EQUAL(int(clippedBBox.volume()), int(leaf.onVoxelCount())); +} diff --git a/openvdb/unittest/TestLeafBool.cc b/openvdb/unittest/TestLeafBool.cc new file mode 100644 index 00000000..d5cb45ed --- /dev/null +++ b/openvdb/unittest/TestLeafBool.cc @@ -0,0 +1,606 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() + + +class TestLeafBool: public CppUnit::TestCase +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestLeafBool); + CPPUNIT_TEST(testGetValue); + CPPUNIT_TEST(testSetValue); + CPPUNIT_TEST(testProbeValue); + CPPUNIT_TEST(testIterators); + CPPUNIT_TEST(testIteratorGetCoord); + CPPUNIT_TEST(testEquivalence); + CPPUNIT_TEST(testGetOrigin); + CPPUNIT_TEST(testNegativeIndexing); + CPPUNIT_TEST(testIO); + CPPUNIT_TEST(testTopologyCopy); + CPPUNIT_TEST(testMerge); + CPPUNIT_TEST(testCombine); + CPPUNIT_TEST(testBoolTree); + CPPUNIT_TEST(testMedian); + //CPPUNIT_TEST(testFilter); + CPPUNIT_TEST_SUITE_END(); + + void testGetValue(); + void testSetValue(); + void testProbeValue(); + void testIterators(); + void testEquivalence(); + void testGetOrigin(); + void testIteratorGetCoord(); + void testNegativeIndexing(); + void testIO(); + void testTopologyCopy(); + void testMerge(); + void testCombine(); + void testBoolTree(); + void testMedian(); + //void testFilter(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafBool); + +typedef openvdb::tree::LeafNode LeafType; + + +//////////////////////////////////////// + + +void +TestLeafBool::testGetValue() +{ + { + LeafType leaf(openvdb::Coord(0, 0, 0), /*background=*/false); + for (openvdb::Index n = 0; n < leaf.numValues(); ++n) { + CPPUNIT_ASSERT_EQUAL(false, leaf.getValue(leaf.offsetToLocalCoord(n))); + } + } + { + LeafType leaf(openvdb::Coord(0, 0, 0), /*background=*/true); + for (openvdb::Index n = 0; n < leaf.numValues(); ++n) { + CPPUNIT_ASSERT_EQUAL(true, leaf.getValue(leaf.offsetToLocalCoord(n))); + } + } + {// test Buffer::data() + LeafType leaf(openvdb::Coord(0, 0, 0), /*background=*/false); + leaf.fill(true); + LeafType::Buffer::WordType* w = leaf.buffer().data(); + for (openvdb::Index n = 0; n < LeafType::Buffer::WORD_COUNT; ++n) { + CPPUNIT_ASSERT_EQUAL(~LeafType::Buffer::WordType(0), w[n]); + } + } + {// test const Buffer::data() + LeafType leaf(openvdb::Coord(0, 0, 0), /*background=*/false); + leaf.fill(true); + const LeafType& cleaf = leaf; + const LeafType::Buffer::WordType* w = cleaf.buffer().data(); + for (openvdb::Index n = 0; n < LeafType::Buffer::WORD_COUNT; ++n) { + CPPUNIT_ASSERT_EQUAL(~LeafType::Buffer::WordType(0), w[n]); + } + } +} + + +void +TestLeafBool::testSetValue() +{ + LeafType leaf(openvdb::Coord(0, 0, 0), false); + + openvdb::Coord xyz(0, 0, 0); + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); + leaf.setValueOn(xyz); + CPPUNIT_ASSERT(leaf.isValueOn(xyz)); + + xyz.reset(7, 7, 7); + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); + leaf.setValueOn(xyz); + CPPUNIT_ASSERT(leaf.isValueOn(xyz)); + leaf.setValueOn(xyz, /*value=*/true); // value argument should be ignored + CPPUNIT_ASSERT(leaf.isValueOn(xyz)); + leaf.setValueOn(xyz, /*value=*/false); // value argument should be ignored + CPPUNIT_ASSERT(leaf.isValueOn(xyz)); + + leaf.setValueOff(xyz); + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); + + xyz.reset(2, 3, 6); + leaf.setValueOn(xyz); + CPPUNIT_ASSERT(leaf.isValueOn(xyz)); + + leaf.setValueOff(xyz); + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); +} + + +void +TestLeafBool::testProbeValue() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.setValueOn(openvdb::Coord(1, 6, 5)); + + bool val; + CPPUNIT_ASSERT(leaf.probeValue(openvdb::Coord(1, 6, 5), val)); + CPPUNIT_ASSERT(!leaf.probeValue(openvdb::Coord(1, 6, 4), val)); +} + + +void +TestLeafBool::testIterators() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.setValueOn(openvdb::Coord(1, 2, 3)); + leaf.setValueOn(openvdb::Coord(5, 2, 3)); + openvdb::Coord sum; + for (LeafType::ValueOnIter iter = leaf.beginValueOn(); iter; ++iter) { + sum += iter.getCoord(); + } + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(1 + 5, 2 + 2, 3 + 3), sum); + + openvdb::Index count = 0; + for (LeafType::ValueOffIter iter = leaf.beginValueOff(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(leaf.numValues() - 2, count); + + count = 0; + for (LeafType::ValueAllIter iter = leaf.beginValueAll(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), count); + + count = 0; + for (LeafType::ChildOnIter iter = leaf.beginChildOn(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(0), count); + + count = 0; + for (LeafType::ChildOffIter iter = leaf.beginChildOff(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(0), count); + + count = 0; + for (LeafType::ChildAllIter iter = leaf.beginChildAll(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), count); +} + + +void +TestLeafBool::testIteratorGetCoord() +{ + using namespace openvdb; + + LeafType leaf(openvdb::Coord(8, 8, 0)); + + CPPUNIT_ASSERT_EQUAL(Coord(8, 8, 0), leaf.origin()); + + leaf.setValueOn(Coord(1, 2, 3), -3); + leaf.setValueOn(Coord(5, 2, 3), 4); + + LeafType::ValueOnIter iter = leaf.beginValueOn(); + Coord xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(9, 10, 3), xyz); + + ++iter; + xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(13, 10, 3), xyz); +} + + +void +TestLeafBool::testEquivalence() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + { + LeafType leaf(Coord(0, 0, 0), false); // false and inactive + LeafType leaf2(Coord(0, 0, 0), true); // true and inactive + + CPPUNIT_ASSERT(leaf != leaf2); + + leaf.fill(CoordBBox(Coord(0), Coord(LeafType::DIM - 1)), true, /*active=*/false); + CPPUNIT_ASSERT(leaf == leaf2); // true and inactive + + leaf.setValuesOn(); // true and active + + leaf2.fill(CoordBBox(Coord(0), Coord(LeafType::DIM - 1)), false); // false and active + CPPUNIT_ASSERT(leaf != leaf2); + + leaf.negate(); // false and active + CPPUNIT_ASSERT(leaf == leaf2); + + // Set some values. + leaf.setValueOn(Coord(0, 0, 0), true); + leaf.setValueOn(Coord(0, 1, 0), true); + leaf.setValueOn(Coord(1, 1, 0), true); + leaf.setValueOn(Coord(1, 1, 2), true); + + leaf2.setValueOn(Coord(0, 0, 0), true); + leaf2.setValueOn(Coord(0, 1, 0), true); + leaf2.setValueOn(Coord(1, 1, 0), true); + leaf2.setValueOn(Coord(1, 1, 2), true); + + CPPUNIT_ASSERT(leaf == leaf2); + + leaf2.setValueOn(Coord(0, 0, 1), true); + + CPPUNIT_ASSERT(leaf != leaf2); + + leaf2.setValueOff(Coord(0, 0, 1), false); + + CPPUNIT_ASSERT(leaf != leaf2); + + leaf2.setValueOn(Coord(0, 0, 1)); + + CPPUNIT_ASSERT(leaf == leaf2); + } + {// test LeafNode::operator==() + LeafType leaf1(Coord(0 , 0, 0), true); // true and inactive + LeafType leaf2(Coord(1 , 0, 0), true); // true and inactive + LeafType leaf3(Coord(LeafType::DIM, 0, 0), true); // true and inactive + LeafType leaf4(Coord(0 , 0, 0), true, true);//true and active + CPPUNIT_ASSERT(leaf1 == leaf2); + CPPUNIT_ASSERT(leaf1 != leaf3); + CPPUNIT_ASSERT(leaf2 != leaf3); + CPPUNIT_ASSERT(leaf1 != leaf4); + CPPUNIT_ASSERT(leaf2 != leaf4); + CPPUNIT_ASSERT(leaf3 != leaf4); + } + +} + + +void +TestLeafBool::testGetOrigin() +{ + { + LeafType leaf(openvdb::Coord(1, 0, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(0, 0, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(8, 0, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(8, 1, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(1024, 1, 3), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(128*8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(1023, 1, 3), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(127*8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(512, 512, 512), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(512, 512, 512), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(2, 52, 515), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 48, 512), leaf.origin()); + } +} + + +void +TestLeafBool::testNegativeIndexing() +{ + using namespace openvdb; + + LeafType leaf(openvdb::Coord(-9, -2, -8)); + + CPPUNIT_ASSERT_EQUAL(Coord(-16, -8, -8), leaf.origin()); + + leaf.setValueOn(Coord(1, 2, 3)); + leaf.setValueOn(Coord(5, 2, 3)); + + CPPUNIT_ASSERT(leaf.isValueOn(Coord(1, 2, 3))); + CPPUNIT_ASSERT(leaf.isValueOn(Coord(5, 2, 3))); + + LeafType::ValueOnIter iter = leaf.beginValueOn(); + Coord xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(-15, -6, -5), xyz); + + ++iter; + xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(-11, -6, -5), xyz); +} + + +void +TestLeafBool::testIO() +{ + LeafType leaf(openvdb::Coord(1, 3, 5)); + const openvdb::Coord origin = leaf.origin(); + + leaf.setValueOn(openvdb::Coord(0, 1, 0)); + leaf.setValueOn(openvdb::Coord(1, 0, 0)); + + std::ostringstream ostr(std::ios_base::binary); + + leaf.writeBuffers(ostr); + + leaf.setValueOff(openvdb::Coord(0, 1, 0)); + leaf.setValueOn(openvdb::Coord(0, 1, 1)); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + // Since the input stream doesn't include a VDB header with file format version info, + // tag the input stream explicitly with the current version number. + openvdb::io::setCurrentVersion(istr); + + leaf.readBuffers(istr); + + CPPUNIT_ASSERT_EQUAL(origin, leaf.origin()); + + CPPUNIT_ASSERT(leaf.isValueOn(openvdb::Coord(0, 1, 0))); + CPPUNIT_ASSERT(leaf.isValueOn(openvdb::Coord(1, 0, 0))); + + CPPUNIT_ASSERT(leaf.onVoxelCount() == 2); +} + + +void +TestLeafBool::testTopologyCopy() +{ + using openvdb::Coord; + + // LeafNode having the same Log2Dim as LeafType + typedef LeafType::ValueConverter::Type FloatLeafType; + + FloatLeafType fleaf(Coord(10, 20, 30), /*background=*/-1.0); + std::set coords; + for (openvdb::Index n = 0; n < fleaf.numValues(); n += 10) { + Coord xyz = fleaf.offsetToGlobalCoord(n); + fleaf.setValueOn(xyz, float(n)); + coords.insert(xyz); + } + + LeafType leaf(fleaf, openvdb::TopologyCopy()); + CPPUNIT_ASSERT_EQUAL(fleaf.onVoxelCount(), leaf.onVoxelCount()); + + CPPUNIT_ASSERT(leaf.hasSameTopology(&fleaf)); + + for (LeafType::ValueOnIter iter = leaf.beginValueOn(); iter; ++iter) { + coords.erase(iter.getCoord()); + } + CPPUNIT_ASSERT(coords.empty()); +} + + +void +TestLeafBool::testMerge() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + for (openvdb::Index n = 0; n < leaf.numValues(); n += 10) { + leaf.setValueOn(n); + } + CPPUNIT_ASSERT(!leaf.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf.isValueMaskOff()); + bool val = false, active = false; + CPPUNIT_ASSERT(!leaf.isConstant(val, active)); + + LeafType leaf2(leaf); + leaf2.getValueMask().toggle(); + CPPUNIT_ASSERT(!leaf2.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf2.isValueMaskOff()); + val = active = false; + CPPUNIT_ASSERT(!leaf2.isConstant(val, active)); + + leaf.merge(leaf2); + CPPUNIT_ASSERT(leaf.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf.isValueMaskOff()); + val = active = false; + CPPUNIT_ASSERT(leaf.isConstant(val, active)); + CPPUNIT_ASSERT(active); +} + + +void +TestLeafBool::testCombine() +{ + struct Local { + static void op(openvdb::CombineArgs& args) { + args.setResult(false); // result should be ignored + args.setResultIsActive(args.aIsActive() ^ args.bIsActive()); + } + }; + + LeafType leaf(openvdb::Coord(0, 0, 0)); + for (openvdb::Index n = 0; n < leaf.numValues(); n += 10) { + leaf.setValueOn(n); + } + CPPUNIT_ASSERT(!leaf.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf.isValueMaskOff()); + const LeafType::NodeMaskType savedMask = leaf.getValueMask(); + OPENVDB_LOG_DEBUG_RUNTIME(leaf.str()); + + LeafType leaf2(leaf); + for (openvdb::Index n = 0; n < leaf.numValues(); n += 4) { + leaf2.setValueOn(n); + } + CPPUNIT_ASSERT(!leaf2.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf2.isValueMaskOff()); + OPENVDB_LOG_DEBUG_RUNTIME(leaf2.str()); + + leaf.combine(leaf2, Local::op); + OPENVDB_LOG_DEBUG_RUNTIME(leaf.str()); + + CPPUNIT_ASSERT(leaf.getValueMask() == (savedMask ^ leaf2.getValueMask())); +} + + +void +TestLeafBool::testBoolTree() +{ + using namespace openvdb; + +#if 0 + FloatGrid::Ptr inGrid; + FloatTree::Ptr inTree; + { + //io::File vdbFile("/work/rd/fx_tools/vdb_unittest/TestGridCombine::testCsg/large1.vdb2"); + io::File vdbFile("/hosts/whitestar/usr/pic1/VDB/bunny_0256.vdb2"); + vdbFile.open(); + inGrid = gridPtrCast(vdbFile.readGrid("LevelSet")); + CPPUNIT_ASSERT(inGrid.get() != NULL); + inTree = inGrid->treePtr(); + CPPUNIT_ASSERT(inTree.get() != NULL); + } +#else + FloatGrid::Ptr inGrid = FloatGrid::create(); + CPPUNIT_ASSERT(inGrid.get() != NULL); + FloatTree& inTree = inGrid->tree(); + inGrid->setName("LevelSet"); + + unittest_util::makeSphere(/*dim =*/Coord(128), + /*center=*/Vec3f(0, 0, 0), + /*radius=*/5, + *inGrid, unittest_util::SPHERE_DENSE); +#endif + + const Index64 + floatTreeMem = inTree.memUsage(), + floatTreeLeafCount = inTree.leafCount(), + floatTreeVoxelCount = inTree.activeVoxelCount(); + + TreeBase::Ptr outTree(new BoolTree(inTree, false, true, TopologyCopy())); + CPPUNIT_ASSERT(outTree.get() != NULL); + + BoolGrid::Ptr outGrid = BoolGrid::create(*inGrid); // copy transform and metadata + outGrid->setTree(outTree); + outGrid->setName("Boolean"); + + const Index64 + boolTreeMem = outTree->memUsage(), + boolTreeLeafCount = outTree->leafCount(), + boolTreeVoxelCount = outTree->activeVoxelCount(); + +#if 0 + GridPtrVec grids; + grids.push_back(inGrid); + grids.push_back(outGrid); + io::File vdbFile("bool_tree.vdb2"); + vdbFile.write(grids); + vdbFile.close(); +#endif + + CPPUNIT_ASSERT_EQUAL(floatTreeLeafCount, boolTreeLeafCount); + CPPUNIT_ASSERT_EQUAL(floatTreeVoxelCount, boolTreeVoxelCount); + + //std::cerr << "\nboolTree mem=" << boolTreeMem << " bytes" << std::endl; + //std::cerr << "floatTree mem=" << floatTreeMem << " bytes" << std::endl; + + // Considering only voxel buffer memory usage, the BoolTree would be expected + // to use (2 mask bits/voxel / ((32 value bits + 1 mask bit)/voxel)) = ~1/16 + // as much memory as the FloatTree. Considering total memory usage, verify that + // the BoolTree is no more than 1/10 the size of the FloatTree. + CPPUNIT_ASSERT(boolTreeMem * 10 <= floatTreeMem); +} + +void +TestLeafBool::testMedian() +{ + using namespace openvdb; + LeafType leaf(openvdb::Coord(0, 0, 0), /*background=*/false); + bool state = false; + + CPPUNIT_ASSERT_EQUAL(Index(0), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + leaf.setValue(Coord(0,0,0), true); + CPPUNIT_ASSERT_EQUAL(Index(1), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-1, leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + leaf.setValue(Coord(0,0,1), true); + CPPUNIT_ASSERT_EQUAL(Index(2), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-2, leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + leaf.setValue(Coord(5,0,1), true); + CPPUNIT_ASSERT_EQUAL(Index(3), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-3, leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + leaf.fill(false, false); + CPPUNIT_ASSERT_EQUAL(Index(0), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + for (Index i=0; itreePtr(); +// CPPUNIT_ASSERT(tree.get() != NULL); +// grid->setName("filtered"); + +// unittest_util::makeSphere(/*dim=*/Coord(32), +// /*ctr=*/Vec3f(0, 0, 0), +// /*radius=*/10, +// *grid, unittest_util::SPHERE_DENSE); + +// BoolTree::Ptr copyOfTree(new BoolTree(*tree)); +// BoolGrid::Ptr copyOfGrid = BoolGrid::create(copyOfTree); +// copyOfGrid->setName("original"); + +// tools::Filter filter(*grid); +// filter.offset(1); + +// #if 0 +// GridPtrVec grids; +// grids.push_back(copyOfGrid); +// grids.push_back(grid); +// io::File vdbFile("TestLeafBool::testFilter.vdb2"); +// vdbFile.write(grids); +// vdbFile.close(); +// #endif + +// // Verify that offsetting all active voxels by 1 (true) has no effect, +// // since the active voxels were all true to begin with. +// CPPUNIT_ASSERT(tree->hasSameTopology(*copyOfTree)); +// } diff --git a/openvdb/unittest/TestLeafIO.cc b/openvdb/unittest/TestLeafIO.cc new file mode 100644 index 00000000..c9efdcac --- /dev/null +++ b/openvdb/unittest/TestLeafIO.cc @@ -0,0 +1,141 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include // for toupper() +#include +#include + +// CPPUNIT_TEST_SUITE() invokes CPPUNIT_TESTNAMER_DECL() to generate a suite name +// from the FixtureType. But if FixtureType is a templated type, the generated name +// can become long and messy. This macro overrides the normal naming logic, +// instead invoking FixtureType::testSuiteName(), which should be a static member +// function that returns a std::string containing the suite name for the specific +// template instantiation. +#undef CPPUNIT_TESTNAMER_DECL +#define CPPUNIT_TESTNAMER_DECL( variableName, FixtureType ) \ + CPPUNIT_NS::TestNamer variableName( FixtureType::testSuiteName() ) + + +template +class TestLeafIO: public CppUnit::TestCase +{ +public: + static std::string testSuiteName() + { + std::string name = openvdb::typeNameAsString(); + if (!name.empty()) name[0] = static_cast(::toupper(name[0])); + return "TestLeafIO" + name; + } + + CPPUNIT_TEST_SUITE(TestLeafIO); + CPPUNIT_TEST(testBuffer); + CPPUNIT_TEST_SUITE_END(); + + void testBuffer(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafIO); + + +template +void +TestLeafIO::testBuffer() +{ + openvdb::tree::LeafNode leaf(openvdb::Coord(0, 0, 0)); + + leaf.setValueOn(openvdb::Coord(0, 1, 0), T(1)); + leaf.setValueOn(openvdb::Coord(1, 0, 0), T(1)); + + std::ostringstream ostr(std::ios_base::binary); + + leaf.writeBuffers(ostr); + + leaf.setValueOn(openvdb::Coord(0, 1, 0), T(0)); + leaf.setValueOn(openvdb::Coord(0, 1, 1), T(1)); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + + // Since the input stream doesn't include a VDB header with file format version info, + // tag the input stream explicitly with the current version number. + openvdb::io::setCurrentVersion(istr); + + leaf.readBuffers(istr); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(T(1), leaf.getValue(openvdb::Coord(0, 1, 0)), /*tolerance=*/0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(T(1), leaf.getValue(openvdb::Coord(1, 0, 0)), /*tolerance=*/0); + + CPPUNIT_ASSERT(leaf.onVoxelCount() == 2); +} + + +template<> +void +TestLeafIO::testBuffer() +{ + openvdb::tree::LeafNode + leaf(openvdb::Coord(0, 0, 0), std::string()); + + leaf.setValueOn(openvdb::Coord(0, 1, 0), std::string("test")); + leaf.setValueOn(openvdb::Coord(1, 0, 0), std::string("test")); + + std::ostringstream ostr(std::ios_base::binary); + + leaf.writeBuffers(ostr); + + leaf.setValueOn(openvdb::Coord(0, 1, 0), std::string("douche")); + leaf.setValueOn(openvdb::Coord(0, 1, 1), std::string("douche")); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + + // Since the input stream doesn't include a VDB header with file format version info, + // tag the input stream explicitly with the current version number. + openvdb::io::setCurrentVersion(istr); + + leaf.readBuffers(istr); + + CPPUNIT_ASSERT_EQUAL(std::string("test"), leaf.getValue(openvdb::Coord(0, 1, 0))); + CPPUNIT_ASSERT_EQUAL(std::string("test"), leaf.getValue(openvdb::Coord(1, 0, 0))); + + CPPUNIT_ASSERT(leaf.onVoxelCount() == 2); +} + + +template<> +void +TestLeafIO::testBuffer() +{ + openvdb::tree::LeafNode leaf(openvdb::Coord(0, 0, 0)); + + leaf.setValueOn(openvdb::Coord(0, 1, 0), openvdb::Vec3R(1, 1, 1)); + leaf.setValueOn(openvdb::Coord(1, 0, 0), openvdb::Vec3R(1, 1, 1)); + + std::ostringstream ostr(std::ios_base::binary); + + leaf.writeBuffers(ostr); + + leaf.setValueOn(openvdb::Coord(0, 1, 0), openvdb::Vec3R(0, 0, 0)); + leaf.setValueOn(openvdb::Coord(0, 1, 1), openvdb::Vec3R(1, 1, 1)); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + + // Since the input stream doesn't include a VDB header with file format version info, + // tag the input stream explicitly with the current version number. + openvdb::io::setCurrentVersion(istr); + + leaf.readBuffers(istr); + + CPPUNIT_ASSERT(leaf.getValue(openvdb::Coord(0, 1, 0)) == openvdb::Vec3R(1, 1, 1)); + CPPUNIT_ASSERT(leaf.getValue(openvdb::Coord(1, 0, 0)) == openvdb::Vec3R(1, 1, 1)); + + CPPUNIT_ASSERT(leaf.onVoxelCount() == 2); +} diff --git a/openvdb/unittest/TestLeafManager.cc b/openvdb/unittest/TestLeafManager.cc new file mode 100644 index 00000000..290df1d6 --- /dev/null +++ b/openvdb/unittest/TestLeafManager.cc @@ -0,0 +1,324 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() +#include + + +class TestLeafManager: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestLeafManager); + CPPUNIT_TEST(testBasics); + CPPUNIT_TEST(testActiveLeafVoxelCount); + CPPUNIT_TEST(testForeach); + CPPUNIT_TEST(testReduce); + CPPUNIT_TEST_SUITE_END(); + + void testBasics(); + void testActiveLeafVoxelCount(); + void testForeach(); + void testReduce(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafManager); + +void +TestLeafManager::testBasics() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + using openvdb::Vec3f; + using openvdb::FloatGrid; + using openvdb::FloatTree; + + const Vec3f center(0.35f, 0.35f, 0.35f); + const float radius = 0.15f; + const int dim = 128, half_width = 5; + const float voxel_size = 1.0f/dim; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/half_width*voxel_size); + FloatTree& tree = grid->tree(); + grid->setTransform(openvdb::math::Transform::createLinearTransform(/*voxel size=*/voxel_size)); + + unittest_util::makeSphere( + Coord(dim), center, radius, *grid, unittest_util::SPHERE_SPARSE_NARROW_BAND); + const size_t leafCount = tree.leafCount(); + + //grid->print(std::cout, 3); + {// test with no aux buffers + openvdb::tree::LeafManager r(tree); + CPPUNIT_ASSERT_EQUAL(leafCount, r.leafCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBufferCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBuffersPerLeaf()); + size_t n = 0; + for (FloatTree::LeafCIter iter=tree.cbeginLeaf(); iter; ++iter, ++n) { + CPPUNIT_ASSERT(r.leaf(n) == *iter); + CPPUNIT_ASSERT(r.getBuffer(n,0) == iter->buffer()); + } + CPPUNIT_ASSERT_EQUAL(r.leafCount(), n); + CPPUNIT_ASSERT(!r.swapBuffer(0,0)); + + r.rebuildAuxBuffers(2); + + CPPUNIT_ASSERT_EQUAL(leafCount, r.leafCount()); + CPPUNIT_ASSERT_EQUAL(size_t(2), r.auxBuffersPerLeaf()); + CPPUNIT_ASSERT_EQUAL(size_t(2*leafCount),r.auxBufferCount()); + + for (n=0; n r(tree, 2); + CPPUNIT_ASSERT_EQUAL(leafCount, r.leafCount()); + CPPUNIT_ASSERT_EQUAL(size_t(2), r.auxBuffersPerLeaf()); + CPPUNIT_ASSERT_EQUAL(size_t(2*leafCount),r.auxBufferCount()); + size_t n = 0; + for (FloatTree::LeafCIter iter=tree.cbeginLeaf(); iter; ++iter, ++n) { + CPPUNIT_ASSERT(r.leaf(n) == *iter); + CPPUNIT_ASSERT(r.getBuffer(n,0) == iter->buffer()); + + CPPUNIT_ASSERT(r.getBuffer(n,0) == r.getBuffer(n,1)); + CPPUNIT_ASSERT(r.getBuffer(n,1) == r.getBuffer(n,2)); + CPPUNIT_ASSERT(r.getBuffer(n,0) == r.getBuffer(n,2)); + } + CPPUNIT_ASSERT_EQUAL(r.leafCount(), n); + for (n=0; n r(tree); + + for (size_t numAuxBuffers = 0; numAuxBuffers <= 2; ++numAuxBuffers += 2) { + r.rebuildAuxBuffers(numAuxBuffers); + + CPPUNIT_ASSERT_EQUAL(leafCount, r.leafCount()); + CPPUNIT_ASSERT_EQUAL(int(numAuxBuffers * leafCount), int(r.auxBufferCount())); + CPPUNIT_ASSERT_EQUAL(numAuxBuffers, r.auxBuffersPerLeaf()); + + size_t n = 0; + for (FloatTree::LeafCIter iter = tree.cbeginLeaf(); iter; ++iter, ++n) { + CPPUNIT_ASSERT(r.leaf(n) == *iter); + // Verify that each aux buffer was initialized with a copy of the leaf buffer. + for (size_t bufIdx = 0; bufIdx < numAuxBuffers; ++bufIdx) { + CPPUNIT_ASSERT(r.getBuffer(n, bufIdx) == iter->buffer()); + } + } + CPPUNIT_ASSERT_EQUAL(r.leafCount(), n); + + for (size_t i = 0; i < numAuxBuffers; ++i) { + for (size_t j = 0; j < numAuxBuffers; ++j) { + // Verify that swapping buffers with themselves and swapping + // leaf buffers with aux buffers have no effect. + const bool canSwap = (i != j && i != 0 && j != 0); + CPPUNIT_ASSERT_EQUAL(canSwap, r.swapBuffer(i, j)); + } + } + } + } +} + +void +TestLeafManager::testActiveLeafVoxelCount() +{ + using namespace openvdb; + + for (const Int32 dim: { 87, 1023, 1024, 2023 }) { + const CoordBBox denseBBox{Coord{0}, Coord{dim - 1}}; + const auto size = denseBBox.volume(); + + // Create a large dense tree for testing but use a MaskTree to + // minimize the memory overhead + MaskTree tree{false}; + tree.denseFill(denseBBox, true, true); + // Add some tiles, which should not contribute to the leaf voxel count. + tree.addTile(/*level=*/2, Coord{10000}, true, true); + tree.addTile(/*level=*/1, Coord{-10000}, true, true); + tree.addTile(/*level=*/1, Coord{20000}, false, false); + + tree::LeafManager mgr(tree); + + // On a dual CPU Intel(R) Xeon(R) E5-2697 v3 @ 2.60GHz + // the speedup of LeafManager::activeLeafVoxelCount over + // Tree::activeLeafVoxelCount is ~15x (assuming a LeafManager already exists) + //openvdb::util::CpuTimer t("\nTree::activeVoxelCount"); + const auto treeActiveVoxels = tree.activeVoxelCount(); + //t.restart("\nTree::activeLeafVoxelCount"); + const auto treeActiveLeafVoxels = tree.activeLeafVoxelCount(); + //t.restart("\nLeafManager::activeLeafVoxelCount"); + const auto mgrActiveLeafVoxels = mgr.activeLeafVoxelCount();//multi-threaded + //t.stop(); + //std::cerr << "Old1 = " << treeActiveVoxels << " old2 = " << treeActiveLeafVoxels + // << " New = " << mgrActiveLeafVoxels << std::endl; + CPPUNIT_ASSERT(size < treeActiveVoxels); + CPPUNIT_ASSERT_EQUAL(size, treeActiveLeafVoxels); + CPPUNIT_ASSERT_EQUAL(size, mgrActiveLeafVoxels); + } +} + +namespace { + +struct ForeachOp +{ + ForeachOp(float v) : mV(v) {} + template + void operator()(T &leaf, size_t) const + { + for (typename T::ValueOnIter iter = leaf.beginValueOn(); iter; ++iter) { + if ( *iter > mV) iter.setValue( 2.0f ); + } + } + const float mV; +};// ForeachOp + +struct ReduceOp +{ + ReduceOp(float v) : mV(v), mN(0) {} + ReduceOp(const ReduceOp &other) : mV(other.mV), mN(other.mN) {} + ReduceOp(const ReduceOp &other, tbb::split) : mV(other.mV), mN(0) {} + template + void operator()(T &leaf, size_t) + { + for (typename T::ValueOnIter iter = leaf.beginValueOn(); iter; ++iter) { + if ( *iter > mV) ++mN; + } + } + void join(const ReduceOp &other) {mN += other.mN;} + const float mV; + openvdb::Index mN; +};// ReduceOp + +}//unnamed namespace + +void +TestLeafManager::testForeach() +{ + using namespace openvdb; + + FloatTree tree( 0.0f ); + const int dim = int(FloatTree::LeafNodeType::dim()); + const CoordBBox bbox1(Coord(0),Coord(dim-1)); + const CoordBBox bbox2(Coord(dim),Coord(2*dim-1)); + + tree.fill( bbox1, -1.0f); + tree.fill( bbox2, 1.0f); + tree.voxelizeActiveTiles(); + + for (CoordBBox::Iterator iter(bbox1); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL( -1.0f, tree.getValue(*iter)); + } + for (CoordBBox::Iterator iter(bbox2); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL( 1.0f, tree.getValue(*iter)); + } + + tree::LeafManager r(tree); + CPPUNIT_ASSERT_EQUAL(size_t(2), r.leafCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBufferCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBuffersPerLeaf()); + + ForeachOp op(0.0f); + r.foreach(op); + + CPPUNIT_ASSERT_EQUAL(size_t(2), r.leafCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBufferCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBuffersPerLeaf()); + + for (CoordBBox::Iterator iter(bbox1); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL( -1.0f, tree.getValue(*iter)); + } + for (CoordBBox::Iterator iter(bbox2); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL( 2.0f, tree.getValue(*iter)); + } +} + +void +TestLeafManager::testReduce() +{ + using namespace openvdb; + + FloatTree tree( 0.0f ); + const int dim = int(FloatTree::LeafNodeType::dim()); + const CoordBBox bbox1(Coord(0),Coord(dim-1)); + const CoordBBox bbox2(Coord(dim),Coord(2*dim-1)); + + tree.fill( bbox1, -1.0f); + tree.fill( bbox2, 1.0f); + tree.voxelizeActiveTiles(); + + for (CoordBBox::Iterator iter(bbox1); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL( -1.0f, tree.getValue(*iter)); + } + for (CoordBBox::Iterator iter(bbox2); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL( 1.0f, tree.getValue(*iter)); + } + + tree::LeafManager r(tree); + CPPUNIT_ASSERT_EQUAL(size_t(2), r.leafCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBufferCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBuffersPerLeaf()); + + ReduceOp op(0.0f); + r.reduce(op); + CPPUNIT_ASSERT_EQUAL(FloatTree::LeafNodeType::numValues(), op.mN); + + CPPUNIT_ASSERT_EQUAL(size_t(2), r.leafCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBufferCount()); + CPPUNIT_ASSERT_EQUAL(size_t(0), r.auxBuffersPerLeaf()); + + Index n = 0; + for (CoordBBox::Iterator iter(bbox1); iter; ++iter) { + ++n; + CPPUNIT_ASSERT_EQUAL( -1.0f, tree.getValue(*iter)); + } + CPPUNIT_ASSERT_EQUAL(FloatTree::LeafNodeType::numValues(), n); + + n = 0; + for (CoordBBox::Iterator iter(bbox2); iter; ++iter) { + ++n; + CPPUNIT_ASSERT_EQUAL( 1.0f, tree.getValue(*iter)); + } + CPPUNIT_ASSERT_EQUAL(FloatTree::LeafNodeType::numValues(), n); +} diff --git a/openvdb/unittest/TestLeafMask.cc b/openvdb/unittest/TestLeafMask.cc new file mode 100644 index 00000000..47cb173b --- /dev/null +++ b/openvdb/unittest/TestLeafMask.cc @@ -0,0 +1,613 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() + + +class TestLeafMask: public CppUnit::TestCase +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestLeafMask); + CPPUNIT_TEST(testGetValue); + CPPUNIT_TEST(testSetValue); + CPPUNIT_TEST(testProbeValue); + CPPUNIT_TEST(testIterators); + CPPUNIT_TEST(testIteratorGetCoord); + CPPUNIT_TEST(testEquivalence); + CPPUNIT_TEST(testGetOrigin); + CPPUNIT_TEST(testNegativeIndexing); + CPPUNIT_TEST(testIO); + CPPUNIT_TEST(testTopologyCopy); + CPPUNIT_TEST(testMerge); + CPPUNIT_TEST(testCombine); + CPPUNIT_TEST(testMedian); + CPPUNIT_TEST(testTopologyTree); + //CPPUNIT_TEST(testFilter); + CPPUNIT_TEST_SUITE_END(); + + void testGetValue(); + void testSetValue(); + void testProbeValue(); + void testIterators(); + void testEquivalence(); + void testGetOrigin(); + void testIteratorGetCoord(); + void testNegativeIndexing(); + void testIO(); + void testTopologyCopy(); + void testMerge(); + void testCombine(); + void testMedian(); + void testTopologyTree(); + //void testFilter(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafMask); + +typedef openvdb::tree::LeafNode LeafType; + + +//////////////////////////////////////// + + +void +TestLeafMask::testGetValue() +{ + { + LeafType leaf1(openvdb::Coord(0, 0, 0)); + openvdb::tree::LeafNode leaf2(openvdb::Coord(0, 0, 0)); + CPPUNIT_ASSERT( leaf1.memUsage() < leaf2.memUsage() ); + //std::cerr << "\nLeafNode uses " << leaf1.memUsage() << " bytes" << std::endl; + //std::cerr << "LeafNode uses " << leaf2.memUsage() << " bytes" << std::endl; + } + { + LeafType leaf(openvdb::Coord(0, 0, 0), false); + for (openvdb::Index n = 0; n < leaf.numValues(); ++n) { + CPPUNIT_ASSERT_EQUAL(false, leaf.getValue(leaf.offsetToLocalCoord(n))); + } + } + { + LeafType leaf(openvdb::Coord(0, 0, 0), true); + for (openvdb::Index n = 0; n < leaf.numValues(); ++n) { + CPPUNIT_ASSERT_EQUAL(true, leaf.getValue(leaf.offsetToLocalCoord(n))); + } + } + {// test Buffer::data() + LeafType leaf(openvdb::Coord(0, 0, 0), false); + leaf.fill(true); + LeafType::Buffer::WordType* w = leaf.buffer().data(); + for (openvdb::Index n = 0; n < LeafType::Buffer::WORD_COUNT; ++n) { + CPPUNIT_ASSERT_EQUAL(~LeafType::Buffer::WordType(0), w[n]); + } + } + {// test const Buffer::data() + LeafType leaf(openvdb::Coord(0, 0, 0), false); + leaf.fill(true); + const LeafType& cleaf = leaf; + const LeafType::Buffer::WordType* w = cleaf.buffer().data(); + for (openvdb::Index n = 0; n < LeafType::Buffer::WORD_COUNT; ++n) { + CPPUNIT_ASSERT_EQUAL(~LeafType::Buffer::WordType(0), w[n]); + } + } +} + + +void +TestLeafMask::testSetValue() +{ + LeafType leaf(openvdb::Coord(0, 0, 0), false); + + openvdb::Coord xyz(0, 0, 0); + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); + leaf.setValueOn(xyz); + CPPUNIT_ASSERT(leaf.isValueOn(xyz)); + + xyz.reset(7, 7, 7); + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); + leaf.setValueOn(xyz); + CPPUNIT_ASSERT(leaf.isValueOn(xyz)); + leaf.setValueOn(xyz, true); + CPPUNIT_ASSERT(leaf.isValueOn(xyz)); + leaf.setValueOn(xyz, false); // value and state are the same! + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); + + leaf.setValueOff(xyz); + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); + + xyz.reset(2, 3, 6); + leaf.setValueOn(xyz); + CPPUNIT_ASSERT(leaf.isValueOn(xyz)); + + leaf.setValueOff(xyz); + CPPUNIT_ASSERT(!leaf.isValueOn(xyz)); +} + +void +TestLeafMask::testProbeValue() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.setValueOn(openvdb::Coord(1, 6, 5)); + + bool val; + CPPUNIT_ASSERT(leaf.probeValue(openvdb::Coord(1, 6, 5), val)); + CPPUNIT_ASSERT(!leaf.probeValue(openvdb::Coord(1, 6, 4), val)); +} + + +void +TestLeafMask::testIterators() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.setValueOn(openvdb::Coord(1, 2, 3)); + leaf.setValueOn(openvdb::Coord(5, 2, 3)); + openvdb::Coord sum; + for (LeafType::ValueOnIter iter = leaf.beginValueOn(); iter; ++iter) { + sum += iter.getCoord(); + } + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(1 + 5, 2 + 2, 3 + 3), sum); + + openvdb::Index count = 0; + for (LeafType::ValueOffIter iter = leaf.beginValueOff(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(leaf.numValues() - 2, count); + + count = 0; + for (LeafType::ValueAllIter iter = leaf.beginValueAll(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), count); + + count = 0; + for (LeafType::ChildOnIter iter = leaf.beginChildOn(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(0), count); + + count = 0; + for (LeafType::ChildOffIter iter = leaf.beginChildOff(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(0), count); + + count = 0; + for (LeafType::ChildAllIter iter = leaf.beginChildAll(); iter; ++iter, ++count); + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), count); +} + + +void +TestLeafMask::testIteratorGetCoord() +{ + using namespace openvdb; + + LeafType leaf(openvdb::Coord(8, 8, 0)); + + CPPUNIT_ASSERT_EQUAL(Coord(8, 8, 0), leaf.origin()); + + leaf.setValueOn(Coord(1, 2, 3), -3); + leaf.setValueOn(Coord(5, 2, 3), 4); + + LeafType::ValueOnIter iter = leaf.beginValueOn(); + Coord xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(9, 10, 3), xyz); + + ++iter; + xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(13, 10, 3), xyz); +} + + +void +TestLeafMask::testEquivalence() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + { + LeafType leaf(Coord(0, 0, 0), false); // false and inactive + LeafType leaf2(Coord(0, 0, 0), true); // true and inactive + + CPPUNIT_ASSERT(leaf != leaf2); + + leaf.fill(CoordBBox(Coord(0), Coord(LeafType::DIM - 1)), true, false); + CPPUNIT_ASSERT(leaf == leaf2); // true and inactive + + leaf.setValuesOn(); // true and active + + leaf2.fill(CoordBBox(Coord(0), Coord(LeafType::DIM - 1)), false); // false and active + CPPUNIT_ASSERT(leaf != leaf2); + + leaf.negate(); // false and active + CPPUNIT_ASSERT(leaf == leaf2); + + // Set some values. + leaf.setValueOn(Coord(0, 0, 0), true); + leaf.setValueOn(Coord(0, 1, 0), true); + leaf.setValueOn(Coord(1, 1, 0), true); + leaf.setValueOn(Coord(1, 1, 2), true); + + leaf2.setValueOn(Coord(0, 0, 0), true); + leaf2.setValueOn(Coord(0, 1, 0), true); + leaf2.setValueOn(Coord(1, 1, 0), true); + leaf2.setValueOn(Coord(1, 1, 2), true); + + CPPUNIT_ASSERT(leaf == leaf2); + + leaf2.setValueOn(Coord(0, 0, 1), true); + + CPPUNIT_ASSERT(leaf != leaf2); + + leaf2.setValueOff(Coord(0, 0, 1), false); + + CPPUNIT_ASSERT(leaf == leaf2);//values and states coinside + + leaf2.setValueOn(Coord(0, 0, 1)); + + CPPUNIT_ASSERT(leaf != leaf2);//values and states coinside + } + {// test LeafNode::operator==() + LeafType leaf1(Coord(0 , 0, 0), true); // true and inactive + LeafType leaf2(Coord(1 , 0, 0), true); // true and inactive + LeafType leaf3(Coord(LeafType::DIM, 0, 0), true); // true and inactive + LeafType leaf4(Coord(0 , 0, 0), true, true);//true and active + CPPUNIT_ASSERT(leaf1 == leaf2); + CPPUNIT_ASSERT(leaf1 != leaf3); + CPPUNIT_ASSERT(leaf2 != leaf3); + CPPUNIT_ASSERT(leaf1 == leaf4); + CPPUNIT_ASSERT(leaf2 == leaf4); + CPPUNIT_ASSERT(leaf3 != leaf4); + } + +} + + +void +TestLeafMask::testGetOrigin() +{ + { + LeafType leaf(openvdb::Coord(1, 0, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(0, 0, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(8, 0, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(8, 1, 0), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(1024, 1, 3), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(128*8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(1023, 1, 3), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(127*8, 0, 0), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(512, 512, 512), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(512, 512, 512), leaf.origin()); + } + { + LeafType leaf(openvdb::Coord(2, 52, 515), 1); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0, 48, 512), leaf.origin()); + } +} + + +void +TestLeafMask::testNegativeIndexing() +{ + using namespace openvdb; + + LeafType leaf(openvdb::Coord(-9, -2, -8)); + + CPPUNIT_ASSERT_EQUAL(Coord(-16, -8, -8), leaf.origin()); + + leaf.setValueOn(Coord(1, 2, 3)); + leaf.setValueOn(Coord(5, 2, 3)); + + CPPUNIT_ASSERT(leaf.isValueOn(Coord(1, 2, 3))); + CPPUNIT_ASSERT(leaf.isValueOn(Coord(5, 2, 3))); + + LeafType::ValueOnIter iter = leaf.beginValueOn(); + Coord xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(-15, -6, -5), xyz); + + ++iter; + xyz = iter.getCoord(); + CPPUNIT_ASSERT_EQUAL(Coord(-11, -6, -5), xyz); +} + + +void +TestLeafMask::testIO() +{ + LeafType leaf(openvdb::Coord(1, 3, 5)); + const openvdb::Coord origin = leaf.origin(); + + leaf.setValueOn(openvdb::Coord(0, 1, 0)); + leaf.setValueOn(openvdb::Coord(1, 0, 0)); + + std::ostringstream ostr(std::ios_base::binary); + + leaf.writeBuffers(ostr); + + leaf.setValueOff(openvdb::Coord(0, 1, 0)); + leaf.setValueOn(openvdb::Coord(0, 1, 1)); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + // Since the input stream doesn't include a VDB header with file format version info, + // tag the input stream explicitly with the current version number. + openvdb::io::setCurrentVersion(istr); + + leaf.readBuffers(istr); + + CPPUNIT_ASSERT_EQUAL(origin, leaf.origin()); + + CPPUNIT_ASSERT(leaf.isValueOn(openvdb::Coord(0, 1, 0))); + CPPUNIT_ASSERT(leaf.isValueOn(openvdb::Coord(1, 0, 0))); + + CPPUNIT_ASSERT(leaf.onVoxelCount() == 2); +} + + +void +TestLeafMask::testTopologyCopy() +{ + using openvdb::Coord; + + // LeafNode having the same Log2Dim as LeafType + typedef LeafType::ValueConverter::Type FloatLeafType; + + FloatLeafType fleaf(Coord(10, 20, 30), -1.0); + std::set coords; + for (openvdb::Index n = 0; n < fleaf.numValues(); n += 10) { + Coord xyz = fleaf.offsetToGlobalCoord(n); + fleaf.setValueOn(xyz, float(n)); + coords.insert(xyz); + } + + LeafType leaf(fleaf, openvdb::TopologyCopy()); + CPPUNIT_ASSERT_EQUAL(fleaf.onVoxelCount(), leaf.onVoxelCount()); + + CPPUNIT_ASSERT(leaf.hasSameTopology(&fleaf)); + + for (LeafType::ValueOnIter iter = leaf.beginValueOn(); iter; ++iter) { + coords.erase(iter.getCoord()); + } + CPPUNIT_ASSERT(coords.empty()); +} + + +void +TestLeafMask::testMerge() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + for (openvdb::Index n = 0; n < leaf.numValues(); n += 10) { + leaf.setValueOn(n); + } + CPPUNIT_ASSERT(!leaf.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf.isValueMaskOff()); + bool val = false, active = false; + CPPUNIT_ASSERT(!leaf.isConstant(val, active)); + + LeafType leaf2(leaf); + leaf2.getValueMask().toggle(); + CPPUNIT_ASSERT(!leaf2.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf2.isValueMaskOff()); + val = active = false; + CPPUNIT_ASSERT(!leaf2.isConstant(val, active)); + + leaf.merge(leaf2); + CPPUNIT_ASSERT(leaf.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf.isValueMaskOff()); + val = active = false; + CPPUNIT_ASSERT(leaf.isConstant(val, active)); + CPPUNIT_ASSERT(active); +} + + +void +TestLeafMask::testCombine() +{ + struct Local { + static void op(openvdb::CombineArgs& args) { + args.setResult(args.aIsActive() ^ args.bIsActive());// state = value + } + }; + + LeafType leaf(openvdb::Coord(0, 0, 0)); + for (openvdb::Index n = 0; n < leaf.numValues(); n += 10) leaf.setValueOn(n); + CPPUNIT_ASSERT(!leaf.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf.isValueMaskOff()); + const LeafType::NodeMaskType savedMask = leaf.getValueMask(); + OPENVDB_LOG_DEBUG_RUNTIME(leaf.str()); + + LeafType leaf2(leaf); + for (openvdb::Index n = 0; n < leaf.numValues(); n += 4) leaf2.setValueOn(n); + + CPPUNIT_ASSERT(!leaf2.isValueMaskOn()); + CPPUNIT_ASSERT(!leaf2.isValueMaskOff()); + OPENVDB_LOG_DEBUG_RUNTIME(leaf2.str()); + + leaf.combine(leaf2, Local::op); + OPENVDB_LOG_DEBUG_RUNTIME(leaf.str()); + + CPPUNIT_ASSERT(leaf.getValueMask() == (savedMask ^ leaf2.getValueMask())); +} + + +void +TestLeafMask::testTopologyTree() +{ + using namespace openvdb; + +#if 0 + FloatGrid::Ptr inGrid; + FloatTree::Ptr inTree; + { + //io::File vdbFile("/work/rd/fx_tools/vdb_unittest/TestGridCombine::testCsg/large1.vdb2"); + io::File vdbFile("/hosts/whitestar/usr/pic1/VDB/bunny_0256.vdb2"); + vdbFile.open(); + inGrid = gridPtrCast(vdbFile.readGrid("LevelSet")); + CPPUNIT_ASSERT(inGrid.get() != NULL); + inTree = inGrid->treePtr(); + CPPUNIT_ASSERT(inTree.get() != NULL); + } +#else + FloatGrid::Ptr inGrid = FloatGrid::create(); + CPPUNIT_ASSERT(inGrid.get() != NULL); + FloatTree& inTree = inGrid->tree(); + inGrid->setName("LevelSet"); + + unittest_util::makeSphere(Coord(128),//dim + Vec3f(0, 0, 0),//center + 5,//radius + *inGrid, unittest_util::SPHERE_DENSE); +#endif + + const Index64 + floatTreeMem = inTree.memUsage(), + floatTreeLeafCount = inTree.leafCount(), + floatTreeVoxelCount = inTree.activeVoxelCount(); + + TreeBase::Ptr outTree(new TopologyTree(inTree, false, true, TopologyCopy())); + CPPUNIT_ASSERT(outTree.get() != NULL); + + TopologyGrid::Ptr outGrid = TopologyGrid::create(*inGrid); // copy transform and metadata + outGrid->setTree(outTree); + outGrid->setName("Boolean"); + + const Index64 + boolTreeMem = outTree->memUsage(), + boolTreeLeafCount = outTree->leafCount(), + boolTreeVoxelCount = outTree->activeVoxelCount(); + +#if 0 + GridPtrVec grids; + grids.push_back(inGrid); + grids.push_back(outGrid); + io::File vdbFile("bool_tree.vdb2"); + vdbFile.write(grids); + vdbFile.close(); +#endif + + CPPUNIT_ASSERT_EQUAL(floatTreeLeafCount, boolTreeLeafCount); + CPPUNIT_ASSERT_EQUAL(floatTreeVoxelCount, boolTreeVoxelCount); + + //std::cerr << "\nboolTree mem=" << boolTreeMem << " bytes" << std::endl; + //std::cerr << "floatTree mem=" << floatTreeMem << " bytes" << std::endl; + + // Considering only voxel buffer memory usage, the BoolTree would be expected + // to use (2 mask bits/voxel / ((32 value bits + 1 mask bit)/voxel)) = ~1/16 + // as much memory as the FloatTree. Considering total memory usage, verify that + // the BoolTree is no more than 1/10 the size of the FloatTree. + CPPUNIT_ASSERT(boolTreeMem * 10 <= floatTreeMem); +} + +void +TestLeafMask::testMedian() +{ + using namespace openvdb; + LeafType leaf(openvdb::Coord(0, 0, 0), /*background=*/false); + bool state = false; + + CPPUNIT_ASSERT_EQUAL(Index(0), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == true); + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + leaf.setValue(Coord(0,0,0), true); + CPPUNIT_ASSERT_EQUAL(Index(1), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == true); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-1, leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + + leaf.setValue(Coord(0,0,1), true); + CPPUNIT_ASSERT_EQUAL(Index(2), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == true); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-2, leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + + leaf.setValue(Coord(5,0,1), true); + CPPUNIT_ASSERT_EQUAL(Index(3), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == true); + CPPUNIT_ASSERT_EQUAL(leaf.numValues()-3, leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + + leaf.fill(false, false); + CPPUNIT_ASSERT_EQUAL(Index(0), leaf.medianOn(state)); + CPPUNIT_ASSERT(state == true); + CPPUNIT_ASSERT_EQUAL(leaf.numValues(), leaf.medianOff(state)); + CPPUNIT_ASSERT(state == false); + CPPUNIT_ASSERT(!leaf.medianAll()); + + + for (Index i=0; itreePtr(); +// CPPUNIT_ASSERT(tree.get() != NULL); +// grid->setName("filtered"); + +// unittest_util::makeSphere(Coord(32),// dim +// Vec3f(0, 0, 0),// center +// 10,// radius +// *grid, unittest_util::SPHERE_DENSE); + +// BoolTree::Ptr copyOfTree(new BoolTree(*tree)); +// BoolGrid::Ptr copyOfGrid = BoolGrid::create(copyOfTree); +// copyOfGrid->setName("original"); + +// tools::Filter filter(*grid); +// filter.offset(1); + +// #if 0 +// GridPtrVec grids; +// grids.push_back(copyOfGrid); +// grids.push_back(grid); +// io::File vdbFile("TestLeafMask::testFilter.vdb2"); +// vdbFile.write(grids); +// vdbFile.close(); +// #endif + +// // Verify that offsetting all active voxels by 1 (true) has no effect, +// // since the active voxels were all true to begin with. +// CPPUNIT_ASSERT(tree->hasSameTopology(*copyOfTree)); +// } diff --git a/openvdb/unittest/TestLeafOrigin.cc b/openvdb/unittest/TestLeafOrigin.cc new file mode 100644 index 00000000..a4272960 --- /dev/null +++ b/openvdb/unittest/TestLeafOrigin.cc @@ -0,0 +1,107 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include + + +class TestLeafOrigin: public CppUnit::TestCase +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestLeafOrigin); + CPPUNIT_TEST(test); + CPPUNIT_TEST(test2Values); + CPPUNIT_TEST(testGetValue); + CPPUNIT_TEST_SUITE_END(); + + void test(); + void test2Values(); + void testGetValue(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafOrigin); + + +//////////////////////////////////////// + + +void +TestLeafOrigin::test() +{ + using namespace openvdb; + + std::set indices; + indices.insert(Coord( 0, 0, 0)); + indices.insert(Coord( 1, 0, 0)); + indices.insert(Coord( 0, 100, 8)); + indices.insert(Coord(-9, 0, 8)); + indices.insert(Coord(32, 0, 16)); + indices.insert(Coord(33, -5, 16)); + indices.insert(Coord(42, 17, 35)); + indices.insert(Coord(43, 17, 64)); + + FloatTree tree(/*bg=*/256.0); + std::set::iterator iter = indices.begin(); + for ( ; iter != indices.end(); ++iter) tree.setValue(*iter, 1.0); + + for (FloatTree::LeafCIter leafIter = tree.cbeginLeaf(); leafIter; ++leafIter) { + const Int32 mask = ~((1 << leafIter->log2dim()) - 1); + const Coord leafOrigin = leafIter->origin(); + for (FloatTree::LeafNodeType::ValueOnCIter valIter = leafIter->cbeginValueOn(); + valIter; ++valIter) + { + Coord xyz = valIter.getCoord(); + CPPUNIT_ASSERT_EQUAL(leafOrigin, xyz & mask); + + iter = indices.find(xyz); + CPPUNIT_ASSERT(iter != indices.end()); + indices.erase(iter); + } + } + CPPUNIT_ASSERT(indices.empty()); +} + + +void +TestLeafOrigin::test2Values() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = createGrid(/*bg=*/1.0f); + FloatTree& tree = grid->tree(); + + tree.setValue(Coord(0, 0, 0), 5); + tree.setValue(Coord(100, 0, 0), 6); + + grid->setTransform(math::Transform::createLinearTransform(0.1)); + + FloatTree::LeafCIter iter = tree.cbeginLeaf(); + CPPUNIT_ASSERT_EQUAL(Coord(0, 0, 0), iter->origin()); + ++iter; + CPPUNIT_ASSERT_EQUAL(Coord(96, 0, 0), iter->origin()); +} + +void +TestLeafOrigin::testGetValue() +{ + const openvdb::Coord c0(0,-10,0), c1(100,13,0); + const float v0=5.0f, v1=6.0f, v2=1.0f; + openvdb::FloatTree::Ptr tree(new openvdb::FloatTree(v2)); + + tree->setValue(c0, v0); + tree->setValue(c1, v1); + + openvdb::FloatTree::LeafCIter iter = tree->cbeginLeaf(); + CPPUNIT_ASSERT_EQUAL(v0, iter->getValue(c0)); + ++iter; + CPPUNIT_ASSERT_EQUAL(v1, iter->getValue(c1)); +} diff --git a/openvdb/unittest/TestLevelSetRayIntersector.cc b/openvdb/unittest/TestLevelSetRayIntersector.cc new file mode 100644 index 00000000..804ab1d8 --- /dev/null +++ b/openvdb/unittest/TestLevelSetRayIntersector.cc @@ -0,0 +1,383 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file unittest/TestLevelSetRayIntersector.cc +/// @author Ken Museth + +// Uncomment to enable statistics of ray-intersections +//#define STATS_TEST + +#include +#include +#include +#include +#include +#include +#include +#include +#include // for Film +#ifdef STATS_TEST +//only needed for statistics +#include +#include +#include +#endif + + +#define ASSERT_DOUBLES_APPROX_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/1.e-6); + + +class TestLevelSetRayIntersector : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestLevelSetRayIntersector); + CPPUNIT_TEST(tests); + +#ifdef STATS_TEST + CPPUNIT_TEST(stats); +#endif + + CPPUNIT_TEST_SUITE_END(); + + void tests(); +#ifdef STATS_TEST + void stats(); +#endif +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLevelSetRayIntersector); + +void +TestLevelSetRayIntersector::tests() +{ + using namespace openvdb; + typedef math::Ray RayT; + typedef RayT::Vec3Type Vec3T; + + {// voxel intersection against a level set sphere + const float r = 5.0f; + const Vec3f c(20.0f, 0.0f, 0.0f); + const float s = 0.5f, w = 2.0f; + + FloatGrid::Ptr ls = tools::createLevelSetSphere(r, c, s, w); + + tools::LevelSetRayIntersector lsri(*ls); + + const Vec3T dir(1.0, 0.0, 0.0); + const Vec3T eye(2.0, 0.0, 0.0); + const RayT ray(eye, dir); + //std::cerr << ray << std::endl; + Vec3T xyz(0); + Real time = 0; + CPPUNIT_ASSERT(lsri.intersectsWS(ray, xyz, time)); + ASSERT_DOUBLES_APPROX_EQUAL(15.0, xyz[0]); + ASSERT_DOUBLES_APPROX_EQUAL( 0.0, xyz[1]); + ASSERT_DOUBLES_APPROX_EQUAL( 0.0, xyz[2]); + ASSERT_DOUBLES_APPROX_EQUAL(13.0, time); + double t0=0, t1=0; + CPPUNIT_ASSERT(ray.intersects(c, r, t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL(t0, time); + //std::cerr << "\nray("< 0.01) { + film.pixel(i, j) = tools::Film::RGBA(1.0f, 0.0f, 0.0f); + } else { + film.pixel(i, j) = tools::Film::RGBA(0.0f, 1.0f, 0.0f); + } + } + } + } + timer.stop(); + + film.savePPM("sphere_serial"); + stats.print("First hit"); + hist.print("First hit"); + } +} +#endif // STATS_TEST + +#undef STATS_TEST diff --git a/openvdb/unittest/TestLevelSetUtil.cc b/openvdb/unittest/TestLevelSetUtil.cc new file mode 100644 index 00000000..d135dab8 --- /dev/null +++ b/openvdb/unittest/TestLevelSetUtil.cc @@ -0,0 +1,242 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + +#include +#include +#include +#include // for createLevelSetBox() +#include // for csgDifference() + +class TestLevelSetUtil: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestLevelSetUtil); + CPPUNIT_TEST(testSDFToFogVolume); + CPPUNIT_TEST(testSDFInteriorMask); + CPPUNIT_TEST(testExtractEnclosedRegion); + CPPUNIT_TEST(testSegmentationTools); + CPPUNIT_TEST_SUITE_END(); + + void testSDFToFogVolume(); + void testSDFInteriorMask(); + void testExtractEnclosedRegion(); + void testSegmentationTools(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLevelSetUtil); + + +//////////////////////////////////////// + +void +TestLevelSetUtil::testSDFToFogVolume() +{ + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(10.0); + + grid->fill(openvdb::CoordBBox(openvdb::Coord(-100), openvdb::Coord(100)), 9.0); + grid->fill(openvdb::CoordBBox(openvdb::Coord(-50), openvdb::Coord(50)), -9.0); + + + openvdb::tools::sdfToFogVolume(*grid); + + CPPUNIT_ASSERT(grid->background() < 1e-7); + + openvdb::FloatGrid::ValueOnIter iter = grid->beginValueOn(); + for (; iter; ++iter) { + CPPUNIT_ASSERT(iter.getValue() > 0.0); + CPPUNIT_ASSERT(std::abs(iter.getValue() - 1.0) < 1e-7); + } +} + + +void +TestLevelSetUtil::testSDFInteriorMask() +{ + typedef openvdb::FloatGrid FloatGrid; + typedef openvdb::BoolGrid BoolGrid; + typedef openvdb::Vec3s Vec3s; + typedef openvdb::math::BBox BBoxs; + typedef openvdb::math::Transform Transform; + + BBoxs bbox(Vec3s(0.0, 0.0, 0.0), Vec3s(1.0, 1.0, 1.0)); + + Transform::Ptr transform = Transform::createLinearTransform(0.1); + + FloatGrid::Ptr sdfGrid = openvdb::tools::createLevelSetBox(bbox, *transform); + + BoolGrid::Ptr maskGrid = openvdb::tools::sdfInteriorMask(*sdfGrid); + + // test inside coord value + openvdb::Coord ijk = transform->worldToIndexNodeCentered(openvdb::Vec3d(0.5, 0.5, 0.5)); + CPPUNIT_ASSERT(maskGrid->tree().getValue(ijk) == true); + + // test outside coord value + ijk = transform->worldToIndexNodeCentered(openvdb::Vec3d(1.5, 1.5, 1.5)); + CPPUNIT_ASSERT(maskGrid->tree().getValue(ijk) == false); +} + + +void +TestLevelSetUtil::testExtractEnclosedRegion() +{ + typedef openvdb::FloatGrid FloatGrid; + typedef openvdb::BoolGrid BoolGrid; + typedef openvdb::Vec3s Vec3s; + typedef openvdb::math::BBox BBoxs; + typedef openvdb::math::Transform Transform; + + BBoxs regionA(Vec3s(0.0f, 0.0f, 0.0f), Vec3s(3.0f, 3.0f, 3.0f)); + BBoxs regionB(Vec3s(1.0f, 1.0f, 1.0f), Vec3s(2.0f, 2.0f, 2.0f)); + + Transform::Ptr transform = Transform::createLinearTransform(0.1); + + FloatGrid::Ptr sdfGrid = openvdb::tools::createLevelSetBox(regionA, *transform); + FloatGrid::Ptr sdfGridB = openvdb::tools::createLevelSetBox(regionB, *transform); + + openvdb::tools::csgDifference(*sdfGrid, *sdfGridB); + + BoolGrid::Ptr maskGrid = openvdb::tools::extractEnclosedRegion(*sdfGrid); + + // test inside ls region coord value + openvdb::Coord ijk = transform->worldToIndexNodeCentered(openvdb::Vec3d(1.5, 1.5, 1.5)); + CPPUNIT_ASSERT(maskGrid->tree().getValue(ijk) == true); + + // test outside coord value + ijk = transform->worldToIndexNodeCentered(openvdb::Vec3d(3.5, 3.5, 3.5)); + CPPUNIT_ASSERT(maskGrid->tree().getValue(ijk) == false); +} + + +void +TestLevelSetUtil::testSegmentationTools() +{ + typedef openvdb::FloatGrid FloatGrid; + typedef openvdb::Vec3s Vec3s; + typedef openvdb::math::BBox BBoxs; + typedef openvdb::math::Transform Transform; + + { // Test SDF segmentation + + // Create two sdf boxes with overlapping narrow-bands. + BBoxs regionA(Vec3s(0.0f, 0.0f, 0.0f), Vec3s(2.0f, 2.0f, 2.0f)); + BBoxs regionB(Vec3s(2.5f, 0.0f, 0.0f), Vec3s(4.3f, 2.0f, 2.0f)); + + Transform::Ptr transform = Transform::createLinearTransform(0.1); + + FloatGrid::Ptr sdfGrid = openvdb::tools::createLevelSetBox(regionA, *transform); + FloatGrid::Ptr sdfGridB = openvdb::tools::createLevelSetBox(regionB, *transform); + + openvdb::tools::csgUnion(*sdfGrid, *sdfGridB); + + std::vector segments; + + // This tool will not identify two separate segments when the narrow-bands overlap. + openvdb::tools::segmentActiveVoxels(*sdfGrid, segments); + CPPUNIT_ASSERT(segments.size() == 1); + + segments.clear(); + + // This tool should properly identify two separate segments + openvdb::tools::segmentSDF(*sdfGrid, segments); + CPPUNIT_ASSERT(segments.size() == 2); + + + // test inside ls region coord value + openvdb::Coord ijk = transform->worldToIndexNodeCentered(openvdb::Vec3d(1.5, 1.5, 1.5)); + CPPUNIT_ASSERT(segments[0]->tree().getValue(ijk) < 0.0f); + + // test outside coord value + ijk = transform->worldToIndexNodeCentered(openvdb::Vec3d(3.5, 3.5, 3.5)); + CPPUNIT_ASSERT(segments[0]->tree().getValue(ijk) > 0.0f); + } + + { // Test empty SDF grid + + FloatGrid::Ptr sdfGrid = openvdb::FloatGrid::create(/*background=*/10.2f); + sdfGrid->setGridClass(openvdb::GRID_LEVEL_SET); + + std::vector segments; + openvdb::tools::segmentSDF(*sdfGrid, segments); + + CPPUNIT_ASSERT_EQUAL(size_t(1), segments.size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), segments[0]->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(10.2f, segments[0]->background()); + } + + { // Test SDF grid with inactive leaf nodes + + BBoxs bbox(Vec3s(0.0, 0.0, 0.0), Vec3s(1.0, 1.0, 1.0)); + Transform::Ptr transform = Transform::createLinearTransform(0.1); + FloatGrid::Ptr sdfGrid = openvdb::tools::createLevelSetBox(bbox, *transform, + /*halfwidth=*/5); + + CPPUNIT_ASSERT(sdfGrid->tree().activeVoxelCount() > openvdb::Index64(0)); + + // make all active voxels inactive + + for (auto leaf = sdfGrid->tree().beginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->beginValueOn(); iter; ++iter) { + leaf->setValueOff(iter.getCoord()); + } + } + + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(0), sdfGrid->tree().activeVoxelCount()); + + std::vector segments; + openvdb::tools::segmentSDF(*sdfGrid, segments); + + CPPUNIT_ASSERT_EQUAL(size_t(1), segments.size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), segments[0]->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(sdfGrid->background(), segments[0]->background()); + } + + { // Test fog volume with active tiles + + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(0.0); + + grid->fill(openvdb::CoordBBox(openvdb::Coord(0), openvdb::Coord(50)), 1.0); + grid->fill(openvdb::CoordBBox(openvdb::Coord(60), openvdb::Coord(100)), 1.0); + + CPPUNIT_ASSERT(grid->tree().hasActiveTiles() == true); + + std::vector segments; + openvdb::tools::segmentActiveVoxels(*grid, segments); + CPPUNIT_ASSERT_EQUAL(size_t(2), segments.size()); + } + + { // Test an empty fog volume + + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(/*background=*/3.1f); + + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), grid->tree().leafCount()); + + std::vector segments; + openvdb::tools::segmentActiveVoxels(*grid, segments); + + // note that an empty volume should segment into an empty volume + CPPUNIT_ASSERT_EQUAL(size_t(1), segments.size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), segments[0]->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(3.1f, segments[0]->background()); + } + + { // Test fog volume with two inactive leaf nodes + + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(0.0); + + grid->tree().touchLeaf(openvdb::Coord(0,0,0)); + grid->tree().touchLeaf(openvdb::Coord(100,100,100)); + + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(2), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(0), grid->tree().activeVoxelCount()); + + std::vector segments; + openvdb::tools::segmentActiveVoxels(*grid, segments); + + CPPUNIT_ASSERT_EQUAL(size_t(1), segments.size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), segments[0]->tree().leafCount()); + } +} + diff --git a/openvdb/unittest/TestLinearInterp.cc b/openvdb/unittest/TestLinearInterp.cc new file mode 100644 index 00000000..b2507217 --- /dev/null +++ b/openvdb/unittest/TestLinearInterp.cc @@ -0,0 +1,1070 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +// CPPUNIT_TEST_SUITE() invokes CPPUNIT_TESTNAMER_DECL() to generate a suite name +// from the FixtureType. But if FixtureType is a templated type, the generated name +// can become long and messy. This macro overrides the normal naming logic, +// instead invoking FixtureType::testSuiteName(), which should be a static member +// function that returns a std::string containing the suite name for the specific +// template instantiation. +#undef CPPUNIT_TESTNAMER_DECL +#define CPPUNIT_TESTNAMER_DECL( variableName, FixtureType ) \ + CPPUNIT_NS::TestNamer variableName( FixtureType::testSuiteName() ) + +namespace { +// Absolute tolerance for floating-point equality comparisons +const double TOLERANCE = 1.e-6; +} + +template +class TestLinearInterp: public CppUnit::TestCase +{ +public: + static std::string testSuiteName() + { + std::string name = openvdb::typeNameAsString(); + if (!name.empty()) name[0] = static_cast(::toupper(name[0])); + return "TestLinearInterp" + name; + } + + CPPUNIT_TEST_SUITE(TestLinearInterp); + CPPUNIT_TEST(test); + CPPUNIT_TEST(testTree); + CPPUNIT_TEST(testAccessor); + CPPUNIT_TEST(testConstantValues); + CPPUNIT_TEST(testFillValues); + CPPUNIT_TEST(testNegativeIndices); + CPPUNIT_TEST(testStencilsMatch); + CPPUNIT_TEST_SUITE_END(); + + void test(); + void testTree(); + void testAccessor(); + void testConstantValues(); + void testFillValues(); + void testNegativeIndices(); + void testStencilsMatch(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLinearInterp); +CPPUNIT_TEST_SUITE_REGISTRATION(TestLinearInterp); +CPPUNIT_TEST_SUITE_REGISTRATION(TestLinearInterp); + + +template +void +TestLinearInterp::test() +{ + typename GridType::TreeType TreeType; + float fillValue = 256.0f; + + GridType grid(fillValue); + typename GridType::TreeType& tree = grid.tree(); + + tree.setValue(openvdb::Coord(10, 10, 10), 1.0); + + tree.setValue(openvdb::Coord(11, 10, 10), 2.0); + tree.setValue(openvdb::Coord(11, 11, 10), 2.0); + tree.setValue(openvdb::Coord(10, 11, 10), 2.0); + tree.setValue(openvdb::Coord( 9, 11, 10), 2.0); + tree.setValue(openvdb::Coord( 9, 10, 10), 2.0); + tree.setValue(openvdb::Coord( 9, 9, 10), 2.0); + tree.setValue(openvdb::Coord(10, 9, 10), 2.0); + tree.setValue(openvdb::Coord(11, 9, 10), 2.0); + + tree.setValue(openvdb::Coord(10, 10, 11), 3.0); + tree.setValue(openvdb::Coord(11, 10, 11), 3.0); + tree.setValue(openvdb::Coord(11, 11, 11), 3.0); + tree.setValue(openvdb::Coord(10, 11, 11), 3.0); + tree.setValue(openvdb::Coord( 9, 11, 11), 3.0); + tree.setValue(openvdb::Coord( 9, 10, 11), 3.0); + tree.setValue(openvdb::Coord( 9, 9, 11), 3.0); + tree.setValue(openvdb::Coord(10, 9, 11), 3.0); + tree.setValue(openvdb::Coord(11, 9, 11), 3.0); + + tree.setValue(openvdb::Coord(10, 10, 9), 4.0); + tree.setValue(openvdb::Coord(11, 10, 9), 4.0); + tree.setValue(openvdb::Coord(11, 11, 9), 4.0); + tree.setValue(openvdb::Coord(10, 11, 9), 4.0); + tree.setValue(openvdb::Coord( 9, 11, 9), 4.0); + tree.setValue(openvdb::Coord( 9, 10, 9), 4.0); + tree.setValue(openvdb::Coord( 9, 9, 9), 4.0); + tree.setValue(openvdb::Coord(10, 9, 9), 4.0); + tree.setValue(openvdb::Coord(11, 9, 9), 4.0); + + {//using BoxSampler + + // transform used for worldspace interpolation) + openvdb::tools::GridSampler + interpolator(grid); + //openvdb::tools::LinearInterp interpolator(*tree); + + typename GridType::ValueType val = + interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.375, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 11.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 11.0, 11.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(9.0, 11.0, 9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(9.0, 10.0, 9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.1, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.792, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.71, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.01, val, TOLERANCE); + + } + {//using Sampler<1> + + // transform used for worldspace interpolation) + openvdb::tools::GridSampler > + interpolator(grid); + //openvdb::tools::LinearInterp interpolator(*tree); + + typename GridType::ValueType val = + interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.375, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 11.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 11.0, 11.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(9.0, 11.0, 9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(9.0, 10.0, 9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.1, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.792, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.71, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.01, val, TOLERANCE); + } +} + + +template<> +void +TestLinearInterp::test() +{ + using namespace openvdb; + + Vec3s fillValue = Vec3s(256.0f, 256.0f, 256.0f); + + Vec3SGrid grid(fillValue); + Vec3STree& tree = grid.tree(); + + tree.setValue(openvdb::Coord(10, 10, 10), Vec3s(1.0, 1.0, 1.0)); + + tree.setValue(openvdb::Coord(11, 10, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 11, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 11, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 11, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 10, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 9, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 9, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 9, 10), Vec3s(2.0, 2.0, 2.0)); + + tree.setValue(openvdb::Coord(10, 10, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(11, 10, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(11, 11, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(10, 11, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord( 9, 11, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord( 9, 10, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord( 9, 9, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(10, 9, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(11, 9, 11), Vec3s(3.0, 3.0, 3.0)); + + tree.setValue(openvdb::Coord(10, 10, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(11, 10, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(11, 11, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(10, 11, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord( 9, 11, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord( 9, 10, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord( 9, 9, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(10, 9, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(11, 9, 9), Vec3s(4.0, 4.0, 4.0)); + + openvdb::tools::GridSampler + interpolator(grid); + + //openvdb::tools::LinearInterp interpolator(*tree); + + Vec3SGrid::ValueType val = interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.375f))); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(1.f))); + + val = interpolator.sampleVoxel(11.0, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.f))); + + val = interpolator.sampleVoxel(11.0, 11.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.f))); + + val = interpolator.sampleVoxel(11.0, 11.0, 11.0); + CPPUNIT_ASSERT(val.eq(Vec3s(3.f))); + + val = interpolator.sampleVoxel(9.0, 11.0, 9.0); + CPPUNIT_ASSERT(val.eq(Vec3s(4.f))); + + val = interpolator.sampleVoxel(9.0, 10.0, 9.0); + CPPUNIT_ASSERT(val.eq(Vec3s(4.f))); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(1.1f))); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.792f))); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.41f))); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.41f))); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.71f))); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT(val.eq(Vec3s(2.01f))); +} + +template +void +TestLinearInterp::testTree() +{ + float fillValue = 256.0f; + typedef typename GridType::TreeType TreeType; + TreeType tree(fillValue); + + tree.setValue(openvdb::Coord(10, 10, 10), 1.0); + + tree.setValue(openvdb::Coord(11, 10, 10), 2.0); + tree.setValue(openvdb::Coord(11, 11, 10), 2.0); + tree.setValue(openvdb::Coord(10, 11, 10), 2.0); + tree.setValue(openvdb::Coord( 9, 11, 10), 2.0); + tree.setValue(openvdb::Coord( 9, 10, 10), 2.0); + tree.setValue(openvdb::Coord( 9, 9, 10), 2.0); + tree.setValue(openvdb::Coord(10, 9, 10), 2.0); + tree.setValue(openvdb::Coord(11, 9, 10), 2.0); + + tree.setValue(openvdb::Coord(10, 10, 11), 3.0); + tree.setValue(openvdb::Coord(11, 10, 11), 3.0); + tree.setValue(openvdb::Coord(11, 11, 11), 3.0); + tree.setValue(openvdb::Coord(10, 11, 11), 3.0); + tree.setValue(openvdb::Coord( 9, 11, 11), 3.0); + tree.setValue(openvdb::Coord( 9, 10, 11), 3.0); + tree.setValue(openvdb::Coord( 9, 9, 11), 3.0); + tree.setValue(openvdb::Coord(10, 9, 11), 3.0); + tree.setValue(openvdb::Coord(11, 9, 11), 3.0); + + tree.setValue(openvdb::Coord(10, 10, 9), 4.0); + tree.setValue(openvdb::Coord(11, 10, 9), 4.0); + tree.setValue(openvdb::Coord(11, 11, 9), 4.0); + tree.setValue(openvdb::Coord(10, 11, 9), 4.0); + tree.setValue(openvdb::Coord( 9, 11, 9), 4.0); + tree.setValue(openvdb::Coord( 9, 10, 9), 4.0); + tree.setValue(openvdb::Coord( 9, 9, 9), 4.0); + tree.setValue(openvdb::Coord(10, 9, 9), 4.0); + tree.setValue(openvdb::Coord(11, 9, 9), 4.0); + + // transform used for worldspace interpolation) + openvdb::tools::GridSampler + interpolator(tree, openvdb::math::Transform()); + + typename GridType::ValueType val = + interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.375, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 11.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 11.0, 11.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(9.0, 11.0, 9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(9.0, 10.0, 9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.1, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.792, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.71, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.01, val, TOLERANCE); +} + + +template<> +void +TestLinearInterp::testTree() +{ + using namespace openvdb; + + Vec3s fillValue = Vec3s(256.0f, 256.0f, 256.0f); + + Vec3STree tree(fillValue); + + tree.setValue(openvdb::Coord(10, 10, 10), Vec3s(1.0, 1.0, 1.0)); + + tree.setValue(openvdb::Coord(11, 10, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 11, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 11, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 11, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 10, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 9, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 9, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 9, 10), Vec3s(2.0, 2.0, 2.0)); + + tree.setValue(openvdb::Coord(10, 10, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(11, 10, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(11, 11, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(10, 11, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord( 9, 11, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord( 9, 10, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord( 9, 9, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(10, 9, 11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(11, 9, 11), Vec3s(3.0, 3.0, 3.0)); + + tree.setValue(openvdb::Coord(10, 10, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(11, 10, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(11, 11, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(10, 11, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord( 9, 11, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord( 9, 10, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord( 9, 9, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(10, 9, 9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(11, 9, 9), Vec3s(4.0, 4.0, 4.0)); + + openvdb::tools::GridSampler + interpolator(tree, openvdb::math::Transform()); + + //openvdb::tools::LinearInterp interpolator(*tree); + + Vec3SGrid::ValueType val = interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.375f))); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(1.f))); + + val = interpolator.sampleVoxel(11.0, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.f))); + + val = interpolator.sampleVoxel(11.0, 11.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.f))); + + val = interpolator.sampleVoxel(11.0, 11.0, 11.0); + CPPUNIT_ASSERT(val.eq(Vec3s(3.f))); + + val = interpolator.sampleVoxel(9.0, 11.0, 9.0); + CPPUNIT_ASSERT(val.eq(Vec3s(4.f))); + + val = interpolator.sampleVoxel(9.0, 10.0, 9.0); + CPPUNIT_ASSERT(val.eq(Vec3s(4.f))); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(1.1f))); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.792f))); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.41f))); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.41f))); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.71f))); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT(val.eq(Vec3s(2.01f))); +} + +template +void +TestLinearInterp::testAccessor() +{ + float fillValue = 256.0f; + + GridType grid(fillValue); + typedef typename GridType::Accessor AccessorType; + + AccessorType acc = grid.getAccessor(); + + acc.setValue(openvdb::Coord(10, 10, 10), 1.0); + + acc.setValue(openvdb::Coord(11, 10, 10), 2.0); + acc.setValue(openvdb::Coord(11, 11, 10), 2.0); + acc.setValue(openvdb::Coord(10, 11, 10), 2.0); + acc.setValue(openvdb::Coord( 9, 11, 10), 2.0); + acc.setValue(openvdb::Coord( 9, 10, 10), 2.0); + acc.setValue(openvdb::Coord( 9, 9, 10), 2.0); + acc.setValue(openvdb::Coord(10, 9, 10), 2.0); + acc.setValue(openvdb::Coord(11, 9, 10), 2.0); + + acc.setValue(openvdb::Coord(10, 10, 11), 3.0); + acc.setValue(openvdb::Coord(11, 10, 11), 3.0); + acc.setValue(openvdb::Coord(11, 11, 11), 3.0); + acc.setValue(openvdb::Coord(10, 11, 11), 3.0); + acc.setValue(openvdb::Coord( 9, 11, 11), 3.0); + acc.setValue(openvdb::Coord( 9, 10, 11), 3.0); + acc.setValue(openvdb::Coord( 9, 9, 11), 3.0); + acc.setValue(openvdb::Coord(10, 9, 11), 3.0); + acc.setValue(openvdb::Coord(11, 9, 11), 3.0); + + acc.setValue(openvdb::Coord(10, 10, 9), 4.0); + acc.setValue(openvdb::Coord(11, 10, 9), 4.0); + acc.setValue(openvdb::Coord(11, 11, 9), 4.0); + acc.setValue(openvdb::Coord(10, 11, 9), 4.0); + acc.setValue(openvdb::Coord( 9, 11, 9), 4.0); + acc.setValue(openvdb::Coord( 9, 10, 9), 4.0); + acc.setValue(openvdb::Coord( 9, 9, 9), 4.0); + acc.setValue(openvdb::Coord(10, 9, 9), 4.0); + acc.setValue(openvdb::Coord(11, 9, 9), 4.0); + + // transform used for worldspace interpolation) + openvdb::tools::GridSampler + interpolator(acc, grid.transform()); + + typename GridType::ValueType val = + interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.375, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 11.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(11.0, 11.0, 11.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(9.0, 11.0, 9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(9.0, 10.0, 9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.1, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.792, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.71, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.01, val, TOLERANCE); +} + + +template<> +void +TestLinearInterp::testAccessor() +{ + using namespace openvdb; + + Vec3s fillValue = Vec3s(256.0f, 256.0f, 256.0f); + + Vec3SGrid grid(fillValue); + typedef Vec3SGrid::Accessor AccessorType; + AccessorType acc = grid.getAccessor(); + + acc.setValue(openvdb::Coord(10, 10, 10), Vec3s(1.0, 1.0, 1.0)); + + acc.setValue(openvdb::Coord(11, 10, 10), Vec3s(2.0, 2.0, 2.0)); + acc.setValue(openvdb::Coord(11, 11, 10), Vec3s(2.0, 2.0, 2.0)); + acc.setValue(openvdb::Coord(10, 11, 10), Vec3s(2.0, 2.0, 2.0)); + acc.setValue(openvdb::Coord( 9, 11, 10), Vec3s(2.0, 2.0, 2.0)); + acc.setValue(openvdb::Coord( 9, 10, 10), Vec3s(2.0, 2.0, 2.0)); + acc.setValue(openvdb::Coord( 9, 9, 10), Vec3s(2.0, 2.0, 2.0)); + acc.setValue(openvdb::Coord(10, 9, 10), Vec3s(2.0, 2.0, 2.0)); + acc.setValue(openvdb::Coord(11, 9, 10), Vec3s(2.0, 2.0, 2.0)); + + acc.setValue(openvdb::Coord(10, 10, 11), Vec3s(3.0, 3.0, 3.0)); + acc.setValue(openvdb::Coord(11, 10, 11), Vec3s(3.0, 3.0, 3.0)); + acc.setValue(openvdb::Coord(11, 11, 11), Vec3s(3.0, 3.0, 3.0)); + acc.setValue(openvdb::Coord(10, 11, 11), Vec3s(3.0, 3.0, 3.0)); + acc.setValue(openvdb::Coord( 9, 11, 11), Vec3s(3.0, 3.0, 3.0)); + acc.setValue(openvdb::Coord( 9, 10, 11), Vec3s(3.0, 3.0, 3.0)); + acc.setValue(openvdb::Coord( 9, 9, 11), Vec3s(3.0, 3.0, 3.0)); + acc.setValue(openvdb::Coord(10, 9, 11), Vec3s(3.0, 3.0, 3.0)); + acc.setValue(openvdb::Coord(11, 9, 11), Vec3s(3.0, 3.0, 3.0)); + + acc.setValue(openvdb::Coord(10, 10, 9), Vec3s(4.0, 4.0, 4.0)); + acc.setValue(openvdb::Coord(11, 10, 9), Vec3s(4.0, 4.0, 4.0)); + acc.setValue(openvdb::Coord(11, 11, 9), Vec3s(4.0, 4.0, 4.0)); + acc.setValue(openvdb::Coord(10, 11, 9), Vec3s(4.0, 4.0, 4.0)); + acc.setValue(openvdb::Coord( 9, 11, 9), Vec3s(4.0, 4.0, 4.0)); + acc.setValue(openvdb::Coord( 9, 10, 9), Vec3s(4.0, 4.0, 4.0)); + acc.setValue(openvdb::Coord( 9, 9, 9), Vec3s(4.0, 4.0, 4.0)); + acc.setValue(openvdb::Coord(10, 9, 9), Vec3s(4.0, 4.0, 4.0)); + acc.setValue(openvdb::Coord(11, 9, 9), Vec3s(4.0, 4.0, 4.0)); + + openvdb::tools::GridSampler + interpolator(acc, grid.transform()); + + Vec3SGrid::ValueType val = interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.375f))); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(1.0f))); + + val = interpolator.sampleVoxel(11.0, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0f))); + + val = interpolator.sampleVoxel(11.0, 11.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0f))); + + val = interpolator.sampleVoxel(11.0, 11.0, 11.0); + CPPUNIT_ASSERT(val.eq(Vec3s(3.0f))); + + val = interpolator.sampleVoxel(9.0, 11.0, 9.0); + CPPUNIT_ASSERT(val.eq(Vec3s(4.0f))); + + val = interpolator.sampleVoxel(9.0, 10.0, 9.0); + CPPUNIT_ASSERT(val.eq(Vec3s(4.0f))); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(1.1f))); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.792f))); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.41f))); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.41f))); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.71f))); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT(val.eq(Vec3s(2.01f))); +} + +template +void +TestLinearInterp::testConstantValues() +{ + typedef typename GridType::TreeType TreeType; + float fillValue = 256.0f; + + GridType grid(fillValue); + TreeType& tree = grid.tree(); + + // Add values to buffer zero. + tree.setValue(openvdb::Coord(10, 10, 10), 2.0); + + tree.setValue(openvdb::Coord(11, 10, 10), 2.0); + tree.setValue(openvdb::Coord(11, 11, 10), 2.0); + tree.setValue(openvdb::Coord(10, 11, 10), 2.0); + tree.setValue(openvdb::Coord( 9, 11, 10), 2.0); + tree.setValue(openvdb::Coord( 9, 10, 10), 2.0); + tree.setValue(openvdb::Coord( 9, 9, 10), 2.0); + tree.setValue(openvdb::Coord(10, 9, 10), 2.0); + tree.setValue(openvdb::Coord(11, 9, 10), 2.0); + + tree.setValue(openvdb::Coord(10, 10, 11), 2.0); + tree.setValue(openvdb::Coord(11, 10, 11), 2.0); + tree.setValue(openvdb::Coord(11, 11, 11), 2.0); + tree.setValue(openvdb::Coord(10, 11, 11), 2.0); + tree.setValue(openvdb::Coord( 9, 11, 11), 2.0); + tree.setValue(openvdb::Coord( 9, 10, 11), 2.0); + tree.setValue(openvdb::Coord( 9, 9, 11), 2.0); + tree.setValue(openvdb::Coord(10, 9, 11), 2.0); + tree.setValue(openvdb::Coord(11, 9, 11), 2.0); + + tree.setValue(openvdb::Coord(10, 10, 9), 2.0); + tree.setValue(openvdb::Coord(11, 10, 9), 2.0); + tree.setValue(openvdb::Coord(11, 11, 9), 2.0); + tree.setValue(openvdb::Coord(10, 11, 9), 2.0); + tree.setValue(openvdb::Coord( 9, 11, 9), 2.0); + tree.setValue(openvdb::Coord( 9, 10, 9), 2.0); + tree.setValue(openvdb::Coord( 9, 9, 9), 2.0); + tree.setValue(openvdb::Coord(10, 9, 9), 2.0); + tree.setValue(openvdb::Coord(11, 9, 9), 2.0); + + openvdb::tools::GridSampler interpolator(grid); + //openvdb::tools::LinearInterp interpolator(*tree); + + typename GridType::ValueType val = + interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); +} + + +template<> +void +TestLinearInterp::testConstantValues() +{ + using namespace openvdb; + + Vec3s fillValue = Vec3s(256.0f, 256.0f, 256.0f); + + Vec3SGrid grid(fillValue); + Vec3STree& tree = grid.tree(); + + // Add values to buffer zero. + tree.setValue(openvdb::Coord(10, 10, 10), Vec3s(2.0, 2.0, 2.0)); + + tree.setValue(openvdb::Coord(11, 10, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 11, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 11, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 11, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 10, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 9, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 9, 10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 9, 10), Vec3s(2.0, 2.0, 2.0)); + + tree.setValue(openvdb::Coord(10, 10, 11), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 10, 11), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 11, 11), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 11, 11), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 11, 11), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 10, 11), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 9, 11), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 9, 11), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 9, 11), Vec3s(2.0, 2.0, 2.0)); + + tree.setValue(openvdb::Coord(10, 10, 9), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 10, 9), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 11, 9), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 11, 9), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 11, 9), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 10, 9), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( 9, 9, 9), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(10, 9, 9), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(11, 9, 9), Vec3s(2.0, 2.0, 2.0)); + + openvdb::tools::GridSampler interpolator(grid); + //openvdb::tools::LinearInterp interpolator(*tree); + + Vec3SGrid::ValueType val = interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0, 2.0, 2.0))); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0, 2.0, 2.0))); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0, 2.0, 2.0))); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0, 2.0, 2.0))); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0, 2.0, 2.0))); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0, 2.0, 2.0))); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0, 2.0, 2.0))); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0, 2.0, 2.0))); +} + + +template +void +TestLinearInterp::testFillValues() +{ + //typedef typename GridType::TreeType TreeType; + float fillValue = 256.0f; + + GridType grid(fillValue); + //typename GridType::TreeType& tree = grid.tree(); + + openvdb::tools::GridSampler + interpolator(grid); + //openvdb::tools::LinearInterp interpolator(*tree); + + typename GridType::ValueType val = + interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256.0, val, TOLERANCE); +} + + +template<> +void +TestLinearInterp::testFillValues() +{ + using namespace openvdb; + + Vec3s fillValue = Vec3s(256.0f, 256.0f, 256.0f); + + Vec3SGrid grid(fillValue); + //Vec3STree& tree = grid.tree(); + + openvdb::tools::GridSampler + interpolator(grid); + //openvdb::tools::LinearInterp interpolator(*tree); + + Vec3SGrid::ValueType val = interpolator.sampleVoxel(10.5, 10.5, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(256.0, 256.0, 256.0))); + + val = interpolator.sampleVoxel(10.0, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(256.0, 256.0, 256.0))); + + val = interpolator.sampleVoxel(10.1, 10.0, 10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(256.0, 256.0, 256.0))); + + val = interpolator.sampleVoxel(10.8, 10.8, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(256.0, 256.0, 256.0))); + + val = interpolator.sampleVoxel(10.1, 10.8, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(256.0, 256.0, 256.0))); + + val = interpolator.sampleVoxel(10.8, 10.1, 10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(256.0, 256.0, 256.0))); + + val = interpolator.sampleVoxel(10.5, 10.1, 10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(256.0, 256.0, 256.0))); + + val = interpolator.sampleVoxel(10.5, 10.8, 10.1); + CPPUNIT_ASSERT(val.eq(Vec3s(256.0, 256.0, 256.0))); +} + + +template +void +TestLinearInterp::testNegativeIndices() +{ + typedef typename GridType::TreeType TreeType; + float fillValue = 256.0f; + + GridType grid(fillValue); + TreeType& tree = grid.tree(); + + tree.setValue(openvdb::Coord(-10, -10, -10), 1.0); + + tree.setValue(openvdb::Coord(-11, -10, -10), 2.0); + tree.setValue(openvdb::Coord(-11, -11, -10), 2.0); + tree.setValue(openvdb::Coord(-10, -11, -10), 2.0); + tree.setValue(openvdb::Coord( -9, -11, -10), 2.0); + tree.setValue(openvdb::Coord( -9, -10, -10), 2.0); + tree.setValue(openvdb::Coord( -9, -9, -10), 2.0); + tree.setValue(openvdb::Coord(-10, -9, -10), 2.0); + tree.setValue(openvdb::Coord(-11, -9, -10), 2.0); + + tree.setValue(openvdb::Coord(-10, -10, -11), 3.0); + tree.setValue(openvdb::Coord(-11, -10, -11), 3.0); + tree.setValue(openvdb::Coord(-11, -11, -11), 3.0); + tree.setValue(openvdb::Coord(-10, -11, -11), 3.0); + tree.setValue(openvdb::Coord( -9, -11, -11), 3.0); + tree.setValue(openvdb::Coord( -9, -10, -11), 3.0); + tree.setValue(openvdb::Coord( -9, -9, -11), 3.0); + tree.setValue(openvdb::Coord(-10, -9, -11), 3.0); + tree.setValue(openvdb::Coord(-11, -9, -11), 3.0); + + tree.setValue(openvdb::Coord(-10, -10, -9), 4.0); + tree.setValue(openvdb::Coord(-11, -10, -9), 4.0); + tree.setValue(openvdb::Coord(-11, -11, -9), 4.0); + tree.setValue(openvdb::Coord(-10, -11, -9), 4.0); + tree.setValue(openvdb::Coord( -9, -11, -9), 4.0); + tree.setValue(openvdb::Coord( -9, -10, -9), 4.0); + tree.setValue(openvdb::Coord( -9, -9, -9), 4.0); + tree.setValue(openvdb::Coord(-10, -9, -9), 4.0); + tree.setValue(openvdb::Coord(-11, -9, -9), 4.0); + + //openvdb::tools::LinearInterp interpolator(*tree); + openvdb::tools::GridSampler interpolator(grid); + + typename GridType::ValueType val = + interpolator.sampleVoxel(-10.5, -10.5, -10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.375, val, TOLERANCE); + + val = interpolator.sampleVoxel(-10.0, -10.0, -10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(-11.0, -10.0, -10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(-11.0, -11.0, -10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(-11.0, -11.0, -11.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(-9.0, -11.0, -9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(-9.0, -10.0, -9.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, val, TOLERANCE); + + val = interpolator.sampleVoxel(-10.1, -10.0, -10.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.1, val, TOLERANCE); + + val = interpolator.sampleVoxel(-10.8, -10.8, -10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.792, val, TOLERANCE); + + val = interpolator.sampleVoxel(-10.1, -10.8, -10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(-10.8, -10.1, -10.5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.41, val, TOLERANCE); + + val = interpolator.sampleVoxel(-10.5, -10.1, -10.8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.71, val, TOLERANCE); + + val = interpolator.sampleVoxel(-10.5, -10.8, -10.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.01, val, TOLERANCE); +} + + +template<> +void +TestLinearInterp::testNegativeIndices() +{ + using namespace openvdb; + + Vec3s fillValue = Vec3s(256.0f, 256.0f, 256.0f); + + Vec3SGrid grid(fillValue); + Vec3STree& tree = grid.tree(); + + tree.setValue(openvdb::Coord(-10, -10, -10), Vec3s(1.0, 1.0, 1.0)); + + tree.setValue(openvdb::Coord(-11, -10, -10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(-11, -11, -10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(-10, -11, -10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( -9, -11, -10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( -9, -10, -10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord( -9, -9, -10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(-10, -9, -10), Vec3s(2.0, 2.0, 2.0)); + tree.setValue(openvdb::Coord(-11, -9, -10), Vec3s(2.0, 2.0, 2.0)); + + tree.setValue(openvdb::Coord(-10, -10, -11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(-11, -10, -11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(-11, -11, -11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(-10, -11, -11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord( -9, -11, -11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord( -9, -10, -11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord( -9, -9, -11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(-10, -9, -11), Vec3s(3.0, 3.0, 3.0)); + tree.setValue(openvdb::Coord(-11, -9, -11), Vec3s(3.0, 3.0, 3.0)); + + tree.setValue(openvdb::Coord(-10, -10, -9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(-11, -10, -9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(-11, -11, -9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(-10, -11, -9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord( -9, -11, -9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord( -9, -10, -9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord( -9, -9, -9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(-10, -9, -9), Vec3s(4.0, 4.0, 4.0)); + tree.setValue(openvdb::Coord(-11, -9, -9), Vec3s(4.0, 4.0, 4.0)); + + openvdb::tools::GridSampler interpolator(grid); + //openvdb::tools::LinearInterp interpolator(*tree); + + Vec3SGrid::ValueType val = interpolator.sampleVoxel(-10.5, -10.5, -10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.375f))); + + val = interpolator.sampleVoxel(-10.0, -10.0, -10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(1.0f))); + + val = interpolator.sampleVoxel(-11.0, -10.0, -10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0f))); + + val = interpolator.sampleVoxel(-11.0, -11.0, -10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(2.0f))); + + val = interpolator.sampleVoxel(-11.0, -11.0, -11.0); + CPPUNIT_ASSERT(val.eq(Vec3s(3.0f))); + + val = interpolator.sampleVoxel(-9.0, -11.0, -9.0); + CPPUNIT_ASSERT(val.eq(Vec3s(4.0f))); + + val = interpolator.sampleVoxel(-9.0, -10.0, -9.0); + CPPUNIT_ASSERT(val.eq(Vec3s(4.0f))); + + val = interpolator.sampleVoxel(-10.1, -10.0, -10.0); + CPPUNIT_ASSERT(val.eq(Vec3s(1.1f))); + + val = interpolator.sampleVoxel(-10.8, -10.8, -10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.792f))); + + val = interpolator.sampleVoxel(-10.1, -10.8, -10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.41f))); + + val = interpolator.sampleVoxel(-10.8, -10.1, -10.5); + CPPUNIT_ASSERT(val.eq(Vec3s(2.41f))); + + val = interpolator.sampleVoxel(-10.5, -10.1, -10.8); + CPPUNIT_ASSERT(val.eq(Vec3s(2.71f))); + + val = interpolator.sampleVoxel(-10.5, -10.8, -10.1); + CPPUNIT_ASSERT(val.eq(Vec3s(2.01f))); +} + + +template +void +TestLinearInterp::testStencilsMatch() +{ + typedef typename GridType::ValueType ValueType; + + GridType grid; + typename GridType::TreeType& tree = grid.tree(); + + // using mostly recurring numbers + + tree.setValue(openvdb::Coord(0, 0, 0), ValueType(1.0/3.0)); + tree.setValue(openvdb::Coord(0, 1, 0), ValueType(1.0/11.0)); + tree.setValue(openvdb::Coord(0, 0, 1), ValueType(1.0/81.0)); + tree.setValue(openvdb::Coord(1, 0, 0), ValueType(1.0/97.0)); + tree.setValue(openvdb::Coord(1, 1, 0), ValueType(1.0/61.0)); + tree.setValue(openvdb::Coord(0, 1, 1), ValueType(9.0/7.0)); + tree.setValue(openvdb::Coord(1, 0, 1), ValueType(9.0/11.0)); + tree.setValue(openvdb::Coord(1, 1, 1), ValueType(22.0/7.0)); + + const openvdb::Vec3f pos(7.0f/12.0f, 1.0f/3.0f, 2.0f/3.0f); + + {//using BoxSampler and BoxStencil + + openvdb::tools::GridSampler + interpolator(grid); + + openvdb::math::BoxStencil + stencil(grid); + + typename GridType::ValueType val1 = interpolator.sampleVoxel(pos.x(), pos.y(), pos.z()); + + stencil.moveTo(pos); + typename GridType::ValueType val2 = stencil.interpolation(pos); + CPPUNIT_ASSERT_EQUAL(val1, val2); + } +} + +template<> +void +TestLinearInterp::testStencilsMatch() {} + diff --git a/openvdb/unittest/TestMaps.cc b/openvdb/unittest/TestMaps.cc new file mode 100644 index 00000000..8b8858bd --- /dev/null +++ b/openvdb/unittest/TestMaps.cc @@ -0,0 +1,799 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + + +class TestMaps: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestMaps); + CPPUNIT_TEST(testTranslation); + CPPUNIT_TEST(testRotation); + CPPUNIT_TEST(testScaleDefault); + CPPUNIT_TEST(testScaleTranslate); + CPPUNIT_TEST(testUniformScaleTranslate); + CPPUNIT_TEST(testDecomposition); + CPPUNIT_TEST(testFrustum); + CPPUNIT_TEST(testCalcBoundingBox); + CPPUNIT_TEST(testApproxInverse); + CPPUNIT_TEST(testUniformScale); + CPPUNIT_TEST(testJacobians); + CPPUNIT_TEST_SUITE_END(); + + void testTranslation(); + void testRotation(); + void testScaleDefault(); + void testScaleTranslate(); + void testUniformScaleTranslate(); + void testDecomposition(); + void testFrustum(); + void testCalcBoundingBox(); + void testApproxInverse(); + void testUniformScale(); + void testJacobians(); + //void testIsType(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMaps); + +void +TestMaps::testApproxInverse() +{ + using namespace openvdb::math; + + Mat4d singular = Mat4d::identity(); + singular[1][1] = 0.f; + { + Mat4d singularInv = approxInverse(singular); + + CPPUNIT_ASSERT( singular == singularInv ); + } + { + Mat4d rot = Mat4d::identity(); + rot.setToRotation(X_AXIS, M_PI/4.); + + Mat4d rotInv = rot.inverse(); + Mat4d mat = rotInv * singular * rot; + + Mat4d singularInv = approxInverse(mat); + + // this matrix is equal to its own singular inverse + CPPUNIT_ASSERT( mat.eq(singularInv) ); + + } + { + Mat4d m = Mat4d::identity(); + m[0][1] = 1; + + // should give true inverse, since this matrix has det=1 + Mat4d minv = approxInverse(m); + + Mat4d prod = m * minv; + CPPUNIT_ASSERT( prod.eq( Mat4d::identity() ) ); + } + { + Mat4d m = Mat4d::identity(); + m[0][1] = 1; + m[1][1] = 0; + // should give true inverse, since this matrix has det=1 + Mat4d minv = approxInverse(m); + + Mat4d expected = Mat4d::zero(); + expected[3][3] = 1; + CPPUNIT_ASSERT( minv.eq(expected ) ); + } + + +} + + +void +TestMaps::testUniformScale() +{ + using namespace openvdb::math; + + AffineMap map; + + CPPUNIT_ASSERT(map.hasUniformScale()); + + // Apply uniform scale: should still have square voxels + map.accumPreScale(Vec3d(2, 2, 2)); + + CPPUNIT_ASSERT(map.hasUniformScale()); + + // Apply a rotation, should still have squaure voxels. + map.accumPostRotation(X_AXIS, 2.5); + + CPPUNIT_ASSERT(map.hasUniformScale()); + + // non uniform scaling will stretch the voxels + map.accumPostScale(Vec3d(1, 3, 1) ); + + CPPUNIT_ASSERT(!map.hasUniformScale()); +} + +void +TestMaps::testTranslation() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + TranslationMap::Ptr translation(new TranslationMap(Vec3d(1,1,1))); + CPPUNIT_ASSERT(is_linear::value); + + TranslationMap another_translation(Vec3d(1,1,1)); + CPPUNIT_ASSERT(another_translation == *translation); + + TranslationMap::Ptr translate_by_two(new TranslationMap(Vec3d(2,2,2))); + + CPPUNIT_ASSERT(*translate_by_two != *translation); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(translate_by_two->determinant(), 1, TOL); + + CPPUNIT_ASSERT(translate_by_two->hasUniformScale()); + + /// apply the map forward + Vec3d unit(1,0,0); + Vec3d result = translate_by_two->applyMap(unit); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), 3, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), 2, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), 2, TOL); + + /// invert the map + result = translate_by_two->applyInverseMap(result); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), 0, TOL); + + /// Inverse Jacobian Transpose + result = translate_by_two->applyIJT(result); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), 0, TOL); + + /// Jacobian Transpose + result = translate_by_two->applyJT(translate_by_two->applyIJT(unit)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), unit(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), unit(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), unit(2), TOL); + + + MapBase::Ptr inverse = translation->inverseMap(); + CPPUNIT_ASSERT(inverse->type() == TranslationMap::mapType()); + // apply the map forward and the inverse map back + result = inverse->applyMap(translation->applyMap(unit)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), 0, TOL); + + +} + +void +TestMaps::testScaleDefault() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + // testing default constructor + // should be the identity + ScaleMap::Ptr scale(new ScaleMap()); + Vec3d unit(1, 1, 1); + + Vec3d result = scale->applyMap(unit); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(unit(0), result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(unit(1), result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(unit(2), result(2), TOL); + + result = scale->applyInverseMap(unit); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(unit(0), result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(unit(1), result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(unit(2), result(2), TOL); + + + MapBase::Ptr inverse = scale->inverseMap(); + CPPUNIT_ASSERT(inverse->type() == ScaleMap::mapType()); + // apply the map forward and the inverse map back + result = inverse->applyMap(scale->applyMap(unit)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), unit(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), unit(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), unit(2), TOL); + + +} + +void +TestMaps::testRotation() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + double pi = 4.*atan(1.); + UnitaryMap::Ptr rotation(new UnitaryMap(Vec3d(1,0,0), pi/2)); + + CPPUNIT_ASSERT(is_linear::value); + + UnitaryMap another_rotation(Vec3d(1,0,0), pi/2.); + CPPUNIT_ASSERT(another_rotation == *rotation); + + UnitaryMap::Ptr rotation_two(new UnitaryMap(Vec3d(1,0,0), pi/4.)); + + CPPUNIT_ASSERT(*rotation_two != *rotation); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(rotation->determinant(), 1, TOL); + + CPPUNIT_ASSERT(rotation_two->hasUniformScale()); + + /// apply the map forward + Vec3d unit(0,1,0); + Vec3d result = rotation->applyMap(unit); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(2), TOL); + + /// invert the map + result = rotation->applyInverseMap(result); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(2), TOL); + + /// Inverse Jacobian Transpose + result = rotation_two->applyIJT(result); // rotate backwards + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(sqrt(2.)/2, result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(sqrt(2.)/2, result(2), TOL); + + /// Jacobian Transpose + result = rotation_two->applyJT(rotation_two->applyIJT(unit)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), unit(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), unit(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), unit(2), TOL); + + + // Test inverse map + MapBase::Ptr inverse = rotation->inverseMap(); + CPPUNIT_ASSERT(inverse->type() == UnitaryMap::mapType()); + // apply the map forward and the inverse map back + result = inverse->applyMap(rotation->applyMap(unit)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), unit(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), unit(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), unit(2), TOL); +} + + +void +TestMaps::testScaleTranslate() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + CPPUNIT_ASSERT(is_linear::value); + + TranslationMap::Ptr translation(new TranslationMap(Vec3d(1,1,1))); + ScaleMap::Ptr scale(new ScaleMap(Vec3d(1,2,3))); + + ScaleTranslateMap::Ptr scaleAndTranslate( + new ScaleTranslateMap(*scale, *translation)); + + TranslationMap translate_by_two(Vec3d(2,2,2)); + ScaleTranslateMap another_scaleAndTranslate(*scale, translate_by_two); + + CPPUNIT_ASSERT(another_scaleAndTranslate != *scaleAndTranslate); + + CPPUNIT_ASSERT(!scaleAndTranslate->hasUniformScale()); + //CPPUNIT_ASSERT_DOUBLES_EQUAL(scaleAndTranslate->determinant(), 6, TOL); + + /// apply the map forward + Vec3d unit(1,0,0); + Vec3d result = scaleAndTranslate->applyMap(unit); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2, result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(2), TOL); + + /// invert the map + result = scaleAndTranslate->applyInverseMap(result); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(2), TOL); + + /// Inverse Jacobian Transpose + result = Vec3d(0,2,0); + result = scaleAndTranslate->applyIJT(result ); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(2), TOL); + + + /// Jacobian Transpose + result = scaleAndTranslate->applyJT(scaleAndTranslate->applyIJT(unit)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), unit(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), unit(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), unit(2), TOL); + + + // Test inverse map + MapBase::Ptr inverse = scaleAndTranslate->inverseMap(); + CPPUNIT_ASSERT(inverse->type() == ScaleTranslateMap::mapType()); + // apply the map forward and the inverse map back + result = inverse->applyMap(scaleAndTranslate->applyMap(unit)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), unit(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), unit(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), unit(2), TOL); + +} + + +void +TestMaps::testUniformScaleTranslate() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + CPPUNIT_ASSERT(is_linear::value); + CPPUNIT_ASSERT(is_linear::value); + + TranslationMap::Ptr translation(new TranslationMap(Vec3d(1,1,1))); + UniformScaleMap::Ptr scale(new UniformScaleMap(2)); + + UniformScaleTranslateMap::Ptr scaleAndTranslate( + new UniformScaleTranslateMap(*scale, *translation)); + + TranslationMap translate_by_two(Vec3d(2,2,2)); + UniformScaleTranslateMap another_scaleAndTranslate(*scale, translate_by_two); + + CPPUNIT_ASSERT(another_scaleAndTranslate != *scaleAndTranslate); + CPPUNIT_ASSERT(scaleAndTranslate->hasUniformScale()); + //CPPUNIT_ASSERT_DOUBLES_EQUAL(scaleAndTranslate->determinant(), 6, TOL); + + /// apply the map forward + Vec3d unit(1,0,0); + Vec3d result = scaleAndTranslate->applyMap(unit); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3, result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(2), TOL); + + /// invert the map + result = scaleAndTranslate->applyInverseMap(result); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(2), TOL); + + /// Inverse Jacobian Transpose + result = Vec3d(0,2,0); + result = scaleAndTranslate->applyIJT(result ); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1, result(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0, result(2), TOL); + + + /// Jacobian Transpose + result = scaleAndTranslate->applyJT(scaleAndTranslate->applyIJT(unit)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), unit(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), unit(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), unit(2), TOL); + + + + // Test inverse map + MapBase::Ptr inverse = scaleAndTranslate->inverseMap(); + CPPUNIT_ASSERT(inverse->type() == UniformScaleTranslateMap::mapType()); + // apply the map forward and the inverse map back + result = inverse->applyMap(scaleAndTranslate->applyMap(unit)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), unit(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), unit(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), unit(2), TOL); + +} + + +void +TestMaps::testDecomposition() +{ + using namespace openvdb::math; + + //double TOL = 1e-7; + + CPPUNIT_ASSERT(is_linear::value); + CPPUNIT_ASSERT(is_linear::value); + CPPUNIT_ASSERT(is_linear::value); + CPPUNIT_ASSERT(is_linear::value); + + Mat4d matrix(Mat4d::identity()); + Vec3d input_translation(0,0,1); + matrix.setTranslation(input_translation); + + + matrix(0,0) = 1.8930039; + matrix(1,0) = -0.120080537; + matrix(2,0) = -0.497615212; + + matrix(0,1) = -0.120080537; + matrix(1,1) = 2.643265436; + matrix(2,1) = 0.6176957495; + + matrix(0,2) = -0.497615212; + matrix(1,2) = 0.6176957495; + matrix(2,2) = 1.4637305884; + + FullyDecomposedMap::Ptr decomp = createFullyDecomposedMap(matrix); + + /// the singular values + const Vec3& singular_values = + decomp->firstMap().firstMap().secondMap().getScale(); + /// expected values + Vec3d expected_values(2, 3, 1); + + CPPUNIT_ASSERT( isApproxEqual(singular_values, expected_values) ); + + const Vec3& the_translation = decomp->secondMap().secondMap().getTranslation(); + CPPUNIT_ASSERT( isApproxEqual(the_translation, input_translation)); +} + + +void +TestMaps::testFrustum() +{ + using namespace openvdb::math; + + openvdb::BBoxd bbox(Vec3d(0), Vec3d(100)); + NonlinearFrustumMap frustum(bbox, 1./6., 5); + /// frustum will have depth, far plane - near plane = 5 + /// the frustum has width 1 in the front and 6 in the back + + Vec3d trans(2,2,2); + NonlinearFrustumMap::Ptr map = + openvdb::StaticPtrCast( + frustum.preScale(Vec3d(10,10,10))->postTranslate(trans)); + + CPPUNIT_ASSERT(!map->hasUniformScale()); + + Vec3d result; + result = map->voxelSize(); + + CPPUNIT_ASSERT( isApproxEqual(result.x(), 0.1)); + CPPUNIT_ASSERT( isApproxEqual(result.y(), 0.1)); + CPPUNIT_ASSERT( isApproxEqual(result.z(), 0.5, 0.0001)); + //--------- Front face + Vec3d corner(0,0,0); + result = map->applyMap(corner); + CPPUNIT_ASSERT(isApproxEqual(result, Vec3d(-5, -5, 0) + trans)); + + corner = Vec3d(100,0,0); + result = map->applyMap(corner); + CPPUNIT_ASSERT( isApproxEqual(result, Vec3d(5, -5, 0) + trans)); + + corner = Vec3d(0,100,0); + result = map->applyMap(corner); + CPPUNIT_ASSERT( isApproxEqual(result, Vec3d(-5, 5, 0) + trans)); + + corner = Vec3d(100,100,0); + result = map->applyMap(corner); + CPPUNIT_ASSERT( isApproxEqual(result, Vec3d(5, 5, 0) + trans)); + + //--------- Back face + corner = Vec3d(0,0,100); + result = map->applyMap(corner); + CPPUNIT_ASSERT( isApproxEqual(result, Vec3d(-30, -30, 50) + trans)); // 10*(5/2 + 1/2) = 30 + + corner = Vec3d(100,0,100); + result = map->applyMap(corner); + CPPUNIT_ASSERT( isApproxEqual(result, Vec3d(30, -30, 50) + trans)); + + corner = Vec3d(0,100,100); + result = map->applyMap(corner); + CPPUNIT_ASSERT( isApproxEqual(result, Vec3d(-30, 30, 50) + trans)); + + corner = Vec3d(100,100,100); + result = map->applyMap(corner); + CPPUNIT_ASSERT( isApproxEqual(result, Vec3d(30, 30, 50) + trans)); + + + // invert a single corner + result = map->applyInverseMap(Vec3d(30,30,50) + trans); + CPPUNIT_ASSERT( isApproxEqual(result, Vec3d(100, 100, 100))); + + CPPUNIT_ASSERT(map->hasSimpleAffine()); + + /// create a frustum from from camera type information + + // the location of the camera + Vec3d position(100,10,1); + // the direction the camera is pointing + Vec3d direction(0,1,1); + direction.normalize(); + + // the up-direction for the camera + Vec3d up(10,3,-3); + + // distance from camera to near-plane measured in the direction 'direction' + double z_near = 100.; + // depth of frustum to far-plane to near-plane + double depth = 500.; + //aspect ratio of frustum: width/height + double aspect = 2; + + // voxel count in frustum. the y_count = x_count / aspect + Coord::ValueType x_count = 500; + Coord::ValueType z_count = 5000; + + + NonlinearFrustumMap frustumMap_from_camera( + position, direction, up, aspect, z_near, depth, x_count, z_count); + Vec3d center; + // find the center of the near plane and make sure it is in the correct place + center = Vec3d(0,0,0); + center += frustumMap_from_camera.applyMap(Vec3d(0,0,0)); + center += frustumMap_from_camera.applyMap(Vec3d(500,0,0)); + center += frustumMap_from_camera.applyMap(Vec3d(0,250,0)); + center += frustumMap_from_camera.applyMap(Vec3d(500,250,0)); + center = center /4.; + CPPUNIT_ASSERT( isApproxEqual(center, position + z_near * direction)); + // find the center of the far plane and make sure it is in the correct place + center = Vec3d(0,0,0); + center += frustumMap_from_camera.applyMap(Vec3d( 0, 0,5000)); + center += frustumMap_from_camera.applyMap(Vec3d(500, 0,5000)); + center += frustumMap_from_camera.applyMap(Vec3d( 0,250,5000)); + center += frustumMap_from_camera.applyMap(Vec3d(500,250,5000)); + center = center /4.; + CPPUNIT_ASSERT( isApproxEqual(center, position + (z_near+depth) * direction)); + // check that the frustum has the correct heigh on the near plane + Vec3d corner1 = frustumMap_from_camera.applyMap(Vec3d(0,0,0)); + Vec3d corner2 = frustumMap_from_camera.applyMap(Vec3d(0,250,0)); + Vec3d side = corner2-corner1; + CPPUNIT_ASSERT( isApproxEqual( side.length(), 2 * up.length())); + // check that the frustum is correctly oriented w.r.t up + side.normalize(); + CPPUNIT_ASSERT( isApproxEqual( side * (up.length()), up)); + // check that the linear map inside the frustum is a simple affine map (i.e. has no shear) + CPPUNIT_ASSERT(frustumMap_from_camera.hasSimpleAffine()); +} + + +void +TestMaps::testCalcBoundingBox() +{ + using namespace openvdb::math; + + openvdb::BBoxd world_bbox(Vec3d(0,0,0), Vec3d(1,1,1)); + openvdb::BBoxd voxel_bbox; + openvdb::BBoxd expected; + { + AffineMap affine; + affine.accumPreScale(Vec3d(2,2,2)); + + openvdb::util::calculateBounds(affine, world_bbox, voxel_bbox); + + expected = openvdb::BBoxd(Vec3d(0,0,0), Vec3d(0.5, 0.5, 0.5)); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.min(), expected.min())); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.max(), expected.max())); + + affine.accumPostTranslation(Vec3d(1,1,1)); + openvdb::util::calculateBounds(affine, world_bbox, voxel_bbox); + expected = openvdb::BBoxd(Vec3d(-0.5,-0.5,-0.5), Vec3d(0, 0, 0)); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.min(), expected.min())); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.max(), expected.max())); + } + { + AffineMap affine; + affine.accumPreScale(Vec3d(2,2,2)); + affine.accumPostTranslation(Vec3d(1,1,1)); + // test a sphere: + Vec3d center(0,0,0); + double radius = 10; + + openvdb::util::calculateBounds(affine, center, radius, voxel_bbox); + expected = openvdb::BBoxd(Vec3d(-5.5,-5.5,-5.5), Vec3d(4.5, 4.5, 4.5)); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.min(), expected.min())); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.max(), expected.max())); + } + { + AffineMap affine; + affine.accumPreScale(Vec3d(2,2,2)); + double pi = 4.*atan(1.); + affine.accumPreRotation(X_AXIS, pi/4.); + Vec3d center(0,0,0); + double radius = 10; + + openvdb::util::calculateBounds(affine, center, radius, voxel_bbox); + expected = openvdb::BBoxd(Vec3d(-5,-5,-5), Vec3d(5, 5, 5)); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.min(), expected.min())); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.max(), expected.max())); + } + { + AffineMap affine; + affine.accumPreScale(Vec3d(2,1,1)); + double pi = 4.*atan(1.); + affine.accumPreRotation(X_AXIS, pi/4.); + Vec3d center(0,0,0); + double radius = 10; + + openvdb::util::calculateBounds(affine, center, radius, voxel_bbox); + expected = openvdb::BBoxd(Vec3d(-5,-10,-10), Vec3d(5, 10, 10)); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.min(), expected.min())); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.max(), expected.max())); + } + { + AffineMap affine; + affine.accumPreScale(Vec3d(2,1,1)); + double pi = 4.*atan(1.); + affine.accumPreRotation(X_AXIS, pi/4.); + affine.accumPostTranslation(Vec3d(1,1,1)); + Vec3d center(1,1,1); + double radius = 10; + + openvdb::util::calculateBounds(affine, center, radius, voxel_bbox); + expected = openvdb::BBoxd(Vec3d(-5,-10,-10), Vec3d(5, 10, 10)); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.min(), expected.min())); + CPPUNIT_ASSERT(isApproxEqual(voxel_bbox.max(), expected.max())); + } + { + openvdb::BBoxd bbox(Vec3d(0), Vec3d(100)); + NonlinearFrustumMap frustum(bbox, 2, 5); + NonlinearFrustumMap::Ptr map = + openvdb::StaticPtrCast( + frustum.preScale(Vec3d(2,2,2))); + Vec3d center(20,20,10); + double radius(1); + + openvdb::util::calculateBounds(*map, center, radius, voxel_bbox); + } +} +void +TestMaps::testJacobians() +{ + using namespace openvdb::math; + const double TOL = 1e-7; + { + AffineMap affine; + + const int n = 10; + const double dtheta = M_PI / n; + + const Vec3d test(1,2,3); + const Vec3d origin(0,0,0); + + for (int i = 0; i < n; ++i) { + double theta = i * dtheta; + + affine.accumPostRotation(X_AXIS, theta); + + Vec3d result = affine.applyJacobian(test); + Vec3d expected = affine.applyMap(test) - affine.applyMap(origin); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), expected(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), expected(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), expected(2), TOL); + + Vec3d tmp = affine.applyInverseJacobian(result); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(0), test(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(1), test(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(2), test(2), TOL); + } + } + + { + UniformScaleMap scale(3); + const Vec3d test(1,2,3); + const Vec3d origin(0,0,0); + + + Vec3d result = scale.applyJacobian(test); + Vec3d expected = scale.applyMap(test) - scale.applyMap(origin); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), expected(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), expected(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), expected(2), TOL); + + Vec3d tmp = scale.applyInverseJacobian(result); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(0), test(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(1), test(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(2), test(2), TOL); + } + + { + ScaleMap scale(Vec3d(1,2,3)); + const Vec3d test(1,2,3); + const Vec3d origin(0,0,0); + + + Vec3d result = scale.applyJacobian(test); + Vec3d expected = scale.applyMap(test) - scale.applyMap(origin); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), expected(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), expected(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), expected(2), TOL); + + Vec3d tmp = scale.applyInverseJacobian(result); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(0), test(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(1), test(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(2), test(2), TOL); + } + { + TranslationMap map(Vec3d(1,2,3)); + const Vec3d test(1,2,3); + const Vec3d origin(0,0,0); + + + Vec3d result = map.applyJacobian(test); + Vec3d expected = map.applyMap(test) - map.applyMap(origin); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), expected(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), expected(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), expected(2), TOL); + + Vec3d tmp = map.applyInverseJacobian(result); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(0), test(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(1), test(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(2), test(2), TOL); + } + { + ScaleTranslateMap map(Vec3d(1,2,3), Vec3d(3,5,4)); + const Vec3d test(1,2,3); + const Vec3d origin(0,0,0); + + + Vec3d result = map.applyJacobian(test); + Vec3d expected = map.applyMap(test) - map.applyMap(origin); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(0), expected(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(1), expected(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(result(2), expected(2), TOL); + + Vec3d tmp = map.applyInverseJacobian(result); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(0), test(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(1), test(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(2), test(2), TOL); + } + { + openvdb::BBoxd bbox(Vec3d(0), Vec3d(100)); + NonlinearFrustumMap frustum(bbox, 1./6., 5); + /// frustum will have depth, far plane - near plane = 5 + /// the frustum has width 1 in the front and 6 in the back + + Vec3d trans(2,2,2); + NonlinearFrustumMap::Ptr map = + openvdb::StaticPtrCast( + frustum.preScale(Vec3d(10,10,10))->postTranslate(trans)); + + const Vec3d test(1,2,3); + const Vec3d origin(0, 0, 0); + + // these two drop down to just the linear part + Vec3d lresult = map->applyJacobian(test); + Vec3d ltmp = map->applyInverseJacobian(lresult); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(ltmp(0), test(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(ltmp(1), test(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(ltmp(2), test(2), TOL); + + Vec3d isloc(4,5,6); + // these two drop down to just the linear part + Vec3d result = map->applyJacobian(test, isloc); + Vec3d tmp = map->applyInverseJacobian(result, isloc); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(0), test(0), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(1), test(1), TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(tmp(2), test(2), TOL); + + + + } + + +} diff --git a/openvdb/unittest/TestMat4Metadata.cc b/openvdb/unittest/TestMat4Metadata.cc new file mode 100644 index 00000000..193d849a --- /dev/null +++ b/openvdb/unittest/TestMat4Metadata.cc @@ -0,0 +1,100 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + +class TestMat4Metadata : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestMat4Metadata); + CPPUNIT_TEST(testMat4s); + CPPUNIT_TEST(testMat4d); + CPPUNIT_TEST_SUITE_END(); + + void testMat4s(); + void testMat4d(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMat4Metadata); + +void +TestMat4Metadata::testMat4s() +{ + using namespace openvdb; + + Metadata::Ptr m(new Mat4SMetadata(openvdb::math::Mat4s(1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f))); + Metadata::Ptr m3 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast( m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m3.get()) != 0); + + CPPUNIT_ASSERT( m->typeName().compare("mat4s") == 0); + CPPUNIT_ASSERT(m3->typeName().compare("mat4s") == 0); + + Mat4SMetadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == openvdb::math::Mat4s(1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f)); + s->value() = openvdb::math::Mat4s(3.0f, 3.0f, 3.0f, 3.0f, + 3.0f, 3.0f, 3.0f, 3.0f, + 3.0f, 3.0f, 3.0f, 3.0f, + 3.0f, 3.0f, 3.0f, 3.0f); + CPPUNIT_ASSERT(s->value() == openvdb::math::Mat4s(3.0f, 3.0f, 3.0f, 3.0f, + 3.0f, 3.0f, 3.0f, 3.0f, + 3.0f, 3.0f, 3.0f, 3.0f, + 3.0f, 3.0f, 3.0f, 3.0f)); + + m3->copy(*s); + + s = dynamic_cast(m3.get()); + CPPUNIT_ASSERT(s->value() == openvdb::math::Mat4s(3.0f, 3.0f, 3.0f, 3.0f, + 3.0f, 3.0f, 3.0f, 3.0f, + 3.0f, 3.0f, 3.0f, 3.0f, + 3.0f, 3.0f, 3.0f, 3.0f)); +} + +void +TestMat4Metadata::testMat4d() +{ + using namespace openvdb; + + Metadata::Ptr m(new Mat4DMetadata(openvdb::math::Mat4d(1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0))); + Metadata::Ptr m3 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast( m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m3.get()) != 0); + + CPPUNIT_ASSERT( m->typeName().compare("mat4d") == 0); + CPPUNIT_ASSERT(m3->typeName().compare("mat4d") == 0); + + Mat4DMetadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == openvdb::math::Mat4d(1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0)); + s->value() = openvdb::math::Mat4d(3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0); + CPPUNIT_ASSERT(s->value() == openvdb::math::Mat4d(3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0)); + + m3->copy(*s); + + s = dynamic_cast(m3.get()); + CPPUNIT_ASSERT(s->value() == openvdb::math::Mat4d(3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0)); +} diff --git a/openvdb/unittest/TestMath.cc b/openvdb/unittest/TestMath.cc new file mode 100644 index 00000000..18eb7082 --- /dev/null +++ b/openvdb/unittest/TestMath.cc @@ -0,0 +1,223 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include + + +class TestMath: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestMath); + CPPUNIT_TEST(testAll); + CPPUNIT_TEST(testRandomInt); + CPPUNIT_TEST(testRandom01); + CPPUNIT_TEST(testMinMaxIndex); + CPPUNIT_TEST_SUITE_END(); + + void testAll(); + void testRandomInt(); + void testRandom01(); + void testMinMaxIndex(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMath); + + +// This suite of tests obviously needs to be expanded! +void +TestMath::testAll() +{ + using namespace openvdb; + + {// Sign + CPPUNIT_ASSERT_EQUAL(math::Sign( 3 ), 1); + CPPUNIT_ASSERT_EQUAL(math::Sign(-1.0 ),-1); + CPPUNIT_ASSERT_EQUAL(math::Sign( 0.0f), 0); + } + {// SignChange + CPPUNIT_ASSERT( math::SignChange( -1, 1)); + CPPUNIT_ASSERT(!math::SignChange( 0.0f, 0.5f)); + CPPUNIT_ASSERT( math::SignChange( 0.0f,-0.5f)); + CPPUNIT_ASSERT( math::SignChange(-0.1, 0.0001)); + + } + {// isApproxZero + CPPUNIT_ASSERT( math::isApproxZero( 0.0f)); + CPPUNIT_ASSERT(!math::isApproxZero( 9.0e-6f)); + CPPUNIT_ASSERT(!math::isApproxZero(-9.0e-6f)); + CPPUNIT_ASSERT( math::isApproxZero( 9.0e-9f)); + CPPUNIT_ASSERT( math::isApproxZero(-9.0e-9f)); + CPPUNIT_ASSERT( math::isApproxZero( 0.01, 0.1)); + } + {// Cbrt + const double a = math::Cbrt(3.0); + CPPUNIT_ASSERT(math::isApproxEqual(a*a*a, 3.0, 1e-6)); + } + {// isNegative + CPPUNIT_ASSERT(!std::is_signed::value); + CPPUNIT_ASSERT(std::is_signed::value); + CPPUNIT_ASSERT(!std::is_signed::value); + //CPPUNIT_ASSERT(std::is_signed::value);//fails! + //CPPUNIT_ASSERT(std::is_signed::value);//fails! + + CPPUNIT_ASSERT( math::isNegative(-1.0f)); + CPPUNIT_ASSERT(!math::isNegative( 1.0f)); + CPPUNIT_ASSERT( math::isNegative(-1.0)); + CPPUNIT_ASSERT(!math::isNegative( 1.0)); + CPPUNIT_ASSERT(!math::isNegative(true)); + CPPUNIT_ASSERT(!math::isNegative(false)); + CPPUNIT_ASSERT(!math::isNegative(1u)); + CPPUNIT_ASSERT( math::isNegative(-1)); + CPPUNIT_ASSERT(!math::isNegative( 1)); + } + {// zeroVal + CPPUNIT_ASSERT_EQUAL(zeroVal(), false); + CPPUNIT_ASSERT_EQUAL(zeroVal(), int(0)); + CPPUNIT_ASSERT_EQUAL(zeroVal(), 0.0f); + CPPUNIT_ASSERT_EQUAL(zeroVal(), 0.0); + CPPUNIT_ASSERT_EQUAL(zeroVal(), Vec3i(0,0,0)); + CPPUNIT_ASSERT_EQUAL(zeroVal(), Vec3s(0,0,0)); + CPPUNIT_ASSERT_EQUAL(zeroVal(), Vec3d(0,0,0)); + CPPUNIT_ASSERT_EQUAL(zeroVal(), Quats::zero()); + CPPUNIT_ASSERT_EQUAL(zeroVal(), Quatd::zero()); + CPPUNIT_ASSERT_EQUAL(zeroVal(), Mat3s::zero()); + CPPUNIT_ASSERT_EQUAL(zeroVal(), Mat3d::zero()); + CPPUNIT_ASSERT_EQUAL(zeroVal(), Mat4s::zero()); + CPPUNIT_ASSERT_EQUAL(zeroVal(), Mat4d::zero()); + } +} + + +void +TestMath::testRandomInt() +{ + using openvdb::math::RandomInt; + + int imin = -3, imax = 11; + RandomInt rnd(/*seed=*/42, imin, imax); + + // Generate a sequence of random integers and verify that they all fall + // in the interval [imin, imax]. + std::vector seq(100); + for (int i = 0; i < 100; ++i) { + seq[i] = rnd(); + CPPUNIT_ASSERT(seq[i] >= imin); + CPPUNIT_ASSERT(seq[i] <= imax); + } + + // Verify that generators with the same seed produce the same sequence. + rnd = RandomInt(42, imin, imax); + for (int i = 0; i < 100; ++i) { + int r = rnd(); + CPPUNIT_ASSERT_EQUAL(seq[i], r); + } + + // Verify that generators with different seeds produce different sequences. + rnd = RandomInt(101, imin, imax); + std::vector newSeq(100); + for (int i = 0; i < 100; ++i) newSeq[i] = rnd(); + CPPUNIT_ASSERT(newSeq != seq); + + // Temporarily change the range. + imin = -5; imax = 6; + for (int i = 0; i < 100; ++i) { + int r = rnd(imin, imax); + CPPUNIT_ASSERT(r >= imin); + CPPUNIT_ASSERT(r <= imax); + } + // Verify that the range change was temporary. + imin = -3; imax = 11; + for (int i = 0; i < 100; ++i) { + int r = rnd(); + CPPUNIT_ASSERT(r >= imin); + CPPUNIT_ASSERT(r <= imax); + } + + // Permanently change the range. + imin = -5; imax = 6; + rnd.setRange(imin, imax); + for (int i = 0; i < 100; ++i) { + int r = rnd(); + CPPUNIT_ASSERT(r >= imin); + CPPUNIT_ASSERT(r <= imax); + } + + // Verify that it is OK to specify imin > imax (they are automatically swapped). + imin = 5; imax = -6; + rnd.setRange(imin, imax); + + rnd = RandomInt(42, imin, imax); +} + + +void +TestMath::testRandom01() +{ + using openvdb::math::Random01; + using openvdb::math::isApproxEqual; + + Random01 rnd(/*seed=*/42); + + // Generate a sequence of random numbers and verify that they all fall + // in the interval [0, 1). + std::vector seq(100); + for (int i = 0; i < 100; ++i) { + seq[i] = rnd(); + CPPUNIT_ASSERT(seq[i] >= 0.0); + CPPUNIT_ASSERT(seq[i] < 1.0); + } + + // Verify that generators with the same seed produce the same sequence. + rnd = Random01(42); + for (int i = 0; i < 100; ++i) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(seq[i], rnd(), /*tolerance=*/1.0e-6); + } + + // Verify that generators with different seeds produce different sequences. + rnd = Random01(101); + bool allEqual = true; + for (int i = 0; allEqual && i < 100; ++i) { + if (!isApproxEqual(rnd(), seq[i])) allEqual = false; + } + CPPUNIT_ASSERT(!allEqual); +} + +void +TestMath::testMinMaxIndex() +{ + const openvdb::Vec3R a(-1, 2, 0); + CPPUNIT_ASSERT_EQUAL(size_t(0), openvdb::math::MinIndex(a)); + CPPUNIT_ASSERT_EQUAL(size_t(1), openvdb::math::MaxIndex(a)); + const openvdb::Vec3R b(-1, -2, 0); + CPPUNIT_ASSERT_EQUAL(size_t(1), openvdb::math::MinIndex(b)); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MaxIndex(b)); + const openvdb::Vec3R c(5, 2, 1); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MinIndex(c)); + CPPUNIT_ASSERT_EQUAL(size_t(0), openvdb::math::MaxIndex(c)); + const openvdb::Vec3R d(0, 0, 1); + CPPUNIT_ASSERT_EQUAL(size_t(1), openvdb::math::MinIndex(d)); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MaxIndex(d)); + const openvdb::Vec3R e(1, 0, 0); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MinIndex(e)); + CPPUNIT_ASSERT_EQUAL(size_t(0), openvdb::math::MaxIndex(e)); + const openvdb::Vec3R f(0, 1, 0); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MinIndex(f)); + CPPUNIT_ASSERT_EQUAL(size_t(1), openvdb::math::MaxIndex(f)); + const openvdb::Vec3R g(1, 1, 0); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MinIndex(g)); + CPPUNIT_ASSERT_EQUAL(size_t(1), openvdb::math::MaxIndex(g)); + const openvdb::Vec3R h(1, 0, 1); + CPPUNIT_ASSERT_EQUAL(size_t(1), openvdb::math::MinIndex(h)); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MaxIndex(h)); + const openvdb::Vec3R i(0, 1, 1); + CPPUNIT_ASSERT_EQUAL(size_t(0), openvdb::math::MinIndex(i)); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MaxIndex(i)); + const openvdb::Vec3R j(1, 1, 1); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MinIndex(j)); + CPPUNIT_ASSERT_EQUAL(size_t(2), openvdb::math::MaxIndex(j)); +} diff --git a/openvdb/unittest/TestMeanCurvature.cc b/openvdb/unittest/TestMeanCurvature.cc new file mode 100644 index 00000000..259d7cea --- /dev/null +++ b/openvdb/unittest/TestMeanCurvature.cc @@ -0,0 +1,810 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() +#include +#include + +class TestMeanCurvature: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestMeanCurvature); + CPPUNIT_TEST(testISMeanCurvature); // MeanCurvature in Index Space + CPPUNIT_TEST(testISMeanCurvatureStencil); + CPPUNIT_TEST(testWSMeanCurvature); // MeanCurvature in World Space + CPPUNIT_TEST(testWSMeanCurvatureStencil); + CPPUNIT_TEST(testMeanCurvatureTool); // MeanCurvature tool + CPPUNIT_TEST(testMeanCurvatureMaskedTool); // MeanCurvature tool + CPPUNIT_TEST(testCurvatureStencil); // CurvatureStencil + CPPUNIT_TEST(testIntersection); + + CPPUNIT_TEST_SUITE_END(); + + void testISMeanCurvature(); + void testISMeanCurvatureStencil(); + void testWSMeanCurvature(); + void testWSMeanCurvatureStencil(); + void testMeanCurvatureTool(); + void testMeanCurvatureMaskedTool(); + void testCurvatureStencil(); + void testIntersection(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMeanCurvature); + + +void +TestMeanCurvature::testISMeanCurvature() +{ + using namespace openvdb; + + typedef FloatGrid::ConstAccessor AccessorType; + + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + AccessorType inAccessor = grid->getConstAccessor(); + AccessorType::ValueType alpha, beta, meancurv, normGrad; + Coord xyz(35,30,30); + + // First test an empty grid + CPPUNIT_ASSERT(tree.empty()); + typedef math::ISMeanCurvature SecondOrder; + CPPUNIT_ASSERT(!SecondOrder::result(inAccessor, xyz, alpha, beta)); + + typedef math::ISMeanCurvature FourthOrder; + CPPUNIT_ASSERT(!FourthOrder::result(inAccessor, xyz, alpha, beta)); + + typedef math::ISMeanCurvature SixthOrder; + CPPUNIT_ASSERT(!SixthOrder::result(inAccessor, xyz, alpha, beta)); + + // Next test a level set sphere + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f ,30.0f, 40.0f); + const float radius=0.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + + SecondOrder::result(inAccessor, xyz, alpha, beta); + + meancurv = alpha/(2*math::Pow3(beta) ); + normGrad = alpha/(2*math::Pow2(beta) ); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + FourthOrder::result(inAccessor, xyz, alpha, beta); + + meancurv = alpha/(2*math::Pow3(beta) ); + normGrad = alpha/(2*math::Pow2(beta) ); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + SixthOrder::result(inAccessor, xyz, alpha, beta); + + meancurv = alpha/(2*math::Pow3(beta) ); + normGrad = alpha/(2*math::Pow2(beta) ); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + xyz.reset(35,10,40); + + SecondOrder::result(inAccessor, xyz, alpha, beta); + + meancurv = alpha/(2*math::Pow3(beta) ); + normGrad = alpha/(2*math::Pow2(beta) ); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, normGrad, 0.001); +} + + +void +TestMeanCurvature::testISMeanCurvatureStencil() +{ + using namespace openvdb; + + typedef FloatGrid::ConstAccessor AccessorType; + + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + math::SecondOrderDenseStencil dense_2nd(*grid); + math::FourthOrderDenseStencil dense_4th(*grid); + math::SixthOrderDenseStencil dense_6th(*grid); + AccessorType::ValueType alpha, beta; + Coord xyz(35,30,30); + dense_2nd.moveTo(xyz); + dense_4th.moveTo(xyz); + dense_6th.moveTo(xyz); + + // First test on an empty grid + CPPUNIT_ASSERT(tree.empty()); + + typedef math::ISMeanCurvature SecondOrder; + CPPUNIT_ASSERT(!SecondOrder::result(dense_2nd, alpha, beta)); + + typedef math::ISMeanCurvature FourthOrder; + CPPUNIT_ASSERT(!FourthOrder::result(dense_4th, alpha, beta)); + + typedef math::ISMeanCurvature SixthOrder; + CPPUNIT_ASSERT(!SixthOrder::result(dense_6th, alpha, beta)); + + // Next test on a level set sphere + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f ,30.0f, 40.0f); + const float radius=0.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + dense_2nd.moveTo(xyz); + dense_4th.moveTo(xyz); + dense_6th.moveTo(xyz); + + CPPUNIT_ASSERT(!tree.empty()); + + CPPUNIT_ASSERT(SecondOrder::result(dense_2nd, alpha, beta)); + + AccessorType::ValueType meancurv = alpha/(2*math::Pow3(beta) ); + AccessorType::ValueType normGrad = alpha/(2*math::Pow2(beta) ); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + CPPUNIT_ASSERT(FourthOrder::result(dense_4th, alpha, beta)); + + meancurv = alpha/(2*math::Pow3(beta) ); + normGrad = alpha/(2*math::Pow2(beta) ); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + CPPUNIT_ASSERT(SixthOrder::result(dense_6th, alpha, beta)); + + meancurv = alpha/(2*math::Pow3(beta) ); + normGrad = alpha/(2*math::Pow2(beta) ); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + xyz.reset(35,10,40); + dense_2nd.moveTo(xyz); + CPPUNIT_ASSERT(SecondOrder::result(dense_2nd, alpha, beta)); + + meancurv = alpha/(2*math::Pow3(beta) ); + normGrad = alpha/(2*math::Pow2(beta) ); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, normGrad, 0.001); +} + + +void +TestMeanCurvature::testWSMeanCurvature() +{ + using namespace openvdb; + using math::AffineMap; + using math::TranslationMap; + using math::UniformScaleMap; + + typedef FloatGrid::ConstAccessor AccessorType; + + {// Empty grid test + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + AccessorType inAccessor = grid->getConstAccessor(); + Coord xyz(35,30,30); + CPPUNIT_ASSERT(tree.empty()); + + AccessorType::ValueType meancurv; + AccessorType::ValueType normGrad; + + AffineMap affine; + meancurv = math::MeanCurvature::result( + affine, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + affine, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, meancurv, 0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, normGrad, 0.0); + + meancurv = math::MeanCurvature::result( + affine, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + affine, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, meancurv, 0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, normGrad, 0.0); + + UniformScaleMap uniform; + meancurv = math::MeanCurvature::result( + uniform, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + uniform, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, meancurv, 0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, normGrad, 0.0); + + xyz.reset(35,10,40); + + TranslationMap trans; + meancurv = math::MeanCurvature::result( + trans, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + trans, inAccessor, xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, meancurv, 0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, normGrad, 0.0); + } + + { // unit size voxel test + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f ,30.0f, 40.0f); + const float radius=0.0f; + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + Coord xyz(35,30,30); + + AccessorType inAccessor = grid->getConstAccessor(); + + AccessorType::ValueType meancurv; + AccessorType::ValueType normGrad; + + AffineMap affine; + meancurv = math::MeanCurvature::result( + affine, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + affine, inAccessor, xyz); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + meancurv = math::MeanCurvature::result( + affine, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + affine, inAccessor, xyz); + + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + UniformScaleMap uniform; + meancurv = math::MeanCurvature::result( + uniform, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + uniform, inAccessor, xyz); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + xyz.reset(35,10,40); + + TranslationMap trans; + meancurv = math::MeanCurvature::result( + trans, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + trans, inAccessor, xyz); + + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, normGrad, 0.001); + } + { // non-unit sized voxel + + double voxel_size = 0.5; + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(voxel_size)); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10.0f; + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + AccessorType inAccessor = grid->getConstAccessor(); + + AccessorType::ValueType meancurv; + AccessorType::ValueType normGrad; + + Coord xyz(20,16,20); + AffineMap affine(voxel_size*math::Mat3d::identity()); + meancurv = math::MeanCurvature::result( + affine, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + affine, inAccessor, xyz); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001); + meancurv = math::MeanCurvature::result( + affine, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + affine, inAccessor, xyz); + + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001); + + UniformScaleMap uniform(voxel_size); + meancurv = math::MeanCurvature::result( + uniform, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + uniform, inAccessor, xyz); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001); + + } + { // NON-UNIFORM SCALING AND ROTATION + + Vec3d voxel_sizes(0.25, 0.45, 0.75); + FloatGrid::Ptr grid = FloatGrid::create(); + math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes)); + // apply rotation + math::MapBase::Ptr rotated_map = base_map->preRotate(1.5, math::X_AXIS); + grid->setTransform(math::Transform::Ptr(new math::Transform(rotated_map))); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10.0f; + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + AccessorType inAccessor = grid->getConstAccessor(); + + AccessorType::ValueType meancurv; + AccessorType::ValueType normGrad; + + Coord xyz(20,16,20); + Vec3d location = grid->indexToWorld(xyz); + double dist = (center - location).length(); + AffineMap::ConstPtr affine = grid->transform().map(); + meancurv = math::MeanCurvature::result( + *affine, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + *affine, inAccessor, xyz); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, normGrad, 0.001); + meancurv = math::MeanCurvature::result( + *affine, inAccessor, xyz); + normGrad = math::MeanCurvature::normGrad( + *affine, inAccessor, xyz); + + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, normGrad, 0.001); + } +} + + +void +TestMeanCurvature::testWSMeanCurvatureStencil() +{ + using namespace openvdb; + using math::AffineMap; + using math::TranslationMap; + using math::UniformScaleMap; + + typedef FloatGrid::ConstAccessor AccessorType; + + {// empty grid test + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + Coord xyz(35,30,30); + + math::SecondOrderDenseStencil dense_2nd(*grid); + math::FourthOrderDenseStencil dense_4th(*grid); + math::SixthOrderDenseStencil dense_6th(*grid); + dense_2nd.moveTo(xyz); + dense_4th.moveTo(xyz); + dense_6th.moveTo(xyz); + + AccessorType::ValueType meancurv; + AccessorType::ValueType normGrad; + + AffineMap affine; + meancurv = math::MeanCurvature::result( + affine, dense_2nd); + normGrad = math::MeanCurvature::normGrad( + affine, dense_2nd); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, meancurv, 0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, normGrad, 0.00); + + meancurv = math::MeanCurvature::result( + affine, dense_4th); + normGrad = math::MeanCurvature::normGrad( + affine, dense_4th); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, meancurv, 0.00); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, normGrad, 0.00); + + UniformScaleMap uniform; + meancurv = math::MeanCurvature::result( + uniform, dense_6th); + normGrad = math::MeanCurvature::normGrad( + uniform, dense_6th); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, meancurv, 0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, normGrad, 0.0); + + xyz.reset(35,10,40); + dense_6th.moveTo(xyz); + + TranslationMap trans; + meancurv = math::MeanCurvature::result( + trans, dense_6th); + normGrad = math::MeanCurvature::normGrad( + trans, dense_6th); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, meancurv, 0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, normGrad, 0.0); + } + + { // unit-sized voxels + + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f);//i.e. (35,30,40) in index space + const float radius=0.0f; + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + Coord xyz(35,30,30); + math::SecondOrderDenseStencil dense_2nd(*grid); + math::FourthOrderDenseStencil dense_4th(*grid); + math::SixthOrderDenseStencil dense_6th(*grid); + dense_2nd.moveTo(xyz); + dense_4th.moveTo(xyz); + dense_6th.moveTo(xyz); + + AccessorType::ValueType meancurv; + AccessorType::ValueType normGrad; + + AffineMap affine; + meancurv = math::MeanCurvature::result( + affine, dense_2nd); + normGrad = math::MeanCurvature::normGrad( + affine, dense_2nd); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + meancurv = math::MeanCurvature::result( + affine, dense_4th); + normGrad = math::MeanCurvature::normGrad( + affine, dense_4th); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + UniformScaleMap uniform; + meancurv = math::MeanCurvature::result( + uniform, dense_6th); + normGrad = math::MeanCurvature::normGrad( + uniform, dense_6th); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001); + + xyz.reset(35,10,40); + dense_6th.moveTo(xyz); + + TranslationMap trans; + meancurv = math::MeanCurvature::result( + trans, dense_6th); + normGrad = math::MeanCurvature::normGrad( + trans, dense_6th); + + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, normGrad, 0.001); + } + { // non-unit sized voxel + + double voxel_size = 0.5; + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(voxel_size)); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10.0f; + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + + AccessorType::ValueType meancurv; + AccessorType::ValueType normGrad; + + Coord xyz(20,16,20); + math::SecondOrderDenseStencil dense_2nd(*grid); + math::FourthOrderDenseStencil dense_4th(*grid); + math::SixthOrderDenseStencil dense_6th(*grid); + dense_2nd.moveTo(xyz); + dense_4th.moveTo(xyz); + dense_6th.moveTo(xyz); + + AffineMap affine(voxel_size*math::Mat3d::identity()); + meancurv = math::MeanCurvature::result( + affine, dense_2nd); + normGrad = math::MeanCurvature::normGrad( + affine, dense_2nd); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001); + meancurv = math::MeanCurvature::result( + affine, dense_4th); + normGrad = math::MeanCurvature::normGrad( + affine, dense_4th); + + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001); + + UniformScaleMap uniform(voxel_size); + meancurv = math::MeanCurvature::result( + uniform, dense_6th); + normGrad = math::MeanCurvature::normGrad( + uniform, dense_6th); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001); + } + { // NON-UNIFORM SCALING AND ROTATION + + Vec3d voxel_sizes(0.25, 0.45, 0.75); + FloatGrid::Ptr grid = FloatGrid::create(); + math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes)); + // apply rotation + math::MapBase::Ptr rotated_map = base_map->preRotate(1.5, math::X_AXIS); + grid->setTransform(math::Transform::Ptr(new math::Transform(rotated_map))); + CPPUNIT_ASSERT(grid->empty()); + + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10.0f; + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + AccessorType::ValueType meancurv; + AccessorType::ValueType normGrad; + + Coord xyz(20,16,20); + math::SecondOrderDenseStencil dense_2nd(*grid); + math::FourthOrderDenseStencil dense_4th(*grid); + dense_2nd.moveTo(xyz); + dense_4th.moveTo(xyz); + + + Vec3d location = grid->indexToWorld(xyz); + double dist = (center - location).length(); + AffineMap::ConstPtr affine = grid->transform().map(); + meancurv = math::MeanCurvature::result( + *affine, dense_2nd); + normGrad = math::MeanCurvature::normGrad( + *affine, dense_2nd); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, normGrad, 0.001); + meancurv = math::MeanCurvature::result( + *affine, dense_4th); + normGrad = math::MeanCurvature::normGrad( + *affine, dense_4th); + + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, meancurv, 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, normGrad, 0.001); + } +} + + +void +TestMeanCurvature::testMeanCurvatureTool() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f);//i.e. (35,30,40) in index space + const float radius=0.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + FloatGrid::Ptr curv = tools::meanCurvature(*grid); + FloatGrid::ConstAccessor accessor = curv->getConstAccessor(); + + Coord xyz(35,30,30); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, accessor.getValue(xyz), 0.001); + + xyz.reset(35,10,40); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, accessor.getValue(xyz), 0.001); +} + + +void +TestMeanCurvature::testMeanCurvatureMaskedTool() +{ + using namespace openvdb; + + FloatGrid::Ptr grid = createGrid(/*background=*/5.0); + FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f);//i.e. (35,30,40) in index space + const float radius=0.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + + + const openvdb::CoordBBox maskbbox(openvdb::Coord(35, 30, 30), openvdb::Coord(41, 41, 41)); + BoolGrid::Ptr maskGrid = BoolGrid::create(false); + maskGrid->fill(maskbbox, true/*value*/, true/*activate*/); + + + FloatGrid::Ptr curv = tools::meanCurvature(*grid, *maskGrid); + FloatGrid::ConstAccessor accessor = curv->getConstAccessor(); + + // test inside + Coord xyz(35,30,30); + CPPUNIT_ASSERT(maskbbox.isInside(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, accessor.getValue(xyz), 0.001); + + // test outside + xyz.reset(35,10,40); + CPPUNIT_ASSERT(!maskbbox.isInside(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, accessor.getValue(xyz), 0.001); +} + + +void +TestMeanCurvature::testCurvatureStencil() +{ + using namespace openvdb; + + {// test of level set to sphere at (6,8,10) with R=10 and dx=0.5 + + FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0); + grid->setTransform(math::Transform::createLinearTransform(/*voxel size=*/0.5)); + CPPUNIT_ASSERT(grid->empty()); + math::CurvatureStencil cs(*grid); + Coord xyz(20,16,20);//i.e. 8 voxel or 4 world units away from the center + cs.moveTo(xyz); + + // First test on an empty grid + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, cs.meanCurvature(), 0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, cs.meanCurvatureNormGrad(), 0.0); + + // Next test on a level set sphere + const openvdb::Coord dim(32,32,32); + const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space + const float radius=10.0f; + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!grid->empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount())); + cs.moveTo(xyz); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, cs.meanCurvature(), 0.01);// 1/distance from center + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, cs.meanCurvatureNormGrad(), 0.01);// 1/distance from center + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/16.0, cs.gaussianCurvature(), 0.01);// 1/distance^2 from center + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/16.0, cs.gaussianCurvatureNormGrad(), 0.01);// 1/distance^2 from center + + float mean, gaussian; + cs.curvatures(mean, gaussian); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, mean, 0.01);// 1/distance from center + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/16.0, gaussian, 0.01);// 1/distance^2 from center + + auto principalCurvatures = cs.principalCurvatures(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, principalCurvatures.first, 0.01);// 1/distance from center + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, principalCurvatures.second, 0.01);// 1/distance from center + + xyz.reset(12,16,10);//i.e. 10 voxel or 5 world units away from the center + cs.moveTo(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/5.0, cs.meanCurvature(), 0.01);// 1/distance from center + CPPUNIT_ASSERT_DOUBLES_EQUAL( + 1.0/5.0, cs.meanCurvatureNormGrad(), 0.01);// 1/distance from center + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/25.0, cs.gaussianCurvature(), 0.01);// 1/distance^2 from center + CPPUNIT_ASSERT_DOUBLES_EQUAL( + 1.0/25.0, cs.gaussianCurvatureNormGrad(), 0.01);// 1/distance^2 from center + + principalCurvatures = cs.principalCurvatures(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/5.0, principalCurvatures.first, 0.01);// 1/distance from center + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/5.0, principalCurvatures.second, 0.01);// 1/distance from center + CPPUNIT_ASSERT_DOUBLES_EQUAL( + 1.0/5.0, principalCurvatures.first, 0.01);// 1/distance from center + CPPUNIT_ASSERT_DOUBLES_EQUAL( + 1.0/5.0, principalCurvatures.second, 0.01);// 1/distance from center + + cs.curvaturesNormGrad(mean, gaussian); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/5.0, mean, 0.01);// 1/distance from center + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/25.0, gaussian, 0.01);// 1/distance^2 from center + } + {// test sparse level set sphere + const double percentage = 0.1/100.0;//i.e. 0.1% + const int dim = 256; + + // sparse level set sphere + Vec3f C(0.35f, 0.35f, 0.35f); + Real r = 0.15, voxelSize = 1.0/(dim-1); + FloatGrid::Ptr sphere = tools::createLevelSetSphere(float(r), C, float(voxelSize)); + + math::CurvatureStencil cs(*sphere); + const Coord ijk = Coord::round(sphere->worldToIndex(Vec3d(0.35, 0.35, 0.35 + 0.15))); + const double radius = (sphere->indexToWorld(ijk)-Vec3d(0.35)).length(); + //std::cerr << "\rRadius = " << radius << std::endl; + //std::cerr << "Index coord =" << ijk << std::endl; + cs.moveTo(ijk); + + //std::cerr << "Mean curvature = " << cs.meanCurvature() << ", 1/r=" << 1.0/radius << std::endl; + //std::cerr << "Gaussian curvature = " << cs.gaussianCurvature() << ", 1/(r*r)=" << 1.0/(radius*radius) << std::endl; + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/radius, cs.meanCurvature(), percentage*1.0/radius); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/(radius*radius), cs.gaussianCurvature(), percentage*1.0/(radius*radius)); + float mean, gauss; + cs.curvatures(mean, gauss); + //std::cerr << "Mean curvature = " << mean << ", 1/r=" << 1.0/radius << std::endl; + //std::cerr << "Gaussian curvature = " << gauss << ", 1/(r*r)=" << 1.0/(radius*radius) << std::endl; + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/radius, mean, percentage*1.0/radius); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/(radius*radius), gauss, percentage*1.0/(radius*radius)); + } +} + +void +TestMeanCurvature::testIntersection() +{ + using namespace openvdb; + const Coord ijk(1,4,-9); + FloatGrid grid(0.0f); + auto acc = grid.getAccessor(); + math::GradStencil stencil(grid); + acc.setValue(ijk,-1.0f); + int cases = 0; + for (int mx=0; mx<2; ++mx) { + acc.setValue(ijk.offsetBy(-1,0,0), mx ? 1.0f : -1.0f); + for (int px=0; px<2; ++px) { + acc.setValue(ijk.offsetBy(1,0,0), px ? 1.0f : -1.0f); + for (int my=0; my<2; ++my) { + acc.setValue(ijk.offsetBy(0,-1,0), my ? 1.0f : -1.0f); + for (int py=0; py<2; ++py) { + acc.setValue(ijk.offsetBy(0,1,0), py ? 1.0f : -1.0f); + for (int mz=0; mz<2; ++mz) { + acc.setValue(ijk.offsetBy(0,0,-1), mz ? 1.0f : -1.0f); + for (int pz=0; pz<2; ++pz) { + acc.setValue(ijk.offsetBy(0,0,1), pz ? 1.0f : -1.0f); + ++cases; + CPPUNIT_ASSERT_EQUAL(7, int(grid.activeVoxelCount())); + stencil.moveTo(ijk); + const size_t count = mx + px + my + py + mz + pz;// number of intersections + CPPUNIT_ASSERT(stencil.intersects() == (count > 0)); + auto mask = stencil.intersectionMask(); + CPPUNIT_ASSERT(mask.none() == (count == 0)); + CPPUNIT_ASSERT(mask.any() == (count > 0)); + CPPUNIT_ASSERT_EQUAL(count, mask.count()); + CPPUNIT_ASSERT(mask.test(0) == mx); + CPPUNIT_ASSERT(mask.test(1) == px); + CPPUNIT_ASSERT(mask.test(2) == my); + CPPUNIT_ASSERT(mask.test(3) == py); + CPPUNIT_ASSERT(mask.test(4) == mz); + CPPUNIT_ASSERT(mask.test(5) == pz); + }//pz + }//mz + }//py + }//my + }//px + }//mx + CPPUNIT_ASSERT_EQUAL(64, cases);// = 2^6 +}//testIntersection \ No newline at end of file diff --git a/openvdb/unittest/TestMeshToVolume.cc b/openvdb/unittest/TestMeshToVolume.cc new file mode 100644 index 00000000..dad3dcf2 --- /dev/null +++ b/openvdb/unittest/TestMeshToVolume.cc @@ -0,0 +1,149 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + +#include +#include + +#include +#include + + +class TestMeshToVolume: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestMeshToVolume); + CPPUNIT_TEST(testUtils); + CPPUNIT_TEST(testConversion); + CPPUNIT_TEST(testCreateLevelSetBox); + CPPUNIT_TEST_SUITE_END(); + + void testUtils(); + void testConversion(); + void testCreateLevelSetBox(); + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMeshToVolume); + + +//////////////////////////////////////// + + +void +TestMeshToVolume::testUtils() +{ + /// Test nearestCoord + openvdb::Vec3d xyz(0.7, 2.2, -2.7); + openvdb::Coord ijk = openvdb::util::nearestCoord(xyz); + CPPUNIT_ASSERT(ijk[0] == 0 && ijk[1] == 2 && ijk[2] == -3); + + xyz = openvdb::Vec3d(-22.1, 4.6, 202.34); + ijk = openvdb::util::nearestCoord(xyz); + CPPUNIT_ASSERT(ijk[0] == -23 && ijk[1] == 4 && ijk[2] == 202); + + /// Test the coordinate offset table for neghbouring voxels + openvdb::Coord sum(0, 0, 0); + + unsigned int pX = 0, pY = 0, pZ = 0, mX = 0, mY = 0, mZ = 0; + + for (unsigned int i = 0; i < 26; ++i) { + ijk = openvdb::util::COORD_OFFSETS[i]; + sum += ijk; + + if (ijk[0] == 1) ++pX; + else if (ijk[0] == -1) ++mX; + + if (ijk[1] == 1) ++pY; + else if (ijk[1] == -1) ++mY; + + if (ijk[2] == 1) ++pZ; + else if (ijk[2] == -1) ++mZ; + } + + CPPUNIT_ASSERT(sum == openvdb::Coord(0, 0, 0)); + + CPPUNIT_ASSERT( pX == 9); + CPPUNIT_ASSERT( pY == 9); + CPPUNIT_ASSERT( pZ == 9); + CPPUNIT_ASSERT( mX == 9); + CPPUNIT_ASSERT( mY == 9); + CPPUNIT_ASSERT( mZ == 9); +} + +void +TestMeshToVolume::testConversion() +{ + using namespace openvdb; + + std::vector points; + std::vector quads; + + // cube vertices + points.push_back(Vec3s(2, 2, 2)); // 0 6--------7 + points.push_back(Vec3s(5, 2, 2)); // 1 /| /| + points.push_back(Vec3s(2, 5, 2)); // 2 2--------3 | + points.push_back(Vec3s(5, 5, 2)); // 3 | | | | + points.push_back(Vec3s(2, 2, 5)); // 4 | 4------|-5 + points.push_back(Vec3s(5, 2, 5)); // 5 |/ |/ + points.push_back(Vec3s(2, 5, 5)); // 6 0--------1 + points.push_back(Vec3s(5, 5, 5)); // 7 + + // cube faces + quads.push_back(Vec4I(0, 1, 3, 2)); // front + quads.push_back(Vec4I(5, 4, 6, 7)); // back + quads.push_back(Vec4I(0, 2, 6, 4)); // left + quads.push_back(Vec4I(1, 5, 7, 3)); // right + quads.push_back(Vec4I(2, 3, 7, 6)); // top + quads.push_back(Vec4I(0, 4, 5, 1)); // bottom + + math::Transform::Ptr xform = math::Transform::createLinearTransform(); + + tools::QuadAndTriangleDataAdapter mesh(points, quads); + + FloatGrid::Ptr grid = tools::meshToVolume(mesh, *xform); + + CPPUNIT_ASSERT(grid.get() != NULL); + CPPUNIT_ASSERT_EQUAL(int(GRID_LEVEL_SET), int(grid->getGridClass())); + CPPUNIT_ASSERT_EQUAL(1, int(grid->baseTree().leafCount())); + + grid = tools::meshToLevelSet(*xform, points, quads); + + CPPUNIT_ASSERT(grid.get() != NULL); + CPPUNIT_ASSERT_EQUAL(int(GRID_LEVEL_SET), int(grid->getGridClass())); + CPPUNIT_ASSERT_EQUAL(1, int(grid->baseTree().leafCount())); +} + + +void +TestMeshToVolume::testCreateLevelSetBox() +{ + typedef openvdb::FloatGrid FloatGrid; + typedef openvdb::Vec3s Vec3s; + typedef openvdb::math::BBox BBoxs; + typedef openvdb::math::Transform Transform; + + BBoxs bbox(Vec3s(0.0, 0.0, 0.0), Vec3s(1.0, 1.0, 1.0)); + + Transform::Ptr transform = Transform::createLinearTransform(0.1); + + FloatGrid::Ptr grid = openvdb::tools::createLevelSetBox(bbox, *transform); + + double gridBackground = grid->background(); + double expectedBackground = transform->voxelSize().x() * double(openvdb::LEVEL_SET_HALF_WIDTH); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expectedBackground, gridBackground, 1e-6); + + CPPUNIT_ASSERT(grid->tree().leafCount() > 0); + + // test inside coord value + openvdb::Coord ijk = transform->worldToIndexNodeCentered(openvdb::Vec3d(0.5, 0.5, 0.5)); + CPPUNIT_ASSERT(grid->tree().getValue(ijk) < 0.0f); + + // test outside coord value + ijk = transform->worldToIndexNodeCentered(openvdb::Vec3d(1.5, 1.5, 1.5)); + CPPUNIT_ASSERT(grid->tree().getValue(ijk) > 0.0f); +} + diff --git a/openvdb/unittest/TestMetaMap.cc b/openvdb/unittest/TestMetaMap.cc new file mode 100644 index 00000000..60f1bc65 --- /dev/null +++ b/openvdb/unittest/TestMetaMap.cc @@ -0,0 +1,373 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +class TestMetaMap: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestMetaMap); + CPPUNIT_TEST(testInsert); + CPPUNIT_TEST(testRemove); + CPPUNIT_TEST(testGetMetadata); + CPPUNIT_TEST(testIO); + CPPUNIT_TEST(testEmptyIO); + CPPUNIT_TEST(testCopyConstructor); + CPPUNIT_TEST(testCopyConstructorEmpty); + CPPUNIT_TEST(testAssignment); + CPPUNIT_TEST(testEquality); + CPPUNIT_TEST_SUITE_END(); + + void testInsert(); + void testRemove(); + void testGetMetadata(); + void testIO(); + void testEmptyIO(); + void testCopyConstructor(); + void testCopyConstructorEmpty(); + void testAssignment(); + void testEquality(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetaMap); + +void +TestMetaMap::testInsert() +{ + using namespace openvdb; + + MetaMap meta; + meta.insertMeta("meta1", StringMetadata("testing")); + meta.insertMeta("meta2", Int32Metadata(20)); + meta.insertMeta("meta3", FloatMetadata(2.0)); + + MetaMap::MetaIterator iter = meta.beginMeta(); + int i = 1; + for( ; iter != meta.endMeta(); ++iter, ++i) { + if(i == 1) { + CPPUNIT_ASSERT(iter->first.compare("meta1") == 0); + std::string val = meta.metaValue("meta1"); + CPPUNIT_ASSERT(val == "testing"); + } else if(i == 2) { + CPPUNIT_ASSERT(iter->first.compare("meta2") == 0); + int32_t val = meta.metaValue("meta2"); + CPPUNIT_ASSERT(val == 20); + } else if(i == 3) { + CPPUNIT_ASSERT(iter->first.compare("meta3") == 0); + float val = meta.metaValue("meta3"); + //CPPUNIT_ASSERT(val == 2.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0f,val,0); + } + } +} + +void +TestMetaMap::testRemove() +{ + using namespace openvdb; + + MetaMap meta; + meta.insertMeta("meta1", StringMetadata("testing")); + meta.insertMeta("meta2", Int32Metadata(20)); + meta.insertMeta("meta3", FloatMetadata(2.0)); + + meta.removeMeta("meta2"); + + MetaMap::MetaIterator iter = meta.beginMeta(); + int i = 1; + for( ; iter != meta.endMeta(); ++iter, ++i) { + if(i == 1) { + CPPUNIT_ASSERT(iter->first.compare("meta1") == 0); + std::string val = meta.metaValue("meta1"); + CPPUNIT_ASSERT(val == "testing"); + } else if(i == 2) { + CPPUNIT_ASSERT(iter->first.compare("meta3") == 0); + float val = meta.metaValue("meta3"); + //CPPUNIT_ASSERT(val == 2.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0f,val,0); + } + } + + meta.removeMeta("meta1"); + + iter = meta.beginMeta(); + for( ; iter != meta.endMeta(); ++iter, ++i) { + CPPUNIT_ASSERT(iter->first.compare("meta3") == 0); + float val = meta.metaValue("meta3"); + //CPPUNIT_ASSERT(val == 2.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0f,val,0); + } + + meta.removeMeta("meta3"); + + CPPUNIT_ASSERT_EQUAL(0, int(meta.metaCount())); +} + +void +TestMetaMap::testGetMetadata() +{ + using namespace openvdb; + + MetaMap meta; + meta.insertMeta("meta1", StringMetadata("testing")); + meta.insertMeta("meta2", Int32Metadata(20)); + meta.insertMeta("meta3", DoubleMetadata(2.0)); + + Metadata::Ptr metadata = meta["meta2"]; + CPPUNIT_ASSERT(metadata); + CPPUNIT_ASSERT(metadata->typeName().compare("int32") == 0); + + DoubleMetadata::Ptr dm = meta.getMetadata("meta3"); + //CPPUNIT_ASSERT(dm->value() == 2.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0,dm->value(),0); + + const DoubleMetadata::Ptr cdm = meta.getMetadata("meta3"); + //CPPUNIT_ASSERT(dm->value() == 2.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0,cdm->value(),0); + + CPPUNIT_ASSERT(!meta.getMetadata("meta2")); + + CPPUNIT_ASSERT_THROW(meta.metaValue("meta3"), + openvdb::TypeError); + + CPPUNIT_ASSERT_THROW(meta.metaValue("meta5"), + openvdb::LookupError); +} + +void +TestMetaMap::testIO() +{ + using namespace openvdb; + + logging::LevelScope suppressLogging{logging::Level::Fatal}; + + Metadata::clearRegistry(); + + // Write some metadata using unregistered types. + MetaMap meta; + meta.insertMeta("meta1", StringMetadata("testing")); + meta.insertMeta("meta2", Int32Metadata(20)); + meta.insertMeta("meta3", DoubleMetadata(2.0)); + std::ostringstream ostr(std::ios_base::binary); + meta.writeMeta(ostr); + + // Verify that reading metadata of unregistered types is possible, + // though the values cannot be retrieved. + MetaMap meta2; + std::istringstream istr(ostr.str(), std::ios_base::binary); + CPPUNIT_ASSERT_NO_THROW(meta2.readMeta(istr)); +#if OPENVDB_ABI_VERSION_NUMBER < 5 + CPPUNIT_ASSERT_EQUAL(0, int(meta2.metaCount())); +#else + CPPUNIT_ASSERT_EQUAL(3, int(meta2.metaCount())); + + // Verify that writing metadata of unknown type (i.e., UnknownMetadata) is possible. + std::ostringstream ostrUnknown(std::ios_base::binary); + meta2.writeMeta(ostrUnknown); +#endif + + // Register just one of the three types, then reread and verify that + // the value of the registered type can be retrieved. + Int32Metadata::registerType(); + istr.seekg(0, std::ios_base::beg); + CPPUNIT_ASSERT_NO_THROW(meta2.readMeta(istr)); +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + CPPUNIT_ASSERT_EQUAL(3, int(meta2.metaCount())); +#else + CPPUNIT_ASSERT_EQUAL(1, int(meta2.metaCount())); +#endif + CPPUNIT_ASSERT_EQUAL(meta.metaValue("meta2"), meta2.metaValue("meta2")); + + // Register the remaining types. + StringMetadata::registerType(); + DoubleMetadata::registerType(); + + { + // Now seek to beginning and read again. + istr.seekg(0, std::ios_base::beg); + meta2.clearMetadata(); + + CPPUNIT_ASSERT_NO_THROW(meta2.readMeta(istr)); + CPPUNIT_ASSERT_EQUAL(meta.metaCount(), meta2.metaCount()); + + std::string val = meta.metaValue("meta1"); + std::string val2 = meta2.metaValue("meta1"); + CPPUNIT_ASSERT_EQUAL(0, val.compare(val2)); + + int intval = meta.metaValue("meta2"); + int intval2 = meta2.metaValue("meta2"); + CPPUNIT_ASSERT_EQUAL(intval, intval2); + + double dval = meta.metaValue("meta3"); + double dval2 = meta2.metaValue("meta3"); + CPPUNIT_ASSERT_DOUBLES_EQUAL(dval, dval2,0); + } + { +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + // Verify that metadata that was written as UnknownMetadata can + // be read as typed metadata once the underlying types are registered. + std::istringstream istrUnknown(ostrUnknown.str(), std::ios_base::binary); + + meta2.clearMetadata(); + CPPUNIT_ASSERT_NO_THROW(meta2.readMeta(istrUnknown)); + + CPPUNIT_ASSERT_EQUAL(meta.metaCount(), meta2.metaCount()); + CPPUNIT_ASSERT_EQUAL( + meta.metaValue("meta1"), meta2.metaValue("meta1")); + CPPUNIT_ASSERT_EQUAL(meta.metaValue("meta2"), meta2.metaValue("meta2")); + CPPUNIT_ASSERT_DOUBLES_EQUAL( + meta.metaValue("meta3"), meta2.metaValue("meta3"), 0.0); +#endif + } + + // Clear the registry once the test is done. + Metadata::clearRegistry(); +} + +void +TestMetaMap::testEmptyIO() +{ + using namespace openvdb; + + MetaMap meta; + + // Write out an empty metadata + std::ostringstream ostr(std::ios_base::binary); + + // Read in the metadata; + MetaMap meta2; + std::istringstream istr(ostr.str(), std::ios_base::binary); + CPPUNIT_ASSERT_NO_THROW(meta2.readMeta(istr)); + + CPPUNIT_ASSERT(meta2.metaCount() == 0); +} + +void +TestMetaMap::testCopyConstructor() +{ + using namespace openvdb; + + MetaMap meta; + meta.insertMeta("meta1", StringMetadata("testing")); + meta.insertMeta("meta2", Int32Metadata(20)); + meta.insertMeta("meta3", FloatMetadata(2.0)); + + // copy constructor + MetaMap meta2(meta); + + CPPUNIT_ASSERT(meta.metaCount() == meta2.metaCount()); + + std::string str = meta.metaValue("meta1"); + std::string str2 = meta2.metaValue("meta1"); + CPPUNIT_ASSERT(str == str2); + + CPPUNIT_ASSERT(meta.metaValue("meta2") == + meta2.metaValue("meta2")); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(meta.metaValue("meta3"), + meta2.metaValue("meta3"),0); + //CPPUNIT_ASSERT(meta.metaValue("meta3") == + // meta2.metaValue("meta3")); +} + + +void +TestMetaMap::testCopyConstructorEmpty() +{ + using namespace openvdb; + + MetaMap meta; + + MetaMap meta2(meta); + + CPPUNIT_ASSERT(meta.metaCount() == 0); + CPPUNIT_ASSERT(meta2.metaCount() == meta.metaCount()); +} + + +void +TestMetaMap::testAssignment() +{ + using namespace openvdb; + + // Populate a map with data. + MetaMap meta; + meta.insertMeta("meta1", StringMetadata("testing")); + meta.insertMeta("meta2", Int32Metadata(20)); + meta.insertMeta("meta3", FloatMetadata(2.0)); + + // Create an empty map. + MetaMap meta2; + CPPUNIT_ASSERT_EQUAL(0, int(meta2.metaCount())); + + // Copy the first map to the second. + meta2 = meta; + CPPUNIT_ASSERT_EQUAL(meta.metaCount(), meta2.metaCount()); + + // Verify that the contents of the two maps are the same. + CPPUNIT_ASSERT_EQUAL( + meta.metaValue("meta1"), meta2.metaValue("meta1")); + CPPUNIT_ASSERT_EQUAL(meta.metaValue("meta2"), meta2.metaValue("meta2")); + CPPUNIT_ASSERT_DOUBLES_EQUAL( + meta.metaValue("meta3"), meta2.metaValue("meta3"), /*tolerance=*/0); + + // Verify that changing one map doesn't affect the other. + meta.insertMeta("meta1", StringMetadata("changed")); + std::string str = meta.metaValue("meta1"); + CPPUNIT_ASSERT_EQUAL(std::string("testing"), meta2.metaValue("meta1")); +} + + +void +TestMetaMap::testEquality() +{ + using namespace openvdb; + + // Populate a map with data. + MetaMap meta; + meta.insertMeta("meta1", StringMetadata("testing")); + meta.insertMeta("meta2", Int32Metadata(20)); + meta.insertMeta("meta3", FloatMetadata(3.14159f)); + + // Create an empty map. + MetaMap meta2; + + // Verify that the two maps differ. + CPPUNIT_ASSERT(meta != meta2); + CPPUNIT_ASSERT(meta2 != meta); + + // Copy the first map to the second. + meta2 = meta; + + // Verify that the two maps are equivalent. + CPPUNIT_ASSERT(meta == meta2); + CPPUNIT_ASSERT(meta2 == meta); + + // Modify the first map. + meta.removeMeta("meta1"); + meta.insertMeta("abc", DoubleMetadata(2.0)); + + // Verify that the two maps differ. + CPPUNIT_ASSERT(meta != meta2); + CPPUNIT_ASSERT(meta2 != meta); + + // Modify the second map and verify that the two maps differ. + meta2 = meta; + meta2.insertMeta("meta2", Int32Metadata(42)); + CPPUNIT_ASSERT(meta != meta2); + CPPUNIT_ASSERT(meta2 != meta); + + meta2 = meta; + meta2.insertMeta("meta3", FloatMetadata(2.0001f)); + CPPUNIT_ASSERT(meta != meta2); + CPPUNIT_ASSERT(meta2 != meta); + + meta2 = meta; + meta2.insertMeta("abc", DoubleMetadata(2.0001)); + CPPUNIT_ASSERT(meta != meta2); + CPPUNIT_ASSERT(meta2 != meta); +} diff --git a/openvdb/unittest/TestMetadata.cc b/openvdb/unittest/TestMetadata.cc new file mode 100644 index 00000000..3a180772 --- /dev/null +++ b/openvdb/unittest/TestMetadata.cc @@ -0,0 +1,163 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + + +class TestMetadata: public CppUnit::TestCase +{ +public: + void setUp() override { openvdb::Metadata::clearRegistry(); } + void tearDown() override { openvdb::Metadata::clearRegistry(); } + + CPPUNIT_TEST_SUITE(TestMetadata); + CPPUNIT_TEST(testMetadataRegistry); + CPPUNIT_TEST(testMetadataAsBool); + CPPUNIT_TEST(testCustomMetadata); + CPPUNIT_TEST_SUITE_END(); + + void testMetadataRegistry(); + void testMetadataAsBool(); + void testCustomMetadata(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetadata); + + +void +TestMetadata::testMetadataRegistry() +{ + using namespace openvdb; + + Int32Metadata::registerType(); + + StringMetadata strMetadata; + + CPPUNIT_ASSERT(!Metadata::isRegisteredType(strMetadata.typeName())); + + StringMetadata::registerType(); + + CPPUNIT_ASSERT(Metadata::isRegisteredType(strMetadata.typeName())); + CPPUNIT_ASSERT(Metadata::isRegisteredType(Int32Metadata::staticTypeName())); + + Metadata::Ptr stringMetadata = Metadata::createMetadata(strMetadata.typeName()); + + CPPUNIT_ASSERT(stringMetadata->typeName() == strMetadata.typeName()); + + StringMetadata::unregisterType(); + + CPPUNIT_ASSERT_THROW(Metadata::createMetadata(strMetadata.typeName()), openvdb::LookupError); +} + +void +TestMetadata::testMetadataAsBool() +{ + using namespace openvdb; + + { + FloatMetadata meta(0.0); + CPPUNIT_ASSERT(!meta.asBool()); + meta.setValue(1.0); + CPPUNIT_ASSERT(meta.asBool()); + meta.setValue(-1.0); + CPPUNIT_ASSERT(meta.asBool()); + meta.setValue(999.0); + CPPUNIT_ASSERT(meta.asBool()); + } + { + Int32Metadata meta(0); + CPPUNIT_ASSERT(!meta.asBool()); + meta.setValue(1); + CPPUNIT_ASSERT(meta.asBool()); + meta.setValue(-1); + CPPUNIT_ASSERT(meta.asBool()); + meta.setValue(999); + CPPUNIT_ASSERT(meta.asBool()); + } + { + StringMetadata meta(""); + CPPUNIT_ASSERT(!meta.asBool()); + meta.setValue("abc"); + CPPUNIT_ASSERT(meta.asBool()); + } + { + Vec3IMetadata meta(Vec3i(0)); + CPPUNIT_ASSERT(!meta.asBool()); + meta.setValue(Vec3i(-1, 0, 1)); + CPPUNIT_ASSERT(meta.asBool()); + } + { + Vec3SMetadata meta(Vec3s(0.0)); + CPPUNIT_ASSERT(!meta.asBool()); + meta.setValue(Vec3s(-1.0, 0.0, 1.0)); + CPPUNIT_ASSERT(meta.asBool()); + } + { + Vec4DMetadata meta(Vec4d(0.0)); + CPPUNIT_ASSERT(!meta.asBool()); + meta.setValue(Vec4d(1.0)); + CPPUNIT_ASSERT(meta.asBool()); + } +} + + +void +TestMetadata::testCustomMetadata() +{ + using namespace openvdb; + + const Vec3i expected(1, 2, 3); + std::ostringstream ostr(std::ios_base::binary); + { + Vec3IMetadata::registerType(); + Vec3IMetadata meta(expected); + + // Write Vec3I metadata to a byte string. + meta.write(ostr); + } + + // Unregister Vec3I metadata. + Metadata::clearRegistry(); + +#if OPENVDB_ABI_VERSION_NUMBER >= 5 + { + std::istringstream istr(ostr.str(), std::ios_base::binary); + + UnknownMetadata meta; + // Verify that metadata of an unregistered type can be read successfully. + CPPUNIT_ASSERT_NO_THROW(meta.read(istr)); + + // Verify that the metadata matches the original vector value. + CPPUNIT_ASSERT_EQUAL(sizeof(Vec3i), size_t(meta.size())); + CPPUNIT_ASSERT(meta.value().size() == size_t(meta.size())); + CPPUNIT_ASSERT_EQUAL(expected, *reinterpret_cast(&meta.value()[0])); + + ostr.str(""); + meta.write(ostr); + + // Verify that UnknownMetadata can be copied. + auto metaPtr = meta.copy(); + CPPUNIT_ASSERT(metaPtr.get() != nullptr); + CPPUNIT_ASSERT(meta == *metaPtr); + + // Verify that typed metadata can be copied into UnknownMetadata. + meta.copy(Vec3IMetadata(expected)); + CPPUNIT_ASSERT_EQUAL(sizeof(expected), size_t(meta.size())); + const auto* ptr = reinterpret_cast(&expected); + CPPUNIT_ASSERT(UnknownMetadata::ByteVec(ptr, ptr + sizeof(expected)) == meta.value()); + } +#endif + + Vec3IMetadata::registerType(); + + { + std::istringstream istr(ostr.str(), std::ios_base::binary); + Vec3IMetadata meta; + meta.read(istr); + + CPPUNIT_ASSERT_EQUAL(expected, meta.value()); + } +} diff --git a/openvdb/unittest/TestMetadataIO.cc b/openvdb/unittest/TestMetadataIO.cc new file mode 100644 index 00000000..e18bcc58 --- /dev/null +++ b/openvdb/unittest/TestMetadataIO.cc @@ -0,0 +1,127 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +// CPPUNIT_TEST_SUITE() invokes CPPUNIT_TESTNAMER_DECL() to generate a suite name +// from the FixtureType. But if FixtureType is a templated type, the generated name +// can become long and messy. This macro overrides the normal naming logic, +// instead invoking FixtureType::testSuiteName(), which should be a static member +// function that returns a std::string containing the suite name for the specific +// template instantiation. +#undef CPPUNIT_TESTNAMER_DECL +#define CPPUNIT_TESTNAMER_DECL( variableName, FixtureType ) \ + CPPUNIT_NS::TestNamer variableName( FixtureType::testSuiteName() ) + +template +class TestMetadataIO: public CppUnit::TestCase +{ +public: + static std::string testSuiteName() + { + std::string name = openvdb::typeNameAsString(); + if (!name.empty()) name[0] = static_cast(::toupper(name[0])); + return "TestMetadataIO" + name; + } + + CPPUNIT_TEST_SUITE(TestMetadataIO); + CPPUNIT_TEST(test); + CPPUNIT_TEST(testMultiple); + CPPUNIT_TEST_SUITE_END(); + + void test(); + void testMultiple(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetadataIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetadataIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetadataIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetadataIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetadataIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetadataIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetadataIO); +CPPUNIT_TEST_SUITE_REGISTRATION(TestMetadataIO); + + +namespace { + +template struct Value { static T create(int i) { return T(i); } }; + +template<> struct Value { + static std::string create(int i) { return "test" + std::to_string(i); } +}; + +template struct Value> { + using ValueType = openvdb::math::Vec2; + static ValueType create(int i) { return ValueType(i, i+1); } +}; +template struct Value> { + using ValueType = openvdb::math::Vec3; + static ValueType create(int i) { return ValueType(i, i+1, i+2); } +}; +template struct Value> { + using ValueType = openvdb::math::Vec4; + static ValueType create(int i) { return ValueType(i, i+1, i+2, i+3); } +}; + +} + + +template +void +TestMetadataIO::test() +{ + using namespace openvdb; + + const T val = Value::create(1); + TypedMetadata m(val); + + std::ostringstream ostr(std::ios_base::binary); + + m.write(ostr); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + + TypedMetadata tm; + tm.read(istr); + + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + + CPPUNIT_ASSERT_EQUAL(val, tm.value()); + + OPENVDB_NO_FP_EQUALITY_WARNING_END +} + + +template +void +TestMetadataIO::testMultiple() +{ + using namespace openvdb; + + const T val1 = Value::create(1), val2 = Value::create(2); + TypedMetadata m1(val1); + TypedMetadata m2(val2); + + std::ostringstream ostr(std::ios_base::binary); + + m1.write(ostr); + m2.write(ostr); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + + TypedMetadata tm1, tm2; + tm1.read(istr); + tm2.read(istr); + + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + + CPPUNIT_ASSERT_EQUAL(val1, tm1.value()); + CPPUNIT_ASSERT_EQUAL(val2, tm2.value()); + + OPENVDB_NO_FP_EQUALITY_WARNING_END +} diff --git a/openvdb/unittest/TestMultiResGrid.cc b/openvdb/unittest/TestMultiResGrid.cc new file mode 100644 index 00000000..6ff952b5 --- /dev/null +++ b/openvdb/unittest/TestMultiResGrid.cc @@ -0,0 +1,341 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include // for remove() + +class TestMultiResGrid : public CppUnit::TestCase +{ + // Use to test logic in openvdb::tools::MultiResGrid + struct CoordMask { + static int Mask(int i, int j, int k) { return (i & 1) | ((j & 1) << 1) | ((k & 1) << 2); } + CoordMask() : mask(0) {} + CoordMask(const openvdb::Coord &c ) : mask( Mask(c[0],c[1],c[2]) ) {} + inline void setCoord(int i, int j, int k) { mask = Mask(i,j,k); } + inline void setCoord(const openvdb::Coord &c) { mask = Mask(c[0],c[1],c[2]); } + inline bool allEven() const { return mask == 0; } + inline bool xOdd() const { return mask == 1; } + inline bool yOdd() const { return mask == 2; } + inline bool zOdd() const { return mask == 4; } + inline bool xyOdd() const { return mask == 3; } + inline bool xzOdd() const { return mask == 5; } + inline bool yzOdd() const { return mask == 6; } + inline bool allOdd() const { return mask == 7; } + int mask; + };// CoordMask + +public: + CPPUNIT_TEST_SUITE(TestMultiResGrid); + CPPUNIT_TEST(testTwosComplement); + CPPUNIT_TEST(testCoordMask); + CPPUNIT_TEST(testManualTopology); + CPPUNIT_TEST(testIO); + CPPUNIT_TEST(testModels); + CPPUNIT_TEST_SUITE_END(); + + void testTwosComplement(); + void testCoordMask(); + void testManualTopology(); + void testIO(); + void testModels(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMultiResGrid); + +// Uncomment to test on models from our web-site +//#define TestMultiResGrid_DATA_PATH "/home/kmu/src/openvdb/data/" +//#define TestMultiResGrid_DATA_PATH "/usr/pic1/Data/OpenVDB/LevelSetModels/" + +void +TestMultiResGrid::testTwosComplement() +{ + // test bit-operations that assume 2's complement representation of negative integers + CPPUNIT_ASSERT_EQUAL( 1, 13 & 1 );// odd + CPPUNIT_ASSERT_EQUAL( 1,-13 & 1 );// odd + CPPUNIT_ASSERT_EQUAL( 0, 12 & 1 );// even + CPPUNIT_ASSERT_EQUAL( 0,-12 & 1 );// even + CPPUNIT_ASSERT_EQUAL( 0, 0 & 1 );// even + for (int i=-50; i<=50; ++i) { + if ( (i % 2) == 0 ) {//i.e. even number + CPPUNIT_ASSERT_EQUAL( 0, i & 1); + CPPUNIT_ASSERT_EQUAL( i, (i >> 1) << 1 ); + } else {//i.e. odd number + CPPUNIT_ASSERT_EQUAL( 1, i & 1); + CPPUNIT_ASSERT( i != (i >> 1) << 1 ); + } + } +} + + +void +TestMultiResGrid::testCoordMask() +{ + using namespace openvdb; + CoordMask mask; + + mask.setCoord(-4, 2, 18); + CPPUNIT_ASSERT(mask.allEven()); + + mask.setCoord(1, 2, -6); + CPPUNIT_ASSERT(mask.xOdd()); + + mask.setCoord(4, -3, -6); + CPPUNIT_ASSERT(mask.yOdd()); + + mask.setCoord(-8, 2, -7); + CPPUNIT_ASSERT(mask.zOdd()); + + mask.setCoord(1, -3, 2); + CPPUNIT_ASSERT(mask.xyOdd()); + + mask.setCoord(1, 2, -7); + CPPUNIT_ASSERT(mask.xzOdd()); + + mask.setCoord(-10, 3, -3); + CPPUNIT_ASSERT(mask.yzOdd()); + + mask.setCoord(1, 3,-3); + CPPUNIT_ASSERT(mask.allOdd()); +} + +void +TestMultiResGrid::testManualTopology() +{ + // Perform tests when the sparsity (or topology) of the multiple grids is defined manually + using namespace openvdb; + + typedef tools::MultiResGrid MultiResGridT; + const double background = -1.0; + const size_t levels = 4; + + MultiResGridT::Ptr mrg(new MultiResGridT( levels, background)); + + CPPUNIT_ASSERT(mrg != nullptr); + CPPUNIT_ASSERT_EQUAL(levels , mrg->numLevels()); + CPPUNIT_ASSERT_EQUAL(size_t(0), mrg->finestLevel()); + CPPUNIT_ASSERT_EQUAL(levels-1, mrg->coarsestLevel()); + + // Define grid domain so they exactly overlap! + const int w = 16;//half-width of dense patch on the finest grid level + const CoordBBox bbox( Coord(-w), Coord(w) );// both inclusive + + // First check all trees against the background value + for (size_t level = 0; level < mrg->numLevels(); ++level) { + for (CoordBBox::Iterator iter(bbox>>level); iter; ++iter) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, + mrg->tree(level).getValue(*iter), /*tolerance=*/0.0); + } + } + + // Fill all trees according to a power of two refinement pattern + for (size_t level = 0; level < mrg->numLevels(); ++level) { + mrg->tree(level).fill( bbox>>level, double(level)); + mrg->tree(level).voxelizeActiveTiles();// avoid active tiles + // Check values + for (CoordBBox::Iterator iter(bbox>>level); iter; ++iter) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(level), + mrg->tree(level).getValue(*iter), /*tolerance=*/0.0); + } + //mrg->tree( level ).print(std::cerr, 2); + // Check bounding box of active voxels + CoordBBox bbox_actual;// Expected Tree dimensions: 33^3 -> 17^3 -> 9^3 ->5^3 + mrg->tree( level ).evalActiveVoxelBoundingBox( bbox_actual ); + CPPUNIT_ASSERT_EQUAL( bbox >> level, bbox_actual ); + } + + //pick a grid point that is shared between all the grids + const Coord ijk(0); + + // Value at ijk equals the level + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, mrg->tree(2).getValue(ijk>>2), /*tolerance=*/ 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, mrg->sampleValue<0>(ijk, size_t(2)), /*tolerance=*/ 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, mrg->sampleValue<1>(ijk, size_t(2)), /*tolerance=*/ 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, mrg->sampleValue<2>(ijk, size_t(2)), /*tolerance=*/ 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, mrg->sampleValue<1>(ijk, 2.0f), /*tolerance=*/ 0.001); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, mrg->sampleValue<1>(ijk, float(2)), /*tolerance=*/ 0.001); + + // Value at ijk at floating point level + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.25, mrg->sampleValue<1>(ijk, 2.25f), /*tolerance=*/ 0.001); + + // Value at a floating-point position close to ijk and a floating point level + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.25, + mrg->sampleValue<1>(Vec3R(0.124), 2.25f), /*tolerance=*/ 0.001); + + // prolongate at a given point at top level + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, mrg->prolongateVoxel(ijk, 0), /*tolerance=*/ 0.0); + + // First check the coarsest level (3) + for (CoordBBox::Iterator iter(bbox>>size_t(3)); iter; ++iter) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, mrg->tree(3).getValue(*iter), /*tolerance=*/0.0); + } + + // Prolongate from level 3 -> level 2 and check values + mrg->prolongateActiveVoxels(2); + for (CoordBBox::Iterator iter(bbox>>size_t(2)); iter; ++iter) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, mrg->tree(2).getValue(*iter), /*tolerance=*/0.0); + } + + // Prolongate from level 2 -> level 1 and check values + mrg->prolongateActiveVoxels(1); + for (CoordBBox::Iterator iter(bbox>>size_t(1)); iter; ++iter) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, mrg->tree(1).getValue(*iter), /*tolerance=*/0.0); + } + + // Prolongate from level 1 -> level 0 and check values + mrg->prolongateActiveVoxels(0); + for (CoordBBox::Iterator iter(bbox); iter; ++iter) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, mrg->tree(0).getValue(*iter), /*tolerance=*/0.0); + } + + // Redefine values at the finest level and check values + mrg->finestTree().fill( bbox, 5.0 ); + mrg->finestTree().voxelizeActiveTiles();// avoid active tiles + for (CoordBBox::Iterator iter(bbox); iter; ++iter) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(5.0, mrg->tree(0).getValue(*iter), /*tolerance=*/0.0); + } + + // USE RESTRICTION BY INJECTION since it doesn't have boundary issues + // // Restrict from level 0 -> level 1 and check values + // mrg->restrictActiveVoxels(1); + // for (CoordBBox::Iterator iter((bbox>>1UL).expandBy(-1)); iter; ++iter) { + // CPPUNIT_ASSERT_DOUBLES_EQUAL(5.0, mrg->tree(1).getValue(*iter), /*tolerance=*/0.0); + // } + + // // Restrict from level 1 -> level 2 and check values + // mrg->restrictActiveVoxels(2); + // for (CoordBBox::Iterator iter(bbox>>2UL); iter; ++iter) { + // CPPUNIT_ASSERT_DOUBLES_EQUAL(5.0, mrg->tree(2).getValue(*iter), /*tolerance=*/0.0); + // } + + // // Restrict from level 2 -> level 3 and check values + // mrg->restrictActiveVoxels(3); + // for (CoordBBox::Iterator iter(bbox>>3UL); iter; ++iter) { + // CPPUNIT_ASSERT_DOUBLES_EQUAL(5.0, mrg->tree(3).getValue(*iter), /*tolerance=*/0.0); + // } +} + +void +TestMultiResGrid::testIO() +{ + using namespace openvdb; + + const float radius = 1.0f; + const Vec3f center(0.0f, 0.0f, 0.0f); + const float voxelSize = 0.01f; + + openvdb::FloatGrid::Ptr ls = + openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + ls->setName("LevelSetSphere"); + + typedef tools::MultiResGrid MultiResGridT; + const size_t levels = 4; + + // Generate LOD sequence + MultiResGridT mrg( levels, *ls, /* reduction by injection */ false ); + //mrg.print( std::cout, 3 ); + + CPPUNIT_ASSERT_EQUAL(levels , mrg.numLevels()); + CPPUNIT_ASSERT_EQUAL(size_t(0), mrg.finestLevel()); + CPPUNIT_ASSERT_EQUAL(levels-1, mrg.coarsestLevel()); + + // Check inside and outside values + for ( size_t level = 1; level < mrg.numLevels(); ++level) { + const float inside = mrg.sampleValue<1>( Coord(0,0,0), 0UL, level ); + CPPUNIT_ASSERT_DOUBLES_EQUAL( -ls->background(), inside,/*tolerance=*/ 0.001 ); + const float outside = mrg.sampleValue<1>( Coord( int(1.1*radius/voxelSize) ), 0UL, level ); + CPPUNIT_ASSERT_DOUBLES_EQUAL( ls->background(), outside,/*tolerance=*/ 0.001 ); + } + + const std::string filename( "sphere.vdb" ); + + // Write grids + io::File outputFile( filename ); + outputFile.write( *mrg.grids() ); + outputFile.close(); + + // Read grids + openvdb::initialize(); + openvdb::io::File file( filename ); + file.open(); + GridPtrVecPtr grids = file.getGrids(); + CPPUNIT_ASSERT_EQUAL( levels, grids->size() ); + //std::cerr << "\nsize = " << grids->size() << std::endl; + for ( size_t i=0; isize(); ++i ) { + FloatGrid::Ptr grid = gridPtrCast(grids->at(i)); + CPPUNIT_ASSERT_EQUAL( grid->activeVoxelCount(), mrg.tree(i).activeVoxelCount() ); + //grid->print(std::cerr, 3); + } + file.close(); + + ::remove(filename.c_str()); +} + +void +TestMultiResGrid::testModels() +{ + using namespace openvdb; + +#ifdef TestMultiResGrid_DATA_PATH + initialize();//required whenever I/O of OpenVDB files is performed! + const std::string path(TestMultiResGrid_DATA_PATH); + std::vector filenames; + filenames.push_back("armadillo.vdb"); + filenames.push_back("buddha.vdb"); + filenames.push_back("bunny.vdb"); + filenames.push_back("crawler.vdb"); + filenames.push_back("dragon.vdb"); + filenames.push_back("iss.vdb"); + filenames.push_back("utahteapot.vdb"); + util::CpuTimer timer; + for ( size_t i=0; i\"" << filenames[i] + << "\" =====================" << std::endl; + std::cerr << "Reading \"" << filenames[i] << "\" ..."; + io::File file( path + filenames[i] ); + file.open(false);//disable delayed loading + FloatGrid::Ptr model = gridPtrCast(file.getGrids()->at(0)); + std::cerr << " done\nProcessing \"" << filenames[i] << "\" ..."; + timer.start("\nMultiResGrid processing"); + tools::MultiResGrid mrg( 6, model ); + timer.stop(); + std::cerr << "\n High-res level set " << tools::checkLevelSet(*mrg.grid(0)) << "\n"; + std::cerr << " done\nWriting \"" << filenames[i] << "\" ..."; + + io::File file( "/tmp/" + filenames[i] ); + file.write( *mrg.grids() ); + file.close(); + + std::cerr << " done\n" << std::endl; + // {// in-betweening + // timer.start("\nIn-betweening"); + // FloatGrid::Ptr model3 = mrg.createGrid( 1.9999f ); + // timer.stop(); + // // + // std::cerr << "\n" << tools::checkLevelSet(*model3) << "\n"; + // // + // GridPtrVecPtr grids2( new GridPtrVec ); + // grids2->push_back( model3 ); + // io::File file2( "/tmp/inbetween_" + filenames[i] ); + // file2.write( *grids2 ); + // file2.close(); + // } + // {// prolongate + // timer.start("\nProlongate"); + // mrg.prolongateActiveVoxels(1); + // FloatGrid::Ptr model31= mrg.grid(1); + // timer.stop(); + // GridPtrVecPtr grids2( new GridPtrVec ); + // grids2->push_back( model31 ); + // io::File file2( "/tmp/prolongate_" + filenames[i] ); + // file2.write( *grids2 ); + // file2.close(); + // } + //::remove(filenames[i].c_str()); + } +#endif +} diff --git a/openvdb/unittest/TestName.cc b/openvdb/unittest/TestName.cc new file mode 100644 index 00000000..1fa5fbcc --- /dev/null +++ b/openvdb/unittest/TestName.cc @@ -0,0 +1,81 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + +class TestName : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestName); + CPPUNIT_TEST(test); + CPPUNIT_TEST(testIO); + CPPUNIT_TEST(testMultipleIO); + CPPUNIT_TEST_SUITE_END(); + + void test(); + void testIO(); + void testMultipleIO(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestName); + +void +TestName::test() +{ + using namespace openvdb; + + Name name; + Name name2("something"); + Name name3 = std::string("something2"); + name = "something"; + + CPPUNIT_ASSERT(name == name2); + CPPUNIT_ASSERT(name != name3); + CPPUNIT_ASSERT(name != Name("testing")); + CPPUNIT_ASSERT(name == Name("something")); +} + +void +TestName::testIO() +{ + using namespace openvdb; + + Name name("some name that i made up"); + + std::ostringstream ostr(std::ios_base::binary); + + openvdb::writeString(ostr, name); + + name = "some other name"; + + CPPUNIT_ASSERT(name == Name("some other name")); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + + name = openvdb::readString(istr); + + CPPUNIT_ASSERT(name == Name("some name that i made up")); +} + +void +TestName::testMultipleIO() +{ + using namespace openvdb; + + Name name("some name that i made up"); + Name name2("something else"); + + std::ostringstream ostr(std::ios_base::binary); + + openvdb::writeString(ostr, name); + openvdb::writeString(ostr, name2); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + + Name n = openvdb::readString(istr), n2 = openvdb::readString(istr); + + CPPUNIT_ASSERT(name == n); + CPPUNIT_ASSERT(name2 == n2); +} diff --git a/openvdb/unittest/TestNodeIterator.cc b/openvdb/unittest/TestNodeIterator.cc new file mode 100644 index 00000000..605f2cb1 --- /dev/null +++ b/openvdb/unittest/TestNodeIterator.cc @@ -0,0 +1,375 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + + +class TestNodeIterator: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestNodeIterator); + CPPUNIT_TEST(testEmpty); + CPPUNIT_TEST(testSinglePositive); + CPPUNIT_TEST(testSingleNegative); + CPPUNIT_TEST(testMultipleBlocks); + CPPUNIT_TEST(testDepthBounds); + CPPUNIT_TEST_SUITE_END(); + + void testEmpty(); + void testSinglePositive(); + void testSingleNegative(); + void testMultipleBlocks(); + void testDepthBounds(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestNodeIterator); + +namespace { +typedef openvdb::tree::Tree4::Type Tree323f; +} + + +//////////////////////////////////////// + + +void +TestNodeIterator::testEmpty() +{ + Tree323f tree(/*fillValue=*/256.0f); + { + Tree323f::NodeCIter iter(tree); + CPPUNIT_ASSERT(!iter.next()); + } + { + tree.setValue(openvdb::Coord(8, 16, 24), 10.f); + Tree323f::NodeIter iter(tree); // non-const + CPPUNIT_ASSERT(iter); + + // Try modifying the tree through a non-const iterator. + Tree323f::RootNodeType* root = NULL; + iter.getNode(root); + CPPUNIT_ASSERT(root != NULL); + root->clear(); + + // Verify that the tree is now empty. + iter = Tree323f::NodeIter(tree); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT(!iter.next()); + } +} + + +void +TestNodeIterator::testSinglePositive() +{ + { + Tree323f tree(/*fillValue=*/256.0f); + + tree.setValue(openvdb::Coord(8, 16, 24), 10.f); + + Tree323f::NodeCIter iter(tree); + + CPPUNIT_ASSERT(Tree323f::LeafNodeType::DIM == 8); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getDepth()); + CPPUNIT_ASSERT_EQUAL(tree.treeDepth(), 1 + iter.getLevel()); + openvdb::CoordBBox range, bbox; + tree.getIndexRange(range); + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(bbox.min(), range.min()); + CPPUNIT_ASSERT_EQUAL(bbox.max(), range.max()); + + // Descend to the depth-1 internal node with bounding box + // (0, 0, 0) -> (255, 255, 255) containing voxel (8, 16, 24). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(1U, iter.getDepth()); + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), bbox.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord((1 << (3 + 2 + 3)) - 1), bbox.max()); + + // Descend to the depth-2 internal node with bounding box + // (0, 0, 0) -> (31, 31, 31) containing voxel (8, 16, 24). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(2U, iter.getDepth()); + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), bbox.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord((1 << (2 + 3)) - 1), bbox.max()); + + // Descend to the leaf node with bounding box (8, 16, 24) -> (15, 23, 31) + // containing voxel (8, 16, 24). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getLevel()); + iter.getBoundingBox(bbox); + range.min().reset(8, 16, 24); + range.max() = range.min().offsetBy((1 << 3) - 1); // add leaf node size + CPPUNIT_ASSERT_EQUAL(range.min(), bbox.min()); + CPPUNIT_ASSERT_EQUAL(range.max(), bbox.max()); + + iter.next(); + CPPUNIT_ASSERT(!iter); + } + { + Tree323f tree(/*fillValue=*/256.0f); + + tree.setValue(openvdb::Coord(129), 10.f); + + Tree323f::NodeCIter iter(tree); + + CPPUNIT_ASSERT(Tree323f::LeafNodeType::DIM == 8); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getDepth()); + CPPUNIT_ASSERT_EQUAL(tree.treeDepth(), 1 + iter.getLevel()); + openvdb::CoordBBox range, bbox; + tree.getIndexRange(range); + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(bbox.min(), range.min()); + CPPUNIT_ASSERT_EQUAL(bbox.max(), range.max()); + + // Descend to the depth-1 internal node with bounding box + // (0, 0, 0) -> (255, 255, 255) containing voxel (129, 129, 129). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(1U, iter.getDepth()); + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), bbox.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord((1 << (3 + 2 + 3)) - 1), bbox.max()); + + // Descend to the depth-2 internal node with bounding box + // (128, 128, 128) -> (159, 159, 159) containing voxel (129, 129, 129). + // (128 is the nearest multiple of 32 less than 129.) + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(2U, iter.getDepth()); + iter.getBoundingBox(bbox); + range.min().reset(128, 128, 128); + CPPUNIT_ASSERT_EQUAL(range.min(), bbox.min()); + CPPUNIT_ASSERT_EQUAL(range.min().offsetBy((1 << (2 + 3)) - 1), bbox.max()); + + // Descend to the leaf node with bounding box + // (128, 128, 128) -> (135, 135, 135) containing voxel (129, 129, 129). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getLevel()); + iter.getBoundingBox(bbox); + range.max() = range.min().offsetBy((1 << 3) - 1); // add leaf node size + CPPUNIT_ASSERT_EQUAL(range.min(), bbox.min()); + CPPUNIT_ASSERT_EQUAL(range.max(), bbox.max()); + + iter.next(); + CPPUNIT_ASSERT(!iter); + } +} + + +void +TestNodeIterator::testSingleNegative() +{ + Tree323f tree(/*fillValue=*/256.0f); + + tree.setValue(openvdb::Coord(-1), 10.f); + + Tree323f::NodeCIter iter(tree); + + CPPUNIT_ASSERT(Tree323f::LeafNodeType::DIM == 8); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getDepth()); + CPPUNIT_ASSERT_EQUAL(tree.treeDepth(), 1 + iter.getLevel()); + openvdb::CoordBBox range, bbox; + tree.getIndexRange(range); + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(bbox.min(), range.min()); + CPPUNIT_ASSERT_EQUAL(bbox.max(), range.max()); + + // Descend to the depth-1 internal node with bounding box + // (-256, -256, -256) -> (-1, -1, -1) containing voxel (-1, -1, -1). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(1U, iter.getDepth()); + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(-(1 << (3 + 2 + 3))), bbox.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(-1), bbox.max()); + + // Descend to the depth-2 internal node with bounding box + // (-32, -32, -32) -> (-1, -1, -1) containing voxel (-1, -1, -1). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(2U, iter.getDepth()); + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(-(1 << (2 + 3))), bbox.min()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(-1), bbox.max()); + + // Descend to the leaf node with bounding box (-8, -8, -8) -> (-1, -1, -1) + // containing voxel (-1, -1, -1). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getLevel()); + iter.getBoundingBox(bbox); + range.max().reset(-1, -1, -1); + range.min() = range.max().offsetBy(-((1 << 3) - 1)); // add leaf node size + CPPUNIT_ASSERT_EQUAL(range.min(), bbox.min()); + CPPUNIT_ASSERT_EQUAL(range.max(), bbox.max()); + + iter.next(); + CPPUNIT_ASSERT(!iter); +} + + +void +TestNodeIterator::testMultipleBlocks() +{ + Tree323f tree(/*fillValue=*/256.0f); + + tree.setValue(openvdb::Coord(-1), 10.f); + tree.setValue(openvdb::Coord(129), 10.f); + + Tree323f::NodeCIter iter(tree); + + CPPUNIT_ASSERT(Tree323f::LeafNodeType::DIM == 8); + + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getDepth()); + CPPUNIT_ASSERT_EQUAL(tree.treeDepth(), 1 + iter.getLevel()); + + // Descend to the depth-1 internal node with bounding box + // (-256, -256, -256) -> (-1, -1, -1) containing voxel (-1, -1, -1). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(1U, iter.getDepth()); + + // Descend to the depth-2 internal node with bounding box + // (-32, -32, -32) -> (-1, -1, -1) containing voxel (-1, -1, -1). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(2U, iter.getDepth()); + + // Descend to the leaf node with bounding box (-8, -8, -8) -> (-1, -1, -1) + // containing voxel (-1, -1, -1). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getLevel()); + openvdb::Coord expectedMin, expectedMax(-1, -1, -1); + expectedMin = expectedMax.offsetBy(-((1 << 3) - 1)); // add leaf node size + openvdb::CoordBBox bbox; + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(expectedMin, bbox.min()); + CPPUNIT_ASSERT_EQUAL(expectedMax, bbox.max()); + + // Ascend to the depth-1 internal node with bounding box (0, 0, 0) -> (255, 255, 255) + // containing voxel (129, 129, 129). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(1U, iter.getDepth()); + + // Descend to the depth-2 internal node with bounding box + // (128, 128, 128) -> (159, 159, 159) containing voxel (129, 129, 129). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(2U, iter.getDepth()); + + // Descend to the leaf node with bounding box (128, 128, 128) -> (135, 135, 135) + // containing voxel (129, 129, 129). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getLevel()); + expectedMin.reset(128, 128, 128); + expectedMax = expectedMin.offsetBy((1 << 3) - 1); // add leaf node size + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(expectedMin, bbox.min()); + CPPUNIT_ASSERT_EQUAL(expectedMax, bbox.max()); + + iter.next(); + CPPUNIT_ASSERT(!iter); +} + + +void +TestNodeIterator::testDepthBounds() +{ + Tree323f tree(/*fillValue=*/256.0f); + + tree.setValue(openvdb::Coord(-1), 10.f); + tree.setValue(openvdb::Coord(129), 10.f); + + { + // Iterate over internal nodes only. + Tree323f::NodeCIter iter(tree); + iter.setMaxDepth(2); + iter.setMinDepth(1); + + // Begin at the depth-1 internal node with bounding box + // (-256, -256, -256) -> (-1, -1, -1) containing voxel (-1, -1, -1). + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(1U, iter.getDepth()); + + // Descend to the depth-2 internal node with bounding box + // (-32, -32, -32) -> (-1, -1, -1) containing voxel (-1, -1, -1). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(2U, iter.getDepth()); + + // Skipping the leaf node, ascend to the depth-1 internal node with bounding box + // (0, 0, 0) -> (255, 255, 255) containing voxel (129, 129, 129). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(1U, iter.getDepth()); + + // Descend to the depth-2 internal node with bounding box + // (128, 128, 128) -> (159, 159, 159) containing voxel (129, 129, 129). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(2U, iter.getDepth()); + + // Verify that no internal nodes remain unvisited. + iter.next(); + CPPUNIT_ASSERT(!iter); + } + { + // Iterate over depth-1 internal nodes only. + Tree323f::NodeCIter iter(tree); + iter.setMaxDepth(1); + iter.setMinDepth(1); + + // Begin at the depth-1 internal node with bounding box + // (-256, -256, -256) -> (-1, -1, -1) containing voxel (-1, -1, -1). + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(1U, iter.getDepth()); + + // Skip to the depth-1 internal node with bounding box + // (0, 0, 0) -> (255, 255, 255) containing voxel (129, 129, 129). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(1U, iter.getDepth()); + + // Verify that no depth-1 nodes remain unvisited. + iter.next(); + CPPUNIT_ASSERT(!iter); + } + { + // Iterate over leaf nodes only. + Tree323f::NodeCIter iter = tree.cbeginNode(); + iter.setMaxDepth(3); + iter.setMinDepth(3); + + // Begin at the leaf node with bounding box (-8, -8, -8) -> (-1, -1, -1) + // containing voxel (-1, -1, -1). + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getLevel()); + + // Skip to the leaf node with bounding box (128, 128, 128) -> (135, 135, 135) + // containing voxel (129, 129, 129). + iter.next(); + CPPUNIT_ASSERT(iter); + CPPUNIT_ASSERT_EQUAL(0U, iter.getLevel()); + + // Verify that no leaf nodes remain unvisited. + iter.next(); + CPPUNIT_ASSERT(!iter); + } +} diff --git a/openvdb/unittest/TestNodeManager.cc b/openvdb/unittest/TestNodeManager.cc new file mode 100644 index 00000000..17b2f2c4 --- /dev/null +++ b/openvdb/unittest/TestNodeManager.cc @@ -0,0 +1,153 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() +#include + + +class TestNodeManager: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestNodeManager); + CPPUNIT_TEST(testAll); + CPPUNIT_TEST_SUITE_END(); + + void testAll(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestNodeManager); + + +namespace { + +template +struct NodeCountOp { + NodeCountOp() : nodeCount(TreeT::DEPTH, 0), totalCount(0) + { + } + NodeCountOp(const NodeCountOp&, tbb::split) + : nodeCount(TreeT::DEPTH, 0), totalCount(0) + { + } + void join(const NodeCountOp& other) + { + for (size_t i = 0; i < nodeCount.size(); ++i) { + nodeCount[i] += other.nodeCount[i]; + } + totalCount += other.totalCount; + } + // do nothing for the root node + void operator()(const typename TreeT::RootNodeType&) + { + } + // count the internal and leaf nodes + template + void operator()(const NodeT&) + { + ++(nodeCount[NodeT::LEVEL]); + ++totalCount; + } + std::vector nodeCount; + openvdb::Index64 totalCount; +};// NodeCountOp + +}//unnamed namespace + +void +TestNodeManager::testAll() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + using openvdb::Vec3f; + using openvdb::Index64; + using openvdb::FloatGrid; + using openvdb::FloatTree; + + const Vec3f center(0.35f, 0.35f, 0.35f); + const float radius = 0.15f; + const int dim = 128, half_width = 5; + const float voxel_size = 1.0f/dim; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/half_width*voxel_size); + FloatTree& tree = grid->tree(); + grid->setTransform(openvdb::math::Transform::createLinearTransform(/*voxel size=*/voxel_size)); + + unittest_util::makeSphere(Coord(dim), center, + radius, *grid, unittest_util::SPHERE_SPARSE_NARROW_BAND); + + CPPUNIT_ASSERT_EQUAL(4, int(FloatTree::DEPTH)); + CPPUNIT_ASSERT_EQUAL(3, int(openvdb::tree::NodeManager::LEVELS)); + + std::vector nodeCount; + for (openvdb::Index i=0; i& points, + const std::vector& radius) + : mPoints(&points) + , mRadius(&radius) + { + } + + // Return the number of points in the array + size_t size() const { + return mPoints->size(); + } + + // Return the world-space position for the nth particle. + void getPos(size_t n, PosType& xyz) const { + xyz = (*mPoints)[n]; + } + + // Return the world-space radius for the nth particle. + void getRadius(size_t n, ScalarType& radius) const { + radius = (*mRadius)[n]; + } + +protected: + std::vector const * const mPoints; + std::vector const * const mRadius; +}; // ParticleList + + +template +bool hasDuplicates(const std::vector& items) +{ + std::vector vec(items); + std::sort(vec.begin(), vec.end()); + + size_t duplicates = 0; + for (size_t n = 1, N = vec.size(); n < N; ++n) { + if (vec[n] == vec[n-1]) ++duplicates; + } + return duplicates != 0; +} + +} // namespace + + + +//////////////////////////////////////// + + +void +TestParticleAtlas::testParticleAtlas() +{ + // generate points + + const size_t numParticle = 40000; + const double minVoxelSize = 0.01; + + std::vector points; + unittest_util::genPoints(numParticle, points); + + std::vector radius; + for (size_t n = 0, N = points.size() / 2; n < N; ++n) { + radius.push_back(minVoxelSize); + } + + for (size_t n = points.size() / 2, N = points.size(); n < N; ++n) { + radius.push_back(minVoxelSize * 2.0); + } + + ParticleList particles(points, radius); + + // construct data structure + + typedef openvdb::tools::ParticleAtlas<> ParticleAtlas; + + ParticleAtlas atlas; + + CPPUNIT_ASSERT(atlas.empty()); + CPPUNIT_ASSERT(atlas.levels() == 0); + + atlas.construct(particles, minVoxelSize); + + CPPUNIT_ASSERT(!atlas.empty()); + CPPUNIT_ASSERT(atlas.levels() == 2); + + CPPUNIT_ASSERT( + openvdb::math::isApproxEqual(atlas.minRadius(0), minVoxelSize)); + + CPPUNIT_ASSERT( + openvdb::math::isApproxEqual(atlas.minRadius(1), minVoxelSize * 2.0)); + + typedef openvdb::tools::ParticleAtlas<>::Iterator ParticleAtlasIterator; + + ParticleAtlasIterator it(atlas); + + CPPUNIT_ASSERT(atlas.levels() == 2); + + std::vector indices; + indices.reserve(numParticle); + + it.updateFromLevel(0); + + CPPUNIT_ASSERT(it); + CPPUNIT_ASSERT_EQUAL(it.size(), numParticle - (points.size() / 2)); + + + for (; it; ++it) { + indices.push_back(*it); + } + + it.updateFromLevel(1); + + CPPUNIT_ASSERT(it); + CPPUNIT_ASSERT_EQUAL(it.size(), (points.size() / 2)); + + + for (; it; ++it) { + indices.push_back(*it); + } + + CPPUNIT_ASSERT_EQUAL(numParticle, indices.size()); + + CPPUNIT_ASSERT(!hasDuplicates(indices)); + + + openvdb::Vec3R center = points[0]; + double searchRadius = minVoxelSize * 10.0; + + it.worldSpaceSearchAndUpdate(center, searchRadius, particles); + CPPUNIT_ASSERT(it); + + indices.clear(); + for (; it; ++it) { + indices.push_back(*it); + } + + CPPUNIT_ASSERT_EQUAL(it.size(), indices.size()); + CPPUNIT_ASSERT(!hasDuplicates(indices)); + + + openvdb::BBoxd bbox; + for (size_t n = 0, N = points.size() / 2; n < N; ++n) { + bbox.expand(points[n]); + } + + it.worldSpaceSearchAndUpdate(bbox, particles); + CPPUNIT_ASSERT(it); + + indices.clear(); + for (; it; ++it) { + indices.push_back(*it); + } + + CPPUNIT_ASSERT_EQUAL(it.size(), indices.size()); + CPPUNIT_ASSERT(!hasDuplicates(indices)); +} + diff --git a/openvdb/unittest/TestParticlesToLevelSet.cc b/openvdb/unittest/TestParticlesToLevelSet.cc new file mode 100644 index 00000000..006b74dc --- /dev/null +++ b/openvdb/unittest/TestParticlesToLevelSet.cc @@ -0,0 +1,580 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include // for sdfInteriorMask() +#include + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + + +class TestParticlesToLevelSet: public CppUnit::TestFixture +{ +public: + virtual void setUp() {openvdb::initialize();} + virtual void tearDown() {openvdb::uninitialize();} + + void writeGrid(openvdb::GridBase::Ptr grid, std::string fileName) const + { + std::cout << "\nWriting \""<setName("TestParticlesToLevelSet"); + openvdb::GridPtrVec grids; + grids.push_back(grid); + openvdb::io::File file(fileName + ".vdb"); + file.write(grids); + file.close(); + } + + CPPUNIT_TEST_SUITE(TestParticlesToLevelSet); + CPPUNIT_TEST(testBlindData); + CPPUNIT_TEST(testMyParticleList); + CPPUNIT_TEST(testRasterizeSpheres); + CPPUNIT_TEST(testRasterizeSpheresAndId); + CPPUNIT_TEST(testRasterizeTrails); + CPPUNIT_TEST(testRasterizeTrailsAndId); + CPPUNIT_TEST(testMaskOutput); + CPPUNIT_TEST_SUITE_END(); + + void testBlindData(); + void testMyParticleList(); + void testRasterizeSpheres(); + void testRasterizeSpheresAndId(); + void testRasterizeTrails(); + void testRasterizeTrailsAndId(); + void testMaskOutput(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestParticlesToLevelSet); + +class MyParticleList +{ +protected: + struct MyParticle { + openvdb::Vec3R p, v; + openvdb::Real r; + }; + openvdb::Real mRadiusScale; + openvdb::Real mVelocityScale; + std::vector mParticleList; +public: + + typedef openvdb::Vec3R PosType; + + MyParticleList(openvdb::Real rScale=1, openvdb::Real vScale=1) + : mRadiusScale(rScale), mVelocityScale(vScale) {} + void add(const openvdb::Vec3R &p, const openvdb::Real &r, + const openvdb::Vec3R &v=openvdb::Vec3R(0,0,0)) + { + MyParticle pa; + pa.p = p; + pa.r = r; + pa.v = v; + mParticleList.push_back(pa); + } + /// @return coordinate bbox in the space of the specified transfrom + openvdb::CoordBBox getBBox(const openvdb::GridBase& grid) { + openvdb::CoordBBox bbox; + openvdb::Coord &min= bbox.min(), &max = bbox.max(); + openvdb::Vec3R pos; + openvdb::Real rad, invDx = 1/grid.voxelSize()[0]; + for (size_t n=0, e=this->size(); ngetPosRad(n, pos, rad); + const openvdb::Vec3d xyz = grid.worldToIndex(pos); + const openvdb::Real r = rad * invDx; + for (int i=0; i<3; ++i) { + min[i] = openvdb::math::Min(min[i], openvdb::math::Floor(xyz[i] - r)); + max[i] = openvdb::math::Max(max[i], openvdb::math::Ceil( xyz[i] + r)); + } + } + return bbox; + } + //typedef int AttributeType; + // The methods below are only required for the unit-tests + openvdb::Vec3R pos(int n) const {return mParticleList[n].p;} + openvdb::Vec3R vel(int n) const {return mVelocityScale*mParticleList[n].v;} + openvdb::Real radius(int n) const {return mRadiusScale*mParticleList[n].r;} + + ////////////////////////////////////////////////////////////////////////////// + /// The methods below are the only ones required by tools::ParticleToLevelSet + /// @note We return by value since the radius and velocities are modified + /// by the scaling factors! Also these methods are all assumed to + /// be thread-safe. + + /// Return the total number of particles in list. + /// Always required! + size_t size() const { return mParticleList.size(); } + + /// Get the world space position of n'th particle. + /// Required by ParticledToLevelSet::rasterizeSphere(*this,radius). + void getPos(size_t n, openvdb::Vec3R&pos) const { pos = mParticleList[n].p; } + + + void getPosRad(size_t n, openvdb::Vec3R& pos, openvdb::Real& rad) const { + pos = mParticleList[n].p; + rad = mRadiusScale*mParticleList[n].r; + } + void getPosRadVel(size_t n, openvdb::Vec3R& pos, openvdb::Real& rad, openvdb::Vec3R& vel) const { + pos = mParticleList[n].p; + rad = mRadiusScale*mParticleList[n].r; + vel = mVelocityScale*mParticleList[n].v; + } + // The method below is only required for attribute transfer + void getAtt(size_t n, openvdb::Index32& att) const { att = openvdb::Index32(n); } +}; + + +void +TestParticlesToLevelSet::testBlindData() +{ + using BlindTypeIF = openvdb::tools::p2ls_internal::BlindData; + + BlindTypeIF value(openvdb::Index(8), 5.2f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(8), value.visible()); + ASSERT_DOUBLES_EXACTLY_EQUAL(5.2f, value.blind()); + + BlindTypeIF value2(openvdb::Index(13), 1.6f); + + { // test equality + // only visible portion needs to be equal + BlindTypeIF blind(openvdb::Index(13), 6.7f); + CPPUNIT_ASSERT(value2 == blind); + } + + { // test addition of two blind types + BlindTypeIF blind = value + value2; + CPPUNIT_ASSERT_EQUAL(openvdb::Index(8+13), blind.visible()); + CPPUNIT_ASSERT_EQUAL(0.0f, blind.blind()); // blind values are both dropped + } + + { // test addition of blind type with visible type + BlindTypeIF blind = value + 3; + CPPUNIT_ASSERT_EQUAL(openvdb::Index(8+3), blind.visible()); + CPPUNIT_ASSERT_EQUAL(5.2f, blind.blind()); + } + + { // test addition of blind type with type that requires casting + // note that this will generate conversion warnings if not handled properly + BlindTypeIF blind = value + 3.7; + CPPUNIT_ASSERT_EQUAL(openvdb::Index(8+3), blind.visible()); + CPPUNIT_ASSERT_EQUAL(5.2f, blind.blind()); + } +} + + +void +TestParticlesToLevelSet::testMyParticleList() +{ + MyParticleList pa; + CPPUNIT_ASSERT_EQUAL(0, int(pa.size())); + pa.add(openvdb::Vec3R(10,10,10), 2, openvdb::Vec3R(1,0,0)); + CPPUNIT_ASSERT_EQUAL(1, int(pa.size())); + ASSERT_DOUBLES_EXACTLY_EQUAL(10, pa.pos(0)[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(10, pa.pos(0)[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(10, pa.pos(0)[2]); + ASSERT_DOUBLES_EXACTLY_EQUAL(1 , pa.vel(0)[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0 , pa.vel(0)[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0 , pa.vel(0)[2]); + ASSERT_DOUBLES_EXACTLY_EQUAL(2 , pa.radius(0)); + pa.add(openvdb::Vec3R(20,20,20), 3); + CPPUNIT_ASSERT_EQUAL(2, int(pa.size())); + ASSERT_DOUBLES_EXACTLY_EQUAL(20, pa.pos(1)[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(20, pa.pos(1)[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(20, pa.pos(1)[2]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0 , pa.vel(1)[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0 , pa.vel(1)[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL(0 , pa.vel(1)[2]); + ASSERT_DOUBLES_EXACTLY_EQUAL(3 , pa.radius(1)); + + const float voxelSize = 0.5f, halfWidth = 4.0f; + openvdb::FloatGrid::Ptr ls = openvdb::createLevelSet(voxelSize, halfWidth); + openvdb::CoordBBox bbox = pa.getBBox(*ls); + ASSERT_DOUBLES_EXACTLY_EQUAL((10-2)/voxelSize, bbox.min()[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL((10-2)/voxelSize, bbox.min()[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL((10-2)/voxelSize, bbox.min()[2]); + ASSERT_DOUBLES_EXACTLY_EQUAL((20+3)/voxelSize, bbox.max()[0]); + ASSERT_DOUBLES_EXACTLY_EQUAL((20+3)/voxelSize, bbox.max()[1]); + ASSERT_DOUBLES_EXACTLY_EQUAL((20+3)/voxelSize, bbox.max()[2]); +} + + +void +TestParticlesToLevelSet::testRasterizeSpheres() +{ + MyParticleList pa; + pa.add(openvdb::Vec3R(10,10,10), 2); + pa.add(openvdb::Vec3R(20,20,20), 2); + // testing CSG + pa.add(openvdb::Vec3R(31.0,31,31), 5); + pa.add(openvdb::Vec3R(31.5,31,31), 5); + pa.add(openvdb::Vec3R(32.0,31,31), 5); + pa.add(openvdb::Vec3R(32.5,31,31), 5); + pa.add(openvdb::Vec3R(33.0,31,31), 5); + pa.add(openvdb::Vec3R(33.5,31,31), 5); + pa.add(openvdb::Vec3R(34.0,31,31), 5); + pa.add(openvdb::Vec3R(34.5,31,31), 5); + pa.add(openvdb::Vec3R(35.0,31,31), 5); + pa.add(openvdb::Vec3R(35.5,31,31), 5); + pa.add(openvdb::Vec3R(36.0,31,31), 5); + CPPUNIT_ASSERT_EQUAL(13, int(pa.size())); + + const float voxelSize = 1.0f, halfWidth = 2.0f; + openvdb::FloatGrid::Ptr ls = openvdb::createLevelSet(voxelSize, halfWidth); + openvdb::tools::ParticlesToLevelSet raster(*ls); + + raster.setGrainSize(1);//a value of zero disables threading + raster.rasterizeSpheres(pa); + raster.finalize(); + //openvdb::FloatGrid::Ptr ls = raster.getSdfGrid(); + + //ls->tree().print(std::cout,4); + //this->writeGrid(ls, "testRasterizeSpheres"); + + ASSERT_DOUBLES_EXACTLY_EQUAL(halfWidth * voxelSize, + ls->tree().getValue(openvdb::Coord( 0, 0, 0))); + + ASSERT_DOUBLES_EXACTLY_EQUAL( 2, ls->tree().getValue(openvdb::Coord( 6,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 1, ls->tree().getValue(openvdb::Coord( 7,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 0, ls->tree().getValue(openvdb::Coord( 8,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-1, ls->tree().getValue(openvdb::Coord( 9,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-2, ls->tree().getValue(openvdb::Coord(10,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-1, ls->tree().getValue(openvdb::Coord(11,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 0, ls->tree().getValue(openvdb::Coord(12,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 1, ls->tree().getValue(openvdb::Coord(13,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 2, ls->tree().getValue(openvdb::Coord(14,10,10))); + + ASSERT_DOUBLES_EXACTLY_EQUAL( 2, ls->tree().getValue(openvdb::Coord(20,16,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 1, ls->tree().getValue(openvdb::Coord(20,17,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 0, ls->tree().getValue(openvdb::Coord(20,18,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-1, ls->tree().getValue(openvdb::Coord(20,19,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-2, ls->tree().getValue(openvdb::Coord(20,20,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-1, ls->tree().getValue(openvdb::Coord(20,21,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 0, ls->tree().getValue(openvdb::Coord(20,22,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 1, ls->tree().getValue(openvdb::Coord(20,23,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 2, ls->tree().getValue(openvdb::Coord(20,24,20))); + {// full but slow test of all voxels + openvdb::CoordBBox bbox = pa.getBBox(*ls); + bbox.expand(static_cast(halfWidth)+1); + openvdb::Index64 count=0; + const float outside = ls->background(), inside = -outside; + const openvdb::Coord &min=bbox.min(), &max=bbox.max(); + for (openvdb::Coord ijk=min; ijk[0]indexToWorld(ijk.asVec3d()); + double dist = (xyz-pa.pos(0)).length()-pa.radius(0); + for (int i = 1, s = int(pa.size()); i < s; ++i) { + dist=openvdb::math::Min(dist,(xyz-pa.pos(i)).length()-pa.radius(i)); + } + const float val = ls->tree().getValue(ijk); + if (dist >= outside) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(outside, val, 0.0001); + CPPUNIT_ASSERT(ls->tree().isValueOff(ijk)); + } else if( dist <= inside ) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(inside, val, 0.0001); + CPPUNIT_ASSERT(ls->tree().isValueOff(ijk)); + } else { + CPPUNIT_ASSERT_DOUBLES_EQUAL( dist, val, 0.0001); + CPPUNIT_ASSERT(ls->tree().isValueOn(ijk)); + ++count; + } + } + } + } + //std::cerr << "\nExpected active voxel count = " << count + // << ", actual active voxle count = " + // << ls->activeVoxelCount() << std::endl; + CPPUNIT_ASSERT_EQUAL(count, ls->activeVoxelCount()); + } +} + + +void +TestParticlesToLevelSet::testRasterizeSpheresAndId() +{ + MyParticleList pa(0.5f); + pa.add(openvdb::Vec3R(10,10,10), 4); + pa.add(openvdb::Vec3R(20,20,20), 4); + // testing CSG + pa.add(openvdb::Vec3R(31.0,31,31),10); + pa.add(openvdb::Vec3R(31.5,31,31),10); + pa.add(openvdb::Vec3R(32.0,31,31),10); + pa.add(openvdb::Vec3R(32.5,31,31),10); + pa.add(openvdb::Vec3R(33.0,31,31),10); + pa.add(openvdb::Vec3R(33.5,31,31),10); + pa.add(openvdb::Vec3R(34.0,31,31),10); + pa.add(openvdb::Vec3R(34.5,31,31),10); + pa.add(openvdb::Vec3R(35.0,31,31),10); + pa.add(openvdb::Vec3R(35.5,31,31),10); + pa.add(openvdb::Vec3R(36.0,31,31),10); + CPPUNIT_ASSERT_EQUAL(13, int(pa.size())); + + typedef openvdb::tools::ParticlesToLevelSet RasterT; + const float voxelSize = 1.0f, halfWidth = 2.0f; + openvdb::FloatGrid::Ptr ls = openvdb::createLevelSet(voxelSize, halfWidth); + + RasterT raster(*ls); + raster.setGrainSize(1);//a value of zero disables threading + raster.rasterizeSpheres(pa); + raster.finalize(); + const RasterT::AttGridType::Ptr id = raster.attributeGrid(); + + int minVal = std::numeric_limits::max(), maxVal = -minVal; + for (RasterT::AttGridType::ValueOnCIter i=id->cbeginValueOn(); i; ++i) { + minVal = openvdb::math::Min(minVal, int(*i)); + maxVal = openvdb::math::Max(maxVal, int(*i)); + } + CPPUNIT_ASSERT_EQUAL(0 , minVal); + CPPUNIT_ASSERT_EQUAL(12, maxVal); + + //grid.tree().print(std::cout,4); + //id->print(std::cout,4); + //this->writeGrid(ls, "testRasterizeSpheres"); + + ASSERT_DOUBLES_EXACTLY_EQUAL(halfWidth * voxelSize, + ls->tree().getValue(openvdb::Coord( 0, 0, 0))); + + ASSERT_DOUBLES_EXACTLY_EQUAL( 2, ls->tree().getValue(openvdb::Coord( 6,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 1, ls->tree().getValue(openvdb::Coord( 7,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 0, ls->tree().getValue(openvdb::Coord( 8,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-1, ls->tree().getValue(openvdb::Coord( 9,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-2, ls->tree().getValue(openvdb::Coord(10,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-1, ls->tree().getValue(openvdb::Coord(11,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 0, ls->tree().getValue(openvdb::Coord(12,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 1, ls->tree().getValue(openvdb::Coord(13,10,10))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 2, ls->tree().getValue(openvdb::Coord(14,10,10))); + + ASSERT_DOUBLES_EXACTLY_EQUAL( 2, ls->tree().getValue(openvdb::Coord(20,16,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 1, ls->tree().getValue(openvdb::Coord(20,17,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 0, ls->tree().getValue(openvdb::Coord(20,18,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-1, ls->tree().getValue(openvdb::Coord(20,19,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-2, ls->tree().getValue(openvdb::Coord(20,20,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(-1, ls->tree().getValue(openvdb::Coord(20,21,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 0, ls->tree().getValue(openvdb::Coord(20,22,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 1, ls->tree().getValue(openvdb::Coord(20,23,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL( 2, ls->tree().getValue(openvdb::Coord(20,24,20))); + + {// full but slow test of all voxels + openvdb::CoordBBox bbox = pa.getBBox(*ls); + bbox.expand(static_cast(halfWidth)+1); + openvdb::Index64 count = 0; + const float outside = ls->background(), inside = -outside; + const openvdb::Coord &min=bbox.min(), &max=bbox.max(); + for (openvdb::Coord ijk=min; ijk[0]indexToWorld(ijk.asVec3d()); + double dist = (xyz-pa.pos(0)).length()-pa.radius(0); + openvdb::Index32 k =0; + for (int i = 1, s = int(pa.size()); i < s; ++i) { + double d = (xyz-pa.pos(i)).length()-pa.radius(i); + if (dtree().getValue(ijk); + openvdb::Index32 m = id->tree().getValue(ijk); + if (dist >= outside) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(outside, val, 0.0001); + CPPUNIT_ASSERT(ls->tree().isValueOff(ijk)); + //CPPUNIT_ASSERT_EQUAL(openvdb::util::INVALID_IDX, m); + CPPUNIT_ASSERT(id->tree().isValueOff(ijk)); + } else if( dist <= inside ) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(inside, val, 0.0001); + CPPUNIT_ASSERT(ls->tree().isValueOff(ijk)); + //CPPUNIT_ASSERT_EQUAL(openvdb::util::INVALID_IDX, m); + CPPUNIT_ASSERT(id->tree().isValueOff(ijk)); + } else { + CPPUNIT_ASSERT_DOUBLES_EQUAL( dist, val, 0.0001); + CPPUNIT_ASSERT(ls->tree().isValueOn(ijk)); + CPPUNIT_ASSERT_EQUAL(k, m); + CPPUNIT_ASSERT(id->tree().isValueOn(ijk)); + ++count; + } + } + } + } + //std::cerr << "\nExpected active voxel count = " << count + // << ", actual active voxle count = " + // << ls->activeVoxelCount() << std::endl; + CPPUNIT_ASSERT_EQUAL(count, ls->activeVoxelCount()); + } +} + + +/// This is not really a conventional unit-test since the result of +/// the tests are written to a file and need to be visually verified! +void +TestParticlesToLevelSet::testRasterizeTrails() +{ + const float voxelSize = 1.0f, halfWidth = 2.0f; + openvdb::FloatGrid::Ptr ls = openvdb::createLevelSet(voxelSize, halfWidth); + + MyParticleList pa(1,5); + + // This particle radius = 1 < 1.5 i.e. it's below the Nyquist frequency and hence ignored + pa.add(openvdb::Vec3R( 0, 0, 0), 1, openvdb::Vec3R( 0, 1, 0)); + pa.add(openvdb::Vec3R(-10,-10,-10), 2, openvdb::Vec3R( 2, 0, 0)); + pa.add(openvdb::Vec3R( 10, 10, 10), 3, openvdb::Vec3R( 0, 1, 0)); + pa.add(openvdb::Vec3R( 0, 0, 0), 6, openvdb::Vec3R( 0, 0,-5)); + pa.add(openvdb::Vec3R( 20, 0, 0), 2, openvdb::Vec3R( 0, 0, 0)); + + openvdb::tools::ParticlesToLevelSet raster(*ls); + raster.rasterizeTrails(pa, 0.75);//scale offset between two instances + + //ls->tree().print(std::cout, 4); + //this->writeGrid(ls, "testRasterizeTrails"); +} + + +void +TestParticlesToLevelSet::testRasterizeTrailsAndId() +{ + MyParticleList pa(1,5); + + // This particle radius = 1 < 1.5 i.e. it's below the Nyquist frequency and hence ignored + pa.add(openvdb::Vec3R( 0, 0, 0), 1, openvdb::Vec3R( 0, 1, 0)); + pa.add(openvdb::Vec3R(-10,-10,-10), 2, openvdb::Vec3R( 2, 0, 0)); + pa.add(openvdb::Vec3R( 10, 10, 10), 3, openvdb::Vec3R( 0, 1, 0)); + pa.add(openvdb::Vec3R( 0, 0, 0), 6, openvdb::Vec3R( 0, 0,-5)); + + typedef openvdb::tools::ParticlesToLevelSet RasterT; + const float voxelSize = 1.0f, halfWidth = 2.0f; + openvdb::FloatGrid::Ptr ls = openvdb::createLevelSet(voxelSize, halfWidth); + RasterT raster(*ls); + raster.rasterizeTrails(pa, 0.75);//scale offset between two instances + raster.finalize(); + const RasterT::AttGridType::Ptr id = raster.attributeGrid(); + CPPUNIT_ASSERT(!ls->empty()); + CPPUNIT_ASSERT(!id->empty()); + CPPUNIT_ASSERT_EQUAL(ls->activeVoxelCount(),id->activeVoxelCount()); + + int min = std::numeric_limits::max(), max = -min; + for (RasterT::AttGridType::ValueOnCIter i=id->cbeginValueOn(); i; ++i) { + min = openvdb::math::Min(min, int(*i)); + max = openvdb::math::Max(max, int(*i)); + } + CPPUNIT_ASSERT_EQUAL(1, min);//first particle is ignored because of its small rdadius! + CPPUNIT_ASSERT_EQUAL(3, max); + + //ls->tree().print(std::cout, 4); + //this->writeGrid(ls, "testRasterizeTrails"); +} + + +void +TestParticlesToLevelSet::testMaskOutput() +{ + using namespace openvdb; + + using SdfGridType = FloatGrid; + using MaskGridType = MaskGrid; + + MyParticleList pa; + const Vec3R vel(10, 5, 1); + pa.add(Vec3R(84.7252, 85.7946, 84.4266), 11.8569, vel); + pa.add(Vec3R(47.9977, 81.2169, 47.7665), 5.45313, vel); + pa.add(Vec3R(87.0087, 14.0351, 95.7155), 7.36483, vel); + pa.add(Vec3R(75.8616, 53.7373, 58.202), 14.4127, vel); + pa.add(Vec3R(14.9675, 32.4141, 13.5218), 4.33101, vel); + pa.add(Vec3R(96.9809, 9.92804, 90.2349), 12.2613, vel); + pa.add(Vec3R(63.4274, 3.84254, 32.5047), 12.1566, vel); + pa.add(Vec3R(62.351, 47.4698, 41.4369), 11.637, vel); + pa.add(Vec3R(62.2846, 1.35716, 66.2527), 18.9914, vel); + pa.add(Vec3R(44.1711, 1.99877, 45.1159), 1.11429, vel); + + { + // Test variable-radius particles. + + // Rasterize into an SDF. + auto sdf = createLevelSet(); + tools::particlesToSdf(pa, *sdf); + + // Rasterize into a boolean mask. + auto mask = MaskGridType::create(); + tools::particlesToMask(pa, *mask); + + // Verify that the rasterized mask matches the interior of the SDF. + mask->tree().voxelizeActiveTiles(); + auto interior = tools::sdfInteriorMask(*sdf); + CPPUNIT_ASSERT(interior); + interior->tree().voxelizeActiveTiles(); + CPPUNIT_ASSERT_EQUAL(interior->activeVoxelCount(), mask->activeVoxelCount()); + interior->topologyDifference(*mask); + CPPUNIT_ASSERT_EQUAL(0, int(interior->activeVoxelCount())); + } + { + // Test fixed-radius particles. + + auto sdf = createLevelSet(); + tools::particlesToSdf(pa, *sdf, /*radius=*/10.0); + + auto mask = MaskGridType::create(); + tools::particlesToMask(pa, *mask, /*radius=*/10.0); + + mask->tree().voxelizeActiveTiles(); + auto interior = tools::sdfInteriorMask(*sdf); + CPPUNIT_ASSERT(interior); + interior->tree().voxelizeActiveTiles(); + CPPUNIT_ASSERT_EQUAL(interior->activeVoxelCount(), mask->activeVoxelCount()); + interior->topologyDifference(*mask); + CPPUNIT_ASSERT_EQUAL(0, int(interior->activeVoxelCount())); + } + { + // Test particle trails. + + auto sdf = createLevelSet(); + tools::particleTrailsToSdf(pa, *sdf); + + auto mask = MaskGridType::create(); + tools::particleTrailsToMask(pa, *mask); + + mask->tree().voxelizeActiveTiles(); + auto interior = tools::sdfInteriorMask(*sdf); + CPPUNIT_ASSERT(interior); + interior->tree().voxelizeActiveTiles(); + CPPUNIT_ASSERT_EQUAL(interior->activeVoxelCount(), mask->activeVoxelCount()); + interior->topologyDifference(*mask); + CPPUNIT_ASSERT_EQUAL(0, int(interior->activeVoxelCount())); + } + { + // Test attribute transfer. + + auto sdf = createLevelSet(); + tools::ParticlesToLevelSet p2sdf(*sdf); + p2sdf.rasterizeSpheres(pa); + p2sdf.finalize(/*prune=*/true); + const auto sdfAttr = p2sdf.attributeGrid(); + CPPUNIT_ASSERT(sdfAttr); + + auto mask = MaskGridType::create(); + tools::ParticlesToLevelSet p2mask(*mask); + p2mask.rasterizeSpheres(pa); + p2mask.finalize(/*prune=*/true); + const auto maskAttr = p2mask.attributeGrid(); + CPPUNIT_ASSERT(maskAttr); + + mask->tree().voxelizeActiveTiles(); + auto interior = tools::sdfInteriorMask(*sdf); + CPPUNIT_ASSERT(interior); + interior->tree().voxelizeActiveTiles(); + CPPUNIT_ASSERT_EQUAL(interior->activeVoxelCount(), mask->activeVoxelCount()); + interior->topologyDifference(*mask); + CPPUNIT_ASSERT_EQUAL(0, int(interior->activeVoxelCount())); + + // Verify that the mask- and SDF-generated attribute grids match. + auto sdfAcc = sdfAttr->getConstAccessor(); + auto maskAcc = maskAttr->getConstAccessor(); + for (auto it = interior->cbeginValueOn(); it; ++it) { + const auto& c = it.getCoord(); + CPPUNIT_ASSERT_EQUAL(sdfAcc.getValue(c), maskAcc.getValue(c)); + } + } +} diff --git a/openvdb/unittest/TestPointAdvect.cc b/openvdb/unittest/TestPointAdvect.cc new file mode 100644 index 00000000..e4742748 --- /dev/null +++ b/openvdb/unittest/TestPointAdvect.cc @@ -0,0 +1,462 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include "util.h" +#include +#include +#include +#include +#include +#include +#include // csgDifference +#include // createLevelSetBox +#include +#include +#include +#include + +using namespace openvdb; +using namespace openvdb::points; + +class TestPointAdvect: public CppUnit::TestCase +{ +public: + + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointAdvect); + CPPUNIT_TEST(testAdvect); + CPPUNIT_TEST(testZalesaksDisk); + CPPUNIT_TEST_SUITE_END(); + + void testAdvect(); + void testZalesaksDisk(); +}; // class TestPointAdvect + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointAdvect); + + +//////////////////////////////////////// + + +void +TestPointAdvect::testAdvect() +{ + // generate four points + + const float voxelSize = 1.0f; + std::vector positions = { + {5, 2, 3}, + {2, 4, 1}, + {50, 5, 1}, + {3, 20, 1}, + }; + + const PointAttributeVector pointList(positions); + + math::Transform::Ptr pointTransform(math::Transform::createLinearTransform(voxelSize)); + + auto pointIndexGrid = tools::createPointIndexGrid( + pointList, *pointTransform); + + auto points = createPointDataGrid( + *pointIndexGrid, pointList, *pointTransform); + + std::vector id; + id.push_back(0); + id.push_back(1); + id.push_back(2); + id.push_back(3); + + auto idAttributeType = TypedAttributeArray::attributeType(); + appendAttribute(points->tree(), "id", idAttributeType); + + // create a wrapper around the id vector + PointAttributeVector idWrapper(id); + + populateAttribute>( + points->tree(), pointIndexGrid->tree(), "id", idWrapper); + + // create "test" group which only contains third point + + appendGroup(points->tree(), "test"); + std::vector groups(positions.size(), 0); + groups[2] = 1; + setGroup(points->tree(), pointIndexGrid->tree(), groups, "test"); + + // create "test2" group which contains second and third point + + appendGroup(points->tree(), "test2"); + groups[1] = 1; + setGroup(points->tree(), pointIndexGrid->tree(), groups, "test2"); + + const Vec3s tolerance(1e-3f); + + // advect by velocity using all integration orders + + for (Index integrationOrder = 0; integrationOrder < 5; integrationOrder++) { + Vec3s velocityBackground(1.0, 2.0, 3.0); + double timeStep = 1.0; + int steps = 1; + auto velocity = Vec3SGrid::create(velocityBackground); // grid with background value only + + auto pointsToAdvect = points->deepCopy(); + const auto& transform = pointsToAdvect->transform(); + + advectPoints(*pointsToAdvect, *velocity, integrationOrder, timeStep, steps); + + for (auto leaf = pointsToAdvect->tree().beginLeaf(); leaf; ++leaf) { + AttributeHandle positionHandle(leaf->constAttributeArray("P")); + AttributeHandle idHandle(leaf->constAttributeArray("id")); + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + int theId = idHandle.get(*iter); + Vec3s position = transform.indexToWorld( + positionHandle.get(*iter) + iter.getCoord().asVec3d()); + Vec3s expectedPosition(positions[theId]); + if (integrationOrder > 0) expectedPosition += velocityBackground; + CPPUNIT_ASSERT(math::isApproxEqual(position, expectedPosition, tolerance)); + } + } + } + + // invalid advection scheme + + auto zeroVelocityGrid = Vec3SGrid::create(Vec3s(0)); + CPPUNIT_ASSERT_THROW(advectPoints(*points, *zeroVelocityGrid, 5, 1.0, 1), ValueError); + + { // advect varying dt and steps + Vec3s velocityBackground(1.0, 2.0, 3.0); + Index integrationOrder = 4; + double timeStep = 0.1; + int steps = 100; + auto velocity = Vec3SGrid::create(velocityBackground); // grid with background value only + + auto pointsToAdvect = points->deepCopy(); + const auto& transform = pointsToAdvect->transform(); + + advectPoints(*pointsToAdvect, *velocity, integrationOrder, timeStep, steps); + + for (auto leaf = pointsToAdvect->tree().beginLeaf(); leaf; ++leaf) { + AttributeHandle positionHandle(leaf->constAttributeArray("P")); + AttributeHandle idHandle(leaf->constAttributeArray("id")); + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + int theId = idHandle.get(*iter); + Vec3s position = transform.indexToWorld( + positionHandle.get(*iter) + iter.getCoord().asVec3d()); + Vec3s expectedPosition(positions[theId] + velocityBackground * 10.0f); + CPPUNIT_ASSERT(math::isApproxEqual(position, expectedPosition, tolerance)); + } + } + } + + { // perform filtered advection + Vec3s velocityBackground(1.0, 2.0, 3.0); + Index integrationOrder = 4; + double timeStep = 1.0; + int steps = 1; + auto velocity = Vec3SGrid::create(velocityBackground); // grid with background value only + + std::vector advectIncludeGroups; + std::vector advectExcludeGroups; + std::vector includeGroups; + std::vector excludeGroups; + + { // only advect points in "test" group + advectIncludeGroups.push_back("test"); + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter advectFilter( + advectIncludeGroups, advectExcludeGroups, leaf->attributeSet()); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + + auto pointsToAdvect = points->deepCopy(); + const auto& transform = pointsToAdvect->transform(); + + advectPoints(*pointsToAdvect, *velocity, integrationOrder, timeStep, steps, + advectFilter, filter); + + CPPUNIT_ASSERT_EQUAL(Index64(4), pointCount(pointsToAdvect->tree())); + + for (auto leafIter = pointsToAdvect->tree().beginLeaf(); leafIter; ++leafIter) { + AttributeHandle positionHandle(leafIter->constAttributeArray("P")); + AttributeHandle idHandle(leafIter->constAttributeArray("id")); + for (auto iter = leafIter->beginIndexOn(); iter; ++iter) { + int theId = idHandle.get(*iter); + Vec3s position = transform.indexToWorld( + positionHandle.get(*iter) + iter.getCoord().asVec3d()); + Vec3s expectedPosition(positions[theId]); + if (theId == 2) expectedPosition += velocityBackground; + CPPUNIT_ASSERT(math::isApproxEqual(position, expectedPosition, tolerance)); + } + } + + advectIncludeGroups.clear(); + } + + { // only keep points in "test" group + includeGroups.push_back("test"); + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter advectFilter( + advectIncludeGroups, advectExcludeGroups, leaf->attributeSet()); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + + auto pointsToAdvect = points->deepCopy(); + const auto& transform = pointsToAdvect->transform(); + + advectPoints(*pointsToAdvect, *velocity, integrationOrder, timeStep, steps, + advectFilter, filter); + + CPPUNIT_ASSERT_EQUAL(Index64(1), pointCount(pointsToAdvect->tree())); + + for (auto leafIter = pointsToAdvect->tree().beginLeaf(); leafIter; ++leafIter) { + AttributeHandle positionHandle(leafIter->constAttributeArray("P")); + AttributeHandle idHandle(leafIter->constAttributeArray("id")); + for (auto iter = leafIter->beginIndexOn(); iter; ++iter) { + int theId = idHandle.get(*iter); + Vec3s position = transform.indexToWorld( + positionHandle.get(*iter) + iter.getCoord().asVec3d()); + Vec3s expectedPosition(positions[theId]); + expectedPosition += velocityBackground; + CPPUNIT_ASSERT(math::isApproxEqual(position, expectedPosition, tolerance)); + } + } + + includeGroups.clear(); + } + + { // only advect points in "test2" group, delete points in "test" group + advectIncludeGroups.push_back("test2"); + excludeGroups.push_back("test"); + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter advectFilter( + advectIncludeGroups, advectExcludeGroups, leaf->attributeSet()); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + + auto pointsToAdvect = points->deepCopy(); + const auto& transform = pointsToAdvect->transform(); + + advectPoints(*pointsToAdvect, *velocity, integrationOrder, timeStep, steps, + advectFilter, filter); + + CPPUNIT_ASSERT_EQUAL(Index64(3), pointCount(pointsToAdvect->tree())); + + for (auto leafIter = pointsToAdvect->tree().beginLeaf(); leafIter; ++leafIter) { + AttributeHandle positionHandle(leafIter->constAttributeArray("P")); + AttributeHandle idHandle(leafIter->constAttributeArray("id")); + for (auto iter = leafIter->beginIndexOn(); iter; ++iter) { + int theId = idHandle.get(*iter); + Vec3s position = transform.indexToWorld( + positionHandle.get(*iter) + iter.getCoord().asVec3d()); + Vec3s expectedPosition(positions[theId]); + if (theId == 1) expectedPosition += velocityBackground; + CPPUNIT_ASSERT(math::isApproxEqual(position, expectedPosition, tolerance)); + } + } + + advectIncludeGroups.clear(); + excludeGroups.clear(); + } + + { // advect all points, caching disabled + auto pointsToAdvect = points->deepCopy(); + const auto& transform = pointsToAdvect->transform(); + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter advectFilter( + advectIncludeGroups, advectExcludeGroups, leaf->attributeSet()); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + + advectPoints(*pointsToAdvect, *velocity, integrationOrder, timeStep, steps, + advectFilter, filter, false); + + CPPUNIT_ASSERT_EQUAL(Index64(4), pointCount(pointsToAdvect->tree())); + + for (auto leafIter = pointsToAdvect->tree().beginLeaf(); leafIter; ++leafIter) { + AttributeHandle positionHandle(leafIter->constAttributeArray("P")); + AttributeHandle idHandle(leafIter->constAttributeArray("id")); + for (auto iter = leafIter->beginIndexOn(); iter; ++iter) { + int theId = idHandle.get(*iter); + Vec3s position = transform.indexToWorld( + positionHandle.get(*iter) + iter.getCoord().asVec3d()); + Vec3s expectedPosition(positions[theId]); + expectedPosition += velocityBackground; + CPPUNIT_ASSERT(math::isApproxEqual(position, expectedPosition, tolerance)); + } + } + } + + { // only advect points in "test2" group, delete points in "test" group, caching disabled + advectIncludeGroups.push_back("test2"); + excludeGroups.push_back("test"); + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter advectFilter( + advectIncludeGroups, advectExcludeGroups, leaf->attributeSet()); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + + auto pointsToAdvect = points->deepCopy(); + const auto& transform = pointsToAdvect->transform(); + + advectPoints(*pointsToAdvect, *velocity, integrationOrder, timeStep, steps, + advectFilter, filter, false); + + CPPUNIT_ASSERT_EQUAL(Index64(3), pointCount(pointsToAdvect->tree())); + + for (auto leafIter = pointsToAdvect->tree().beginLeaf(); leafIter; ++leafIter) { + AttributeHandle positionHandle(leafIter->constAttributeArray("P")); + AttributeHandle idHandle(leafIter->constAttributeArray("id")); + for (auto iter = leafIter->beginIndexOn(); iter; ++iter) { + int theId = idHandle.get(*iter); + Vec3s position = transform.indexToWorld( + positionHandle.get(*iter) + iter.getCoord().asVec3d()); + Vec3s expectedPosition(positions[theId]); + if (theId == 1) expectedPosition += velocityBackground; + CPPUNIT_ASSERT(math::isApproxEqual(position, expectedPosition, tolerance)); + } + } + + advectIncludeGroups.clear(); + excludeGroups.clear(); + } + } +} + + +void +TestPointAdvect::testZalesaksDisk() +{ + // advect a notched sphere known as Zalesak's disk in a rotational velocity field + + // build the level set sphere + + Vec3s center(0, 0, 0); + float radius = 10; + float voxelSize = 0.2f; + + auto zalesak = tools::createLevelSetSphere(radius, center, voxelSize); + + // create box for notch using width and depth relative to radius + + const math::Transform::Ptr xform = math::Transform::createLinearTransform(voxelSize); + + Vec3f min(center); + Vec3f max(center); + float notchWidth = 0.5f; + float notchDepth = 1.5f; + + min.x() -= (radius * notchWidth) / 2; + min.y() -= (radius * (notchDepth - 1)); + min.z() -= radius * 1.1f; + + max.x() += (radius * notchWidth) / 2; + max.y() += radius * 1.1f; + max.z() += radius * 1.1f; + + math::BBox bbox(min, max); + + auto notch = tools::createLevelSetBox(bbox, *xform); + + // subtract notch from the sphere + + tools::csgDifference(*zalesak, *notch); + + // scatter points inside the sphere + + auto points = points::denseUniformPointScatter(*zalesak, /*pointsPerVoxel=*/8); + + // append an integer "id" attribute + + auto idAttributeType = TypedAttributeArray::attributeType(); + appendAttribute(points->tree(), "id", idAttributeType); + + // populate it in serial based on iteration order + + int id = 0; + for (auto leaf = points->tree().beginLeaf(); leaf; ++leaf) { + AttributeWriteHandle handle(leaf->attributeArray("id")); + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + handle.set(*iter, id++); + } + } + + // copy grid into new grid for advecting + + auto pointsToAdvect = points->deepCopy(); + + // populate a velocity grid that rotates in X + + auto velocity = Vec3SGrid::create(Vec3s(0)); + velocity->setTransform(xform); + + CoordBBox activeBbox(zalesak->evalActiveVoxelBoundingBox()); + activeBbox.expand(5); + + velocity->denseFill(activeBbox, Vec3s(0)); + + for (auto leaf = velocity->tree().beginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->beginValueOn(); iter; ++iter) { + Vec3s position = xform->indexToWorld(iter.getCoord().asVec3d()); + Vec3s vel = (position.cross(Vec3s(0, 0, 1)) * 2.0f * M_PI) / 10.0f; + iter.setValue(vel); + } + } + + // extract original positions + + const Index count = Index(pointCount(points->constTree())); + + std::vector preAdvectPositions(count, Vec3f(0)); + + for (auto leaf = points->constTree().cbeginLeaf(); leaf; ++leaf) { + AttributeHandle idHandle(leaf->constAttributeArray("id")); + AttributeHandle posHandle(leaf->constAttributeArray("P")); + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + Vec3f position = posHandle.get(*iter) + iter.getCoord().asVec3d(); + preAdvectPositions[idHandle.get(*iter)] = Vec3f(xform->indexToWorld(position)); + } + } + + // advect points a half revolution + + points::advectPoints(*pointsToAdvect, *velocity, Index(4), 1.0, 5); + + // extract new positions + + std::vector postAdvectPositions(count, Vec3f(0)); + + for (auto leaf = pointsToAdvect->constTree().cbeginLeaf(); leaf; ++leaf) { + AttributeHandle idHandle(leaf->constAttributeArray("id")); + AttributeHandle posHandle(leaf->constAttributeArray("P")); + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + Vec3f position = posHandle.get(*iter) + iter.getCoord().asVec3d(); + postAdvectPositions[idHandle.get(*iter)] = Vec3f(xform->indexToWorld(position)); + } + } + + for (Index i = 0; i < count; i++) { + CPPUNIT_ASSERT(!math::isApproxEqual( + preAdvectPositions[i], postAdvectPositions[i], Vec3f(0.1))); + } + + // advect points another half revolution + + points::advectPoints(*pointsToAdvect, *velocity, Index(4), 1.0, 5); + + for (auto leaf = pointsToAdvect->constTree().cbeginLeaf(); leaf; ++leaf) { + AttributeHandle idHandle(leaf->constAttributeArray("id")); + AttributeHandle posHandle(leaf->constAttributeArray("P")); + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + Vec3f position = posHandle.get(*iter) + iter.getCoord().asVec3d(); + postAdvectPositions[idHandle.get(*iter)] = Vec3f(xform->indexToWorld(position)); + } + } + + for (Index i = 0; i < count; i++) { + CPPUNIT_ASSERT(math::isApproxEqual( + preAdvectPositions[i], postAdvectPositions[i], Vec3f(0.1))); + } +} diff --git a/openvdb/unittest/TestPointAttribute.cc b/openvdb/unittest/TestPointAttribute.cc new file mode 100644 index 00000000..b7801973 --- /dev/null +++ b/openvdb/unittest/TestPointAttribute.cc @@ -0,0 +1,433 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +using namespace openvdb; +using namespace openvdb::points; + +class TestPointAttribute: public CppUnit::TestCase +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointAttribute); + CPPUNIT_TEST(testAppendDrop); + CPPUNIT_TEST(testRename); + CPPUNIT_TEST(testBloscCompress); + + CPPUNIT_TEST_SUITE_END(); + + void testAppendDrop(); + void testRename(); + void testBloscCompress(); +}; // class TestPointAttribute + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointAttribute); + +//////////////////////////////////////// + + +void +TestPointAttribute::testAppendDrop() +{ + using AttributeI = TypedAttributeArray; + + std::vector positions{{1, 1, 1}, {1, 10, 1}, {10, 1, 1}, {10, 10, 1}}; + + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + PointDataGrid::Ptr grid = createPointDataGrid(positions, *transform); + PointDataTree& tree = grid->tree(); + + // check one leaf per point + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), Index32(4)); + + // retrieve first and last leaf attribute sets + + auto leafIter = tree.cbeginLeaf(); + const AttributeSet& attributeSet = leafIter->attributeSet(); + + ++leafIter; + ++leafIter; + ++leafIter; + + const AttributeSet& attributeSet4 = leafIter->attributeSet(); + + // check just one attribute exists (position) + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(1)); + + { // append an attribute, different initial values and collapse + appendAttribute(tree, "id"); + + CPPUNIT_ASSERT(tree.beginLeaf()->hasAttribute("id")); + + AttributeArray& array = tree.beginLeaf()->attributeArray("id"); + CPPUNIT_ASSERT(array.isUniform()); + CPPUNIT_ASSERT_EQUAL(AttributeI::cast(array).get(0), zeroVal()); + + dropAttribute(tree, "id"); + + appendAttribute(tree, "id", 10, /*stride*/1); + + CPPUNIT_ASSERT(tree.beginLeaf()->hasAttribute("id")); + + AttributeArray& array2 = tree.beginLeaf()->attributeArray("id"); + CPPUNIT_ASSERT(array2.isUniform()); + CPPUNIT_ASSERT_EQUAL(AttributeI::cast(array2).get(0), AttributeI::ValueType(10)); + + array2.expand(); + CPPUNIT_ASSERT(!array2.isUniform()); + + collapseAttribute(tree, "id", 50); + + AttributeArray& array3 = tree.beginLeaf()->attributeArray("id"); + CPPUNIT_ASSERT(array3.isUniform()); + CPPUNIT_ASSERT_EQUAL(AttributeI::cast(array3).get(0), AttributeI::ValueType(50)); + + dropAttribute(tree, "id"); + + appendAttribute(tree, "name", "test"); + + AttributeArray& array4 = tree.beginLeaf()->attributeArray("name"); + CPPUNIT_ASSERT(array4.isUniform()); + StringAttributeHandle handle(array4, attributeSet.descriptor().getMetadata()); + CPPUNIT_ASSERT_EQUAL(handle.get(0), Name("test")); + + dropAttribute(tree, "name"); + } + + { // append a strided attribute + appendAttribute(tree, "id", 0, /*stride=*/1); + + AttributeArray& array = tree.beginLeaf()->attributeArray("id"); + CPPUNIT_ASSERT_EQUAL(array.stride(), Index(1)); + + dropAttribute(tree, "id"); + + appendAttribute(tree, "id", 0, /*stride=*/10); + + CPPUNIT_ASSERT(tree.beginLeaf()->hasAttribute("id")); + + AttributeArray& array2 = tree.beginLeaf()->attributeArray("id"); + CPPUNIT_ASSERT_EQUAL(array2.stride(), Index(10)); + + dropAttribute(tree, "id"); + } + + { // append an attribute, check descriptors are as expected, default value test + appendAttribute(tree, "id", + /*uniformValue*/0, + /*stride=*/1, + /*constantStride=*/true, + /*defaultValue*/TypedMetadata(10).copy(), + /*hidden=*/false, /*transient=*/false); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(2)); + CPPUNIT_ASSERT(attributeSet.descriptor() == attributeSet4.descriptor()); + CPPUNIT_ASSERT(&attributeSet.descriptor() == &attributeSet4.descriptor()); + + CPPUNIT_ASSERT(attributeSet.descriptor().getMetadata()["default:id"]); + } + + { // append three attributes, check ordering is consistent with insertion + appendAttribute(tree, "test3"); + appendAttribute(tree, "test1"); + appendAttribute(tree, "test2"); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(5)); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("P"), size_t(0)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("id"), size_t(1)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("test3"), size_t(2)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("test1"), size_t(3)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("test2"), size_t(4)); + } + + { // drop an attribute by index, check ordering remains consistent + std::vector indices{2}; + + dropAttributes(tree, indices); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(4)); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("P"), size_t(0)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("id"), size_t(1)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("test1"), size_t(2)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("test2"), size_t(3)); + } + + { // drop attributes by index, check ordering remains consistent + std::vector indices{1, 3}; + + dropAttributes(tree, indices); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(2)); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("P"), size_t(0)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("test1"), size_t(1)); + } + + { // drop last non-position attribute + std::vector indices{1}; + + dropAttributes(tree, indices); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(1)); + } + + { // attempt (and fail) to drop position + std::vector indices{0}; + + CPPUNIT_ASSERT_THROW(dropAttributes(tree, indices), openvdb::KeyError); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(1)); + CPPUNIT_ASSERT(attributeSet.descriptor().find("P") != AttributeSet::INVALID_POS); + } + + { // add back previous attributes + appendAttribute(tree, "id"); + appendAttribute(tree, "test3"); + appendAttribute(tree, "test1"); + appendAttribute(tree, "test2"); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(5)); + } + + { // attempt (and fail) to drop non-existing attribute + std::vector names{"test1000"}; + + CPPUNIT_ASSERT_THROW(dropAttributes(tree, names), openvdb::KeyError); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(5)); + } + + { // drop by name + std::vector names{"test1", "test2"}; + + dropAttributes(tree, names); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(3)); + CPPUNIT_ASSERT(attributeSet.descriptor() == attributeSet4.descriptor()); + CPPUNIT_ASSERT(&attributeSet.descriptor() == &attributeSet4.descriptor()); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("P"), size_t(0)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("id"), size_t(1)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("test3"), size_t(2)); + } + + { // attempt (and fail) to drop position + std::vector names{"P"}; + + CPPUNIT_ASSERT_THROW(dropAttributes(tree, names), openvdb::KeyError); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(3)); + CPPUNIT_ASSERT(attributeSet.descriptor().find("P") != AttributeSet::INVALID_POS); + } + + { // drop one attribute by name + dropAttribute(tree, "test3"); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(2)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("P"), size_t(0)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("id"), size_t(1)); + } + + { // drop one attribute by id + dropAttribute(tree, 1); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().find("P"), size_t(0)); + } + + { // attempt to add an attribute with a name that already exists + appendAttribute(tree, "test3"); + CPPUNIT_ASSERT_THROW(appendAttribute(tree, "test3"), openvdb::KeyError); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(2)); + } + + { // attempt to add an attribute with an unregistered type (Vec2R) + CPPUNIT_ASSERT_THROW(appendAttribute(tree, "unregistered"), openvdb::KeyError); + } + + { // append attributes marked as hidden, transient, group and string + appendAttribute(tree, "testHidden", 0, + /*stride=*/1, /*constantStride=*/true, Metadata::Ptr(), true, false); + appendAttribute(tree, "testTransient", 0, + /*stride=*/1, /*constantStride=*/true, Metadata::Ptr(), false, true); + appendAttribute(tree, "testString", "", + /*stride=*/1, /*constantStride=*/true, Metadata::Ptr(), false, false); + + const AttributeArray& arrayHidden = leafIter->attributeArray("testHidden"); + const AttributeArray& arrayTransient = leafIter->attributeArray("testTransient"); + const AttributeArray& arrayString = leafIter->attributeArray("testString"); + + CPPUNIT_ASSERT(arrayHidden.isHidden()); + CPPUNIT_ASSERT(!arrayTransient.isHidden()); + + CPPUNIT_ASSERT(!arrayHidden.isTransient()); + CPPUNIT_ASSERT(arrayTransient.isTransient()); + CPPUNIT_ASSERT(!arrayString.isTransient()); + + CPPUNIT_ASSERT(!isGroup(arrayHidden)); + CPPUNIT_ASSERT(!isGroup(arrayTransient)); + CPPUNIT_ASSERT(!isGroup(arrayString)); + + CPPUNIT_ASSERT(!isString(arrayHidden)); + CPPUNIT_ASSERT(!isString(arrayTransient)); + CPPUNIT_ASSERT(isString(arrayString)); + } + + { // collapsing non-existing attribute throws exception + CPPUNIT_ASSERT_THROW(collapseAttribute(tree, "unknown", 0), openvdb::KeyError); + CPPUNIT_ASSERT_THROW(collapseAttribute(tree, "unknown", "unknown"), openvdb::KeyError); + } +} + +void +TestPointAttribute::testRename() +{ + std::vector positions{{1, 1, 1}, {1, 10, 1}, {10, 1, 1}, {10, 10, 1}}; + + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + PointDataGrid::Ptr grid = createPointDataGrid(positions, *transform); + PointDataTree& tree = grid->tree(); + + // check one leaf per point + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), Index32(4)); + + const openvdb::TypedMetadata defaultValue(5.0f); + + appendAttribute(tree, "test1", 0, + /*stride=*/1, /*constantStride=*/true, defaultValue.copy()); + appendAttribute(tree, "id"); + appendAttribute(tree, "test2"); + + // retrieve first and last leaf attribute sets + + auto leafIter = tree.cbeginLeaf(); + const AttributeSet& attributeSet = leafIter->attributeSet(); + ++leafIter; + const AttributeSet& attributeSet4 = leafIter->attributeSet(); + + { // rename one attribute + renameAttribute(tree, "test1", "test1renamed"); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().size(), size_t(4)); + CPPUNIT_ASSERT(attributeSet.descriptor().find("test1") == AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(attributeSet.descriptor().find("test1renamed") != AttributeSet::INVALID_POS); + + CPPUNIT_ASSERT_EQUAL(attributeSet4.descriptor().size(), size_t(4)); + CPPUNIT_ASSERT(attributeSet4.descriptor().find("test1") == AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(attributeSet4.descriptor().find("test1renamed") != AttributeSet::INVALID_POS); + + renameAttribute(tree, "test1renamed", "test1"); + } + + { // rename non-existing, matching and existing attributes + CPPUNIT_ASSERT_THROW(renameAttribute(tree, "nonexist", "newname"), openvdb::KeyError); + CPPUNIT_ASSERT_THROW(renameAttribute(tree, "test1", "test1"), openvdb::KeyError); + CPPUNIT_ASSERT_THROW(renameAttribute(tree, "test2", "test1"), openvdb::KeyError); + } + + { // rename multiple attributes + std::vector oldNames{"test1", "test2"}; + std::vector newNames{"test1renamed"}; + + CPPUNIT_ASSERT_THROW(renameAttributes(tree, oldNames, newNames), openvdb::ValueError); + + newNames.push_back("test2renamed"); + renameAttributes(tree, oldNames, newNames); + + renameAttribute(tree, "test1renamed", "test1"); + renameAttribute(tree, "test2renamed", "test2"); + } + + { // rename an attribute with a default value + CPPUNIT_ASSERT(attributeSet.descriptor().hasDefaultValue("test1")); + + renameAttribute(tree, "test1", "test1renamed"); + + CPPUNIT_ASSERT(attributeSet.descriptor().hasDefaultValue("test1renamed")); + } +} + +void +TestPointAttribute::testBloscCompress() +{ + std::vector positions; + for (float i = 1.f; i < 6.f; i += 0.1f) { + positions.emplace_back(1, i, 1); + positions.emplace_back(1, 1, i); + positions.emplace_back(10, i, 1); + positions.emplace_back(10, 1, i); + } + + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + PointDataGrid::Ptr grid = createPointDataGrid(positions, *transform); + PointDataTree& tree = grid->tree(); + + // check two leaves + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), Index32(2)); + + // retrieve first and last leaf attribute sets + + auto leafIter = tree.beginLeaf(); + auto leafIter2 = ++tree.beginLeaf(); + + { // append an attribute, check descriptors are as expected + appendAttribute(tree, "compact"); + appendAttribute(tree, "id"); + appendAttribute(tree, "id2"); + } + + using AttributeHandleRWI = AttributeWriteHandle; + + { // set some id values (leaf 1) + AttributeHandleRWI handleCompact(leafIter->attributeArray("compact")); + AttributeHandleRWI handleId(leafIter->attributeArray("id")); + AttributeHandleRWI handleId2(leafIter->attributeArray("id2")); + + const int size = leafIter->attributeArray("id").size(); + + CPPUNIT_ASSERT_EQUAL(size, 102); + + for (int i = 0; i < size; i++) { + handleCompact.set(i, 5); + handleId.set(i, i); + handleId2.set(i, i); + } + } + + { // set some id values (leaf 2) + AttributeHandleRWI handleCompact(leafIter2->attributeArray("compact")); + AttributeHandleRWI handleId(leafIter2->attributeArray("id")); + AttributeHandleRWI handleId2(leafIter2->attributeArray("id2")); + + const int size = leafIter2->attributeArray("id").size(); + + CPPUNIT_ASSERT_EQUAL(size, 102); + + for (int i = 0; i < size; i++) { + handleCompact.set(i, 10); + handleId.set(i, i); + handleId2.set(i, i); + } + } + + compactAttributes(tree); + + CPPUNIT_ASSERT(leafIter->attributeArray("compact").isUniform()); + CPPUNIT_ASSERT(leafIter2->attributeArray("compact").isUniform()); +} diff --git a/openvdb/unittest/TestPointConversion.cc b/openvdb/unittest/TestPointConversion.cc new file mode 100644 index 00000000..ef359ee2 --- /dev/null +++ b/openvdb/unittest/TestPointConversion.cc @@ -0,0 +1,1280 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#include +#endif + +using namespace openvdb; +using namespace openvdb::points; + +class TestPointConversion: public CppUnit::TestCase +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointConversion); + CPPUNIT_TEST(testPointConversion); + CPPUNIT_TEST(testPointConversionNans); + CPPUNIT_TEST(testStride); + CPPUNIT_TEST(testComputeVoxelSize); + CPPUNIT_TEST(testPrecision); + + CPPUNIT_TEST_SUITE_END(); + + void testPointConversion(); + void testPointConversionNans(); + void testStride(); + void testComputeVoxelSize(); + void testPrecision(); + +}; // class TestPointConversion + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointConversion); + + +// Simple Attribute Wrapper +template +struct AttributeWrapper +{ + using ValueType = T; + using PosType = T; + using value_type = T; + + struct Handle + { + Handle(AttributeWrapper& attribute) + : mBuffer(attribute.mAttribute) + , mStride(attribute.mStride) { } + + template + void set(size_t n, openvdb::Index m, const ValueType& value) { + mBuffer[n * mStride + m] = static_cast(value); + } + + template + void set(size_t n, openvdb::Index m, const openvdb::math::Vec3& value) { + mBuffer[n * mStride + m] = static_cast(value); + } + + private: + std::vector& mBuffer; + Index mStride; + }; // struct Handle + + explicit AttributeWrapper(const Index stride) : mStride(stride) { } + + void expand() { } + void compact() { } + + void resize(const size_t n) { mAttribute.resize(n); } + size_t size() const { return mAttribute.size(); } + + std::vector& buffer() { return mAttribute; } + + template + void get(ValueT& value, size_t n, openvdb::Index m = 0) const { value = mAttribute[n * mStride + m]; } + template + void getPos(size_t n, ValueT& value) const { this->get(value, n); } + +private: + std::vector mAttribute; + Index mStride; +}; // struct AttributeWrapper + + +struct GroupWrapper +{ + GroupWrapper() = default; + + void setOffsetOn(openvdb::Index index) { + mGroup[index] = short(1); + } + + void finalize() { } + + void resize(const size_t n) { mGroup.resize(n, short(0)); } + size_t size() const { return mGroup.size(); } + + std::vector& buffer() { return mGroup; } + +private: + std::vector mGroup; +}; // struct GroupWrapper + + +struct PointData +{ + int id; + Vec3f position; + Vec3i xyz; + float uniform; + openvdb::Name string; + short group; + + bool operator<(const PointData& other) const { return id < other.id; } +}; // PointData + + +// Generate random points by uniformly distributing points +// on a unit-sphere. +inline void +genPoints(const int numPoints, const double scale, const bool stride, + AttributeWrapper& position, + AttributeWrapper& xyz, + AttributeWrapper& id, + AttributeWrapper& uniform, + AttributeWrapper& string, + GroupWrapper& group) +{ + // init + openvdb::math::Random01 randNumber(0); + const int n = int(std::sqrt(double(numPoints))); + const double xScale = (2.0 * M_PI) / double(n); + const double yScale = M_PI / double(n); + + double x, y, theta, phi; + openvdb::Vec3f pos; + + position.resize(n*n); + xyz.resize(stride ? n*n*3 : 1); + id.resize(n*n); + uniform.resize(n*n); + string.resize(n*n); + group.resize(n*n); + + AttributeWrapper::Handle positionHandle(position); + AttributeWrapper::Handle xyzHandle(xyz); + AttributeWrapper::Handle idHandle(id); + AttributeWrapper::Handle uniformHandle(uniform); + AttributeWrapper::Handle stringHandle(string); + + int i = 0; + + // loop over a [0 to n) x [0 to n) grid. + for (int a = 0; a < n; ++a) { + for (int b = 0; b < n; ++b) { + + // jitter, move to random pos. inside the current cell + x = double(a) + randNumber(); + y = double(b) + randNumber(); + + // remap to a lat/long map + theta = y * yScale; // [0 to PI] + phi = x * xScale; // [0 to 2PI] + + // convert to cartesian coordinates on a unit sphere. + // spherical coordinate triplet (r=1, theta, phi) + pos[0] = static_cast(std::sin(theta)*std::cos(phi)*scale); + pos[1] = static_cast(std::sin(theta)*std::sin(phi)*scale); + pos[2] = static_cast(std::cos(theta)*scale); + + positionHandle.set(i, /*stride*/0, pos); + idHandle.set(i, /*stride*/0, i); + uniformHandle.set(i, /*stride*/0, 100.0f); + + if (stride) + { + xyzHandle.set(i, 0, i); + xyzHandle.set(i, 1, i*i); + xyzHandle.set(i, 2, i*i*i); + } + + // add points with even id to the group + if ((i % 2) == 0) { + group.setOffsetOn(i); + stringHandle.set(i, /*stride*/0, "testA"); + } + else { + stringHandle.set(i, /*stride*/0, "testB"); + } + + i++; + } + } +} + + +//////////////////////////////////////// + + +void +TestPointConversion::testPointConversion() +{ + // generate points + + const size_t count(1000000); + + AttributeWrapper position(1); + AttributeWrapper xyz(1); + AttributeWrapper id(1); + AttributeWrapper uniform(1); + AttributeWrapper string(1); + GroupWrapper group; + + genPoints(count, /*scale=*/ 100.0, /*stride=*/false, + position, xyz, id, uniform, string, group); + + CPPUNIT_ASSERT_EQUAL(position.size(), count); + CPPUNIT_ASSERT_EQUAL(id.size(), count); + CPPUNIT_ASSERT_EQUAL(uniform.size(), count); + CPPUNIT_ASSERT_EQUAL(string.size(), count); + CPPUNIT_ASSERT_EQUAL(group.size(), count); + + // convert point positions into a Point Data Grid + + const float voxelSize = 1.0f; + openvdb::math::Transform::Ptr transform(openvdb::math::Transform::createLinearTransform(voxelSize)); + + tools::PointIndexGrid::Ptr pointIndexGrid = tools::createPointIndexGrid(position, *transform); + PointDataGrid::Ptr pointDataGrid = createPointDataGrid(*pointIndexGrid, position, *transform); + + tools::PointIndexTree& indexTree = pointIndexGrid->tree(); + PointDataTree& tree = pointDataGrid->tree(); + + // add id and populate + + appendAttribute(tree, "id"); + populateAttribute>(tree, indexTree, "id", id); + + // add uniform and populate + + appendAttribute(tree, "uniform"); + populateAttribute>(tree, indexTree, "uniform", uniform); + + // add string and populate + + appendAttribute(tree, "string"); + + // reset the descriptors + PointDataTree::LeafIter leafIter = tree.beginLeaf(); + const AttributeSet::Descriptor& descriptor = leafIter->attributeSet().descriptor(); + auto newDescriptor = std::make_shared(descriptor); + for (; leafIter; ++leafIter) { + leafIter->resetDescriptor(newDescriptor); + } + + populateAttribute>( + tree, indexTree, "string", string); + + // add group and set membership + + appendGroup(tree, "test"); + setGroup(tree, indexTree, group.buffer(), "test"); + + CPPUNIT_ASSERT_EQUAL(indexTree.leafCount(), tree.leafCount()); + + // read/write grid to a temp file + + std::string tempDir; + if (const char* dir = std::getenv("TMPDIR")) tempDir = dir; +#ifdef _MSC_VER + if (tempDir.empty()) { + char tempDirBuffer[MAX_PATH+1]; + int tempDirLen = GetTempPath(MAX_PATH+1, tempDirBuffer); + CPPUNIT_ASSERT(tempDirLen > 0 && tempDirLen <= MAX_PATH); + tempDir = tempDirBuffer; + } +#else + if (tempDir.empty()) tempDir = P_tmpdir; +#endif + + std::string filename = tempDir + "/openvdb_test_point_conversion"; + + io::File fileOut(filename); + + GridCPtrVec grids; + grids.push_back(pointDataGrid); + + fileOut.write(grids); + + fileOut.close(); + + io::File fileIn(filename); + fileIn.open(); + + GridPtrVecPtr readGrids = fileIn.getGrids(); + + fileIn.close(); + + CPPUNIT_ASSERT_EQUAL(readGrids->size(), size_t(1)); + + pointDataGrid = GridBase::grid((*readGrids)[0]); + PointDataTree& inputTree = pointDataGrid->tree(); + + // create accessor and iterator for Point Data Tree + + PointDataTree::LeafCIter leafCIter = inputTree.cbeginLeaf(); + + CPPUNIT_ASSERT_EQUAL(5, int(leafCIter->attributeSet().size())); + + CPPUNIT_ASSERT(leafCIter->attributeSet().find("id") != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(leafCIter->attributeSet().find("uniform") != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(leafCIter->attributeSet().find("P") != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(leafCIter->attributeSet().find("string") != AttributeSet::INVALID_POS); + + const auto idIndex = static_cast(leafCIter->attributeSet().find("id")); + const auto uniformIndex = static_cast(leafCIter->attributeSet().find("uniform")); + const auto stringIndex = static_cast(leafCIter->attributeSet().find("string")); + const AttributeSet::Descriptor::GroupIndex groupIndex = + leafCIter->attributeSet().groupIndex("test"); + + // convert back into linear point attribute data + + AttributeWrapper outputPosition(1); + AttributeWrapper outputId(1); + AttributeWrapper outputUniform(1); + AttributeWrapper outputString(1); + GroupWrapper outputGroup; + + // test offset the whole point block by an arbitrary amount + + Index64 startOffset = 10; + + outputPosition.resize(startOffset + position.size()); + outputId.resize(startOffset + id.size()); + outputUniform.resize(startOffset + uniform.size()); + outputString.resize(startOffset + string.size()); + outputGroup.resize(startOffset + group.size()); + + std::vector includeGroups; + std::vector excludeGroups; + + std::vector offsets; + MultiGroupFilter filter(includeGroups, excludeGroups, inputTree.cbeginLeaf()->attributeSet()); + pointOffsets(offsets, inputTree, filter); + + convertPointDataGridPosition(outputPosition, *pointDataGrid, offsets, startOffset, filter); + convertPointDataGridAttribute(outputId, inputTree, offsets, startOffset, idIndex, 1, filter); + convertPointDataGridAttribute(outputUniform, inputTree, offsets, startOffset, uniformIndex, 1, filter); + convertPointDataGridAttribute(outputString, inputTree, offsets, startOffset, stringIndex, 1, filter); + convertPointDataGridGroup(outputGroup, inputTree, offsets, startOffset, groupIndex, filter); + + // pack and sort the new buffers based on id + + std::vector pointData(count); + + for (unsigned int i = 0; i < count; i++) { + pointData[i].id = outputId.buffer()[startOffset + i]; + pointData[i].position = outputPosition.buffer()[startOffset + i]; + pointData[i].uniform = outputUniform.buffer()[startOffset + i]; + pointData[i].string = outputString.buffer()[startOffset + i]; + pointData[i].group = outputGroup.buffer()[startOffset + i]; + } + + std::sort(pointData.begin(), pointData.end()); + + // compare old and new buffers + + for (unsigned int i = 0; i < count; i++) + { + CPPUNIT_ASSERT_EQUAL(id.buffer()[i], pointData[i].id); + CPPUNIT_ASSERT_EQUAL(group.buffer()[i], pointData[i].group); + CPPUNIT_ASSERT_EQUAL(uniform.buffer()[i], pointData[i].uniform); + CPPUNIT_ASSERT_EQUAL(string.buffer()[i], pointData[i].string); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[i].x(), pointData[i].position.x(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[i].y(), pointData[i].position.y(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[i].z(), pointData[i].position.z(), /*tolerance=*/1e-6); + } + + // convert based on even group + + const size_t halfCount = count / 2; + + outputPosition.resize(startOffset + halfCount); + outputId.resize(startOffset + halfCount); + outputUniform.resize(startOffset + halfCount); + outputString.resize(startOffset + halfCount); + outputGroup.resize(startOffset + halfCount); + + includeGroups.push_back("test"); + + offsets.clear(); + MultiGroupFilter filter2(includeGroups, excludeGroups, inputTree.cbeginLeaf()->attributeSet()); + pointOffsets(offsets, inputTree, filter2); + + convertPointDataGridPosition(outputPosition, *pointDataGrid, offsets, startOffset, filter2); + convertPointDataGridAttribute(outputId, inputTree, offsets, startOffset, idIndex, /*stride*/1, filter2); + convertPointDataGridAttribute(outputUniform, inputTree, offsets, startOffset, uniformIndex, /*stride*/1, filter2); + convertPointDataGridAttribute(outputString, inputTree, offsets, startOffset, stringIndex, /*stride*/1, filter2); + convertPointDataGridGroup(outputGroup, inputTree, offsets, startOffset, groupIndex, filter2); + + CPPUNIT_ASSERT_EQUAL(size_t(outputPosition.size() - startOffset), size_t(halfCount)); + CPPUNIT_ASSERT_EQUAL(size_t(outputId.size() - startOffset), size_t(halfCount)); + CPPUNIT_ASSERT_EQUAL(size_t(outputUniform.size() - startOffset), size_t(halfCount)); + CPPUNIT_ASSERT_EQUAL(size_t(outputString.size() - startOffset), size_t(halfCount)); + CPPUNIT_ASSERT_EQUAL(size_t(outputGroup.size() - startOffset), size_t(halfCount)); + + pointData.clear(); + + for (unsigned int i = 0; i < halfCount; i++) { + PointData data; + data.id = outputId.buffer()[startOffset + i]; + data.position = outputPosition.buffer()[startOffset + i]; + data.uniform = outputUniform.buffer()[startOffset + i]; + data.string = outputString.buffer()[startOffset + i]; + data.group = outputGroup.buffer()[startOffset + i]; + pointData.push_back(data); + } + + std::sort(pointData.begin(), pointData.end()); + + // compare old and new buffers + + for (unsigned int i = 0; i < halfCount; i++) + { + CPPUNIT_ASSERT_EQUAL(id.buffer()[i*2], pointData[i].id); + CPPUNIT_ASSERT_EQUAL(group.buffer()[i*2], pointData[i].group); + CPPUNIT_ASSERT_EQUAL(uniform.buffer()[i*2], pointData[i].uniform); + CPPUNIT_ASSERT_EQUAL(string.buffer()[i*2], pointData[i].string); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[i*2].x(), pointData[i].position.x(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[i*2].y(), pointData[i].position.y(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[i*2].z(), pointData[i].position.z(), /*tolerance=*/1e-6); + } + + std::remove(filename.c_str()); +} + + +//////////////////////////////////////// + + +void +TestPointConversion::testPointConversionNans() +{ + // generate points + + const size_t count(25); + + AttributeWrapper position(1); + AttributeWrapper xyz(1); + AttributeWrapper id(1); + AttributeWrapper uniform(1); + AttributeWrapper string(1); + GroupWrapper group; + + genPoints(count, /*scale=*/ 1.0, /*stride=*/false, + position, xyz, id, uniform, string, group); + + // set point numbers 0, 10, 20 and 24 to a nan position + + const std::vector nanIndices = { 0, 10, 20, 24 }; + + AttributeWrapper::Handle positionHandle(position); + const Vec3f nanPos(std::nan("0")); + CPPUNIT_ASSERT(nanPos.isNan()); + for (const int& idx : nanIndices) { + positionHandle.set(idx, /*stride*/0, nanPos); + } + + CPPUNIT_ASSERT_EQUAL(count, position.size()); + CPPUNIT_ASSERT_EQUAL(count, id.size()); + CPPUNIT_ASSERT_EQUAL(count, uniform.size()); + CPPUNIT_ASSERT_EQUAL(count, string.size()); + CPPUNIT_ASSERT_EQUAL(count, group.size()); + + // convert point positions into a Point Data Grid + + openvdb::math::Transform::Ptr transform = + openvdb::math::Transform::createLinearTransform(/*voxelsize*/1.0f); + + tools::PointIndexGrid::Ptr pointIndexGrid = tools::createPointIndexGrid(position, *transform); + PointDataGrid::Ptr pointDataGrid = createPointDataGrid(*pointIndexGrid, position, *transform); + + tools::PointIndexTree& indexTree = pointIndexGrid->tree(); + PointDataTree& tree = pointDataGrid->tree(); + + // set expected point count to the total minus the number of nan positions + const size_t expected = count - nanIndices.size(); + CPPUNIT_ASSERT_EQUAL(expected, static_cast(pointCount(tree))); + + // add id and populate + + appendAttribute(tree, "id"); + populateAttribute>(tree, indexTree, "id", id); + + // add uniform and populate + + appendAttribute(tree, "uniform"); + populateAttribute>(tree, indexTree, "uniform", uniform); + + // add string and populate + + appendAttribute(tree, "string"); + populateAttribute>( + tree, indexTree, "string", string); + + // add group and set membership + + appendGroup(tree, "test"); + setGroup(tree, indexTree, group.buffer(), "test"); + + // create accessor and iterator for Point Data Tree + + const auto leafCIter = tree.cbeginLeaf(); + + CPPUNIT_ASSERT_EQUAL(5, int(leafCIter->attributeSet().size())); + + CPPUNIT_ASSERT(leafCIter->attributeSet().find("id") != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(leafCIter->attributeSet().find("uniform") != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(leafCIter->attributeSet().find("P") != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(leafCIter->attributeSet().find("string") != AttributeSet::INVALID_POS); + + const auto idIndex = static_cast(leafCIter->attributeSet().find("id")); + const auto uniformIndex = static_cast(leafCIter->attributeSet().find("uniform")); + const auto stringIndex = static_cast(leafCIter->attributeSet().find("string")); + const AttributeSet::Descriptor::GroupIndex groupIndex = + leafCIter->attributeSet().groupIndex("test"); + + // convert back into linear point attribute data + + AttributeWrapper outputPosition(1); + AttributeWrapper outputId(1); + AttributeWrapper outputUniform(1); + AttributeWrapper outputString(1); + GroupWrapper outputGroup; + + outputPosition.resize(position.size()); + outputId.resize(id.size()); + outputUniform.resize(uniform.size()); + outputString.resize(string.size()); + outputGroup.resize(group.size()); + + std::vector offsets; + pointOffsets(offsets, tree); + + convertPointDataGridPosition(outputPosition, *pointDataGrid, offsets, 0); + convertPointDataGridAttribute(outputId, tree, offsets, 0, idIndex, 1); + convertPointDataGridAttribute(outputUniform, tree, offsets, 0, uniformIndex, 1); + convertPointDataGridAttribute(outputString, tree, offsets, 0, stringIndex, 1); + convertPointDataGridGroup(outputGroup, tree, offsets, 0, groupIndex); + + // pack and sort the new buffers based on id + + std::vector pointData(expected); + + for (unsigned int i = 0; i < expected; i++) { + pointData[i].id = outputId.buffer()[i]; + pointData[i].position = outputPosition.buffer()[i]; + pointData[i].uniform = outputUniform.buffer()[i]; + pointData[i].string = outputString.buffer()[i]; + pointData[i].group = outputGroup.buffer()[i]; + } + + std::sort(pointData.begin(), pointData.end()); + + // compare old and new buffers, taking into account the nan position + // which should not have been converted + + for (unsigned int i = 0; i < expected; ++i) + { + size_t iOffset = i; + for (const int& idx : nanIndices) { + if (int(iOffset) >= idx) iOffset += 1; + } + + CPPUNIT_ASSERT_EQUAL(id.buffer()[iOffset], pointData[i].id); + CPPUNIT_ASSERT_EQUAL(group.buffer()[iOffset], pointData[i].group); + CPPUNIT_ASSERT_EQUAL(uniform.buffer()[iOffset], pointData[i].uniform); + CPPUNIT_ASSERT_EQUAL(string.buffer()[iOffset], pointData[i].string); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[iOffset].x(), pointData[i].position.x(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[iOffset].y(), pointData[i].position.y(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[iOffset].z(), pointData[i].position.z(), /*tolerance=*/1e-6); + } +} + + +//////////////////////////////////////// + + +void +TestPointConversion::testStride() +{ + // generate points + + const size_t count(40000); + + AttributeWrapper position(1); + AttributeWrapper xyz(3); + AttributeWrapper id(1); + AttributeWrapper uniform(1); + AttributeWrapper string(1); + GroupWrapper group; + + genPoints(count, /*scale=*/ 100.0, /*stride=*/true, + position, xyz, id, uniform, string, group); + + CPPUNIT_ASSERT_EQUAL(position.size(), count); + CPPUNIT_ASSERT_EQUAL(xyz.size(), count*3); + CPPUNIT_ASSERT_EQUAL(id.size(), count); + + // convert point positions into a Point Data Grid + + const float voxelSize = 1.0f; + openvdb::math::Transform::Ptr transform(openvdb::math::Transform::createLinearTransform(voxelSize)); + + tools::PointIndexGrid::Ptr pointIndexGrid = tools::createPointIndexGrid(position, *transform); + PointDataGrid::Ptr pointDataGrid = createPointDataGrid(*pointIndexGrid, position, *transform); + + tools::PointIndexTree& indexTree = pointIndexGrid->tree(); + PointDataTree& tree = pointDataGrid->tree(); + + // add id and populate + + appendAttribute(tree, "id"); + populateAttribute>(tree, indexTree, "id", id); + + // add xyz and populate + + appendAttribute(tree, "xyz", 0, /*stride=*/3); + populateAttribute>(tree, indexTree, "xyz", xyz, /*stride=*/3); + + // create accessor and iterator for Point Data Tree + + PointDataTree::LeafCIter leafCIter = tree.cbeginLeaf(); + + CPPUNIT_ASSERT_EQUAL(3, int(leafCIter->attributeSet().size())); + + CPPUNIT_ASSERT(leafCIter->attributeSet().find("id") != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(leafCIter->attributeSet().find("P") != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(leafCIter->attributeSet().find("xyz") != AttributeSet::INVALID_POS); + + const auto idIndex = static_cast(leafCIter->attributeSet().find("id")); + const auto xyzIndex = static_cast(leafCIter->attributeSet().find("xyz")); + + // convert back into linear point attribute data + + AttributeWrapper outputPosition(1); + AttributeWrapper outputXyz(3); + AttributeWrapper outputId(1); + + // test offset the whole point block by an arbitrary amount + + Index64 startOffset = 10; + + outputPosition.resize(startOffset + position.size()); + outputXyz.resize((startOffset + id.size())*3); + outputId.resize(startOffset + id.size()); + + std::vector offsets; + pointOffsets(offsets, tree); + + convertPointDataGridPosition(outputPosition, *pointDataGrid, offsets, startOffset); + convertPointDataGridAttribute(outputId, tree, offsets, startOffset, idIndex); + convertPointDataGridAttribute(outputXyz, tree, offsets, startOffset, xyzIndex, /*stride=*/3); + + // pack and sort the new buffers based on id + + std::vector pointData; + + pointData.resize(count); + + for (unsigned int i = 0; i < count; i++) { + pointData[i].id = outputId.buffer()[startOffset + i]; + pointData[i].position = outputPosition.buffer()[startOffset + i]; + for (unsigned int j = 0; j < 3; j++) { + pointData[i].xyz[j] = outputXyz.buffer()[startOffset * 3 + i * 3 + j]; + } + } + + std::sort(pointData.begin(), pointData.end()); + + // compare old and new buffers + + for (unsigned int i = 0; i < count; i++) + { + CPPUNIT_ASSERT_EQUAL(id.buffer()[i], pointData[i].id); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[i].x(), pointData[i].position.x(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[i].y(), pointData[i].position.y(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(position.buffer()[i].z(), pointData[i].position.z(), /*tolerance=*/1e-6); + CPPUNIT_ASSERT_EQUAL(Vec3i(xyz.buffer()[i*3], xyz.buffer()[i*3+1], xyz.buffer()[i*3+2]), pointData[i].xyz); + } +} + + +//////////////////////////////////////// + + +void +TestPointConversion::testComputeVoxelSize() +{ + struct Local { + + static PointDataGrid::Ptr genPointsGrid(const float voxelSize, const AttributeWrapper& positions) + { + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + tools::PointIndexGrid::Ptr pointIndexGrid = tools::createPointIndexGrid(positions, *transform); + return createPointDataGrid(*pointIndexGrid, positions, *transform); + } + }; + + // minimum and maximum voxel sizes + + const auto minimumVoxelSize = static_cast(math::Pow(double(3e-15), 1.0/3.0)); + const auto maximumVoxelSize = + static_cast(math::Pow(double(std::numeric_limits::max()), 1.0/3.0)); + + AttributeWrapper position(/*stride*/1); + AttributeWrapper positionD(/*stride*/1); + + // test with no positions + + { + const float voxelSize = computeVoxelSize(position, /*points per voxel*/8); + CPPUNIT_ASSERT_EQUAL(voxelSize, 0.1f); + } + + // test with one point + + { + position.resize(1); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(0.0f)); + + const float voxelSize = computeVoxelSize(position, /*points per voxel*/8); + CPPUNIT_ASSERT_EQUAL(voxelSize, 0.1f); + } + + // test with n points, where n > 1 && n <= num points per voxel + + { + position.resize(7); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(-8.6f, 0.0f,-23.8f)); + positionHandle.set(1, 0, Vec3f( 8.6f, 7.8f, 23.8f)); + + for (size_t i = 2; i < 7; ++ i) + positionHandle.set(i, 0, Vec3f(0.0f)); + + float voxelSize = computeVoxelSize(position, /*points per voxel*/8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(18.5528f, voxelSize, /*tolerance=*/1e-4); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(5.51306f, voxelSize, /*tolerance=*/1e-4); + + // test decimal place accuracy + + voxelSize = computeVoxelSize(position, /*points per voxel*/1, math::Mat4d::identity(), 10); + CPPUNIT_ASSERT_DOUBLES_EQUAL(5.5130610466f, voxelSize, /*tolerance=*/1e-9); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1, math::Mat4d::identity(), 1); + CPPUNIT_ASSERT_EQUAL(5.5f, voxelSize); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1, math::Mat4d::identity(), 0); + CPPUNIT_ASSERT_EQUAL(6.0f, voxelSize); + } + + // test coplanar points (Y=0) + + { + position.resize(5); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(0.0f, 0.0f, 10.0f)); + positionHandle.set(1, 0, Vec3f(0.0f, 0.0f, -10.0f)); + positionHandle.set(2, 0, Vec3f(20.0f, 0.0f, -10.0f)); + positionHandle.set(3, 0, Vec3f(20.0f, 0.0f, 10.0f)); + positionHandle.set(4, 0, Vec3f(10.0f, 0.0f, 0.0f)); + + float voxelSize = computeVoxelSize(position, /*points per voxel*/5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(20.0f, voxelSize, /*tolerance=*/1e-4); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(11.696f, voxelSize, /*tolerance=*/1e-4); + } + + // test collinear points (X=0, Y=0) + + { + position.resize(5); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(0.0f, 0.0f, 10.0f)); + positionHandle.set(1, 0, Vec3f(0.0f, 0.0f, -10.0f)); + positionHandle.set(2, 0, Vec3f(0.0f, 0.0f, -10.0f)); + positionHandle.set(3, 0, Vec3f(0.0f, 0.0f, 10.0f)); + positionHandle.set(4, 0, Vec3f(0.0f, 0.0f, 0.0f)); + + float voxelSize = computeVoxelSize(position, /*points per voxel*/5); + CPPUNIT_ASSERT_DOUBLES_EQUAL(20.0f, voxelSize, /*tolerance=*/1e-4); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(8.32034f, voxelSize, /*tolerance=*/1e-4); + } + + // test min limit collinear points (X=0, Y=0, Z=+/-float min) + + { + position.resize(2); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(0.0f, 0.0f, -std::numeric_limits::min())); + positionHandle.set(1, 0, Vec3f(0.0f, 0.0f, std::numeric_limits::min())); + + float voxelSize = computeVoxelSize(position, /*points per voxel*/2); + CPPUNIT_ASSERT_DOUBLES_EQUAL(minimumVoxelSize, voxelSize, /*tolerance=*/1e-4); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(minimumVoxelSize, voxelSize, /*tolerance=*/1e-4); + } + + // test max limit collinear points (X=+/-float max, Y=0, Z=0) + + { + position.resize(2); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(-std::numeric_limits::max(), 0.0f, 0.0f)); + positionHandle.set(1, 0, Vec3f(std::numeric_limits::max(), 0.0f, 0.0f)); + + float voxelSize = computeVoxelSize(position, /*points per voxel*/2); + CPPUNIT_ASSERT_DOUBLES_EQUAL(maximumVoxelSize, voxelSize, /*tolerance=*/1e-4); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(maximumVoxelSize, voxelSize, /*tolerance=*/1e-4); + } + + // max pointsPerVoxel + + { + position.resize(2); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(0)); + positionHandle.set(1, 0, Vec3f(1)); + + float voxelSize = computeVoxelSize(position, /*points per voxel*/std::numeric_limits::max()); + CPPUNIT_ASSERT_EQUAL(voxelSize, 1.0f); + } + + // limits test + + { + positionD.resize(2); + AttributeWrapper::Handle positionHandleD(positionD); + positionHandleD.set(0, 0, Vec3d(0)); + positionHandleD.set(1, 0, Vec3d(std::numeric_limits::max())); + + float voxelSize = computeVoxelSize(positionD, /*points per voxel*/2); + CPPUNIT_ASSERT_EQUAL(voxelSize, maximumVoxelSize); + } + + { + const float smallest(std::numeric_limits::min()); + + position.resize(4); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(0.0f)); + positionHandle.set(1, 0, Vec3f(smallest)); + positionHandle.set(2, 0, Vec3f(smallest, 0.0f, 0.0f)); + positionHandle.set(3, 0, Vec3f(smallest, 0.0f, smallest)); + + float voxelSize = computeVoxelSize(position, /*points per voxel*/4); + CPPUNIT_ASSERT_EQUAL(voxelSize, minimumVoxelSize); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(minimumVoxelSize, voxelSize, /*tolerance=*/1e-4); + + PointDataGrid::Ptr grid = Local::genPointsGrid(voxelSize, position); + CPPUNIT_ASSERT_EQUAL(grid->activeVoxelCount(), Index64(1)); + } + + // the smallest possible vector extent that can exist from an input set + // without being clamped to the minimum voxel size + // is Tolerance::value() + std::numeric_limits::min() + + { + position.resize(2); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(0.0f)); + positionHandle.set(1, 0, Vec3f(math::Tolerance::value() + std::numeric_limits::min())); + + float voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_EQUAL(voxelSize, minimumVoxelSize); + } + + // in-between smallest extent and ScaleMap determinant test + + { + position.resize(2); + AttributeWrapper::Handle positionHandle(position); + positionHandle.set(0, 0, Vec3f(0.0f)); + positionHandle.set(1, 0, Vec3f(math::Tolerance::value()*1e8 + std::numeric_limits::min())); + + float voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_EQUAL(voxelSize, float(math::Pow(double(3e-15), 1.0/3.0))); + } + + { + const float smallValue(1e-5f); + + position.resize(300000); + AttributeWrapper::Handle positionHandle(position); + + for (size_t i = 0; i < 100000; ++ i) { + positionHandle.set(i, 0, Vec3f(smallValue*float(i), 0, 0)); + positionHandle.set(i+100000, 0, Vec3f(0, smallValue*float(i), 0)); + positionHandle.set(i+200000, 0, Vec3f(0, 0, smallValue*float(i))); + } + + float voxelSize = computeVoxelSize(position, /*points per voxel*/10); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.00012f, voxelSize, /*tolerance=*/1e-4); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2e-5, voxelSize, /*tolerance=*/1e-6); + + PointDataGrid::Ptr grid = Local::genPointsGrid(voxelSize, position); + CPPUNIT_ASSERT_EQUAL(grid->activeVoxelCount(), Index64(150001)); + + // check zero decimal place still returns valid result + + voxelSize = computeVoxelSize(position, /*points per voxel*/1, math::Mat4d::identity(), 0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2e-5, voxelSize, /*tolerance=*/1e-6); + } + + // random position generation within two bounds of equal size. + // This test distributes 1000 points within a 1x1x1 box centered at (0,0,0) + // and another 1000 points within a separate 1x1x1 box centered at (20,20,20). + // Points are randomly positioned however can be defined as having a stochastic + // distribution. Tests that sparsity between these data sets causes no issues + // and that computeVoxelSize produces accurate results + + { + position.resize(2000); + AttributeWrapper::Handle positionHandle(position); + openvdb::math::Random01 randNumber(0); + + // positions between -0.5 and 0.5 + + for (size_t i = 0; i < 1000; ++ i) { + const Vec3f pos(randNumber() - 0.5f); + positionHandle.set(i, 0, pos); + } + + // positions between 19.5 and 20.5 + + for (size_t i = 1000; i < 2000; ++ i) { + const Vec3f pos(randNumber() - 0.5f + 20.0f); + positionHandle.set(i, 0, pos); + } + + float voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.00052f, voxelSize, /*tolerance=*/1e-4); + + PointDataGrid::Ptr grid = Local::genPointsGrid(voxelSize, position); + const auto pointsPerVoxel = static_cast( + math::Round(2000.0f / static_cast(grid->activeVoxelCount()))); + CPPUNIT_ASSERT_EQUAL(pointsPerVoxel, Index64(1)); + } + + // random position generation within three bounds of varying size. + // This test distributes 1000 points within a 1x1x1 box centered at (0.5,0.5,0,5) + // another 1000 points within a separate 10x10x10 box centered at (15,15,15) and + // a final 1000 points within a separate 50x50x50 box centered at (75,75,75) + // Points are randomly positioned however can be defined as having a stochastic + // distribution. Tests that sparsity between these data sets causes no issues as + // well as computeVoxelSize producing a good average result + + { + position.resize(3000); + AttributeWrapper::Handle positionHandle(position); + openvdb::math::Random01 randNumber(0); + + // positions between 0 and 1 + + for (size_t i = 0; i < 1000; ++ i) { + const Vec3f pos(randNumber()); + positionHandle.set(i, 0, pos); + } + + // positions between 10 and 20 + + for (size_t i = 1000; i < 2000; ++ i) { + const Vec3f pos((randNumber() * 10.0f) + 10.0f); + positionHandle.set(i, 0, pos); + } + + // positions between 50 and 100 + + for (size_t i = 2000; i < 3000; ++ i) { + const Vec3f pos((randNumber() * 50.0f) + 50.0f); + positionHandle.set(i, 0, pos); + } + + float voxelSize = computeVoxelSize(position, /*points per voxel*/10); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.24758f, voxelSize, /*tolerance=*/1e-3); + + PointDataGrid::Ptr grid = Local::genPointsGrid(voxelSize, position); + auto pointsPerVoxel = static_cast( + math::Round(3000.0f/ static_cast(grid->activeVoxelCount()))); + CPPUNIT_ASSERT(math::isApproxEqual(pointsPerVoxel, Index64(10), Index64(2))); + + voxelSize = computeVoxelSize(position, /*points per voxel*/1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.00231f, voxelSize, /*tolerance=*/1e-4); + + grid = Local::genPointsGrid(voxelSize, position); + pointsPerVoxel = static_cast( + math::Round(3000.0f/ static_cast(grid->activeVoxelCount()))); + CPPUNIT_ASSERT_EQUAL(pointsPerVoxel, Index64(1)); + } + + // Generate a sphere + // NOTE: The sphere does NOT provide uniform distribution + + const size_t count(40000); + + position.resize(0); + + AttributeWrapper xyz(1); + AttributeWrapper id(1); + AttributeWrapper uniform(1); + AttributeWrapper string(1); + GroupWrapper group; + + genPoints(count, /*scale=*/ 100.0, /*stride=*/false, position, xyz, id, uniform, string, group); + + CPPUNIT_ASSERT_EQUAL(position.size(), count); + CPPUNIT_ASSERT_EQUAL(id.size(), count); + CPPUNIT_ASSERT_EQUAL(uniform.size(), count); + CPPUNIT_ASSERT_EQUAL(string.size(), count); + CPPUNIT_ASSERT_EQUAL(group.size(), count); + + // test a distributed point set around a sphere + + { + const float voxelSize = computeVoxelSize(position, /*points per voxel*/2); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.6275f, voxelSize, /*tolerance=*/0.01); + + PointDataGrid::Ptr grid = Local::genPointsGrid(voxelSize, position); + const Index64 pointsPerVoxel = count / grid->activeVoxelCount(); + CPPUNIT_ASSERT_EQUAL(pointsPerVoxel, Index64(2)); + } + + // test with given target transforms + + { + // test that a different scale doesn't change the result + + openvdb::math::Transform::Ptr transform1(openvdb::math::Transform::createLinearTransform(0.33)); + openvdb::math::Transform::Ptr transform2(openvdb::math::Transform::createLinearTransform(0.87)); + + math::UniformScaleMap::ConstPtr scaleMap1 = transform1->constMap(); + math::UniformScaleMap::ConstPtr scaleMap2 = transform2->constMap(); + CPPUNIT_ASSERT(scaleMap1.get()); + CPPUNIT_ASSERT(scaleMap2.get()); + + math::AffineMap::ConstPtr affineMap1 = scaleMap1->getAffineMap(); + math::AffineMap::ConstPtr affineMap2 = scaleMap2->getAffineMap(); + + float voxelSize1 = computeVoxelSize(position, /*points per voxel*/2, affineMap1->getMat4()); + float voxelSize2 = computeVoxelSize(position, /*points per voxel*/2, affineMap2->getMat4()); + CPPUNIT_ASSERT_EQUAL(voxelSize1, voxelSize2); + + // test that applying a rotation roughly calculates to the same result for this example + // NOTE: distribution is not uniform + + // Rotate by 45 degrees in X, Y, Z + + transform1->postRotate(M_PI / 4.0, math::X_AXIS); + transform1->postRotate(M_PI / 4.0, math::Y_AXIS); + transform1->postRotate(M_PI / 4.0, math::Z_AXIS); + + affineMap1 = transform1->constMap(); + CPPUNIT_ASSERT(affineMap1.get()); + + float voxelSize3 = computeVoxelSize(position, /*points per voxel*/2, affineMap1->getMat4()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(voxelSize1, voxelSize3, 0.1); + + // test that applying a translation roughly calculates to the same result for this example + + transform1->postTranslate(Vec3d(-5.0f, 3.3f, 20.1f)); + affineMap1 = transform1->constMap(); + CPPUNIT_ASSERT(affineMap1.get()); + + float voxelSize4 = computeVoxelSize(position, /*points per voxel*/2, affineMap1->getMat4()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(voxelSize1, voxelSize4, 0.1); + } +} + +void +TestPointConversion::testPrecision() +{ + const double tolerance = math::Tolerance::value(); + + { // test values far from origin + const double voxelSize = 0.5; + const float halfVoxelSize = 0.25f; + auto transform = math::Transform::createLinearTransform(voxelSize); + + float onBorder = 1000.0f + halfVoxelSize; // can be represented exactly in floating-point + float beforeBorder = std::nextafterf(onBorder, /*to=*/0.0f); + float afterBorder = std::nextafterf(onBorder, /*to=*/2000.0f); + + const Vec3f positionBefore(beforeBorder, afterBorder, onBorder); + + std::vector points{positionBefore}; + PointAttributeVector wrapper(points); + auto pointIndexGrid = tools::createPointIndexGrid( + wrapper, *transform); + + Vec3f positionAfterNull; + Vec3f positionAfterFixed16; + + { // null codec + auto points = createPointDataGrid( + *pointIndexGrid, wrapper, *transform); + + auto leafIter = points->tree().cbeginLeaf(); + auto indexIter = leafIter->beginIndexOn(); + auto handle = AttributeHandle(leafIter->constAttributeArray("P")); + + const auto& ijk = indexIter.getCoord(); + + CPPUNIT_ASSERT_EQUAL(ijk.x(), 2000); + CPPUNIT_ASSERT_EQUAL(ijk.y(), 2001); + CPPUNIT_ASSERT_EQUAL(ijk.z(), 2001); // on border value is stored in the higher voxel + + const Vec3f positionVoxelSpace = handle.get(*indexIter); + + // voxel-space range: -0.5f >= value > 0.5f + + CPPUNIT_ASSERT(positionVoxelSpace.x() > 0.49f && positionVoxelSpace.x() < 0.5f); + CPPUNIT_ASSERT(positionVoxelSpace.y() > -0.5f && positionVoxelSpace.y() < -0.49f); + CPPUNIT_ASSERT(positionVoxelSpace.z() == -0.5f); // on border value is stored at -0.5f + + positionAfterNull = Vec3f(transform->indexToWorld(positionVoxelSpace + ijk.asVec3d())); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterNull.x(), positionBefore.x(), tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterNull.y(), positionBefore.y(), tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterNull.z(), positionBefore.z(), tolerance); + } + + { // fixed 16-bit codec + auto points = createPointDataGrid, PointDataGrid>( + *pointIndexGrid, wrapper, *transform); + + auto leafIter = points->tree().cbeginLeaf(); + auto indexIter = leafIter->beginIndexOn(); + auto handle = AttributeHandle(leafIter->constAttributeArray("P")); + + const auto& ijk = indexIter.getCoord(); + + CPPUNIT_ASSERT_EQUAL(ijk.x(), 2000); + CPPUNIT_ASSERT_EQUAL(ijk.y(), 2001); + CPPUNIT_ASSERT_EQUAL(ijk.z(), 2001); // on border value is stored in the higher voxel + + const Vec3f positionVoxelSpace = handle.get(*indexIter); + + // voxel-space range: -0.5f >= value > 0.5f + + CPPUNIT_ASSERT(positionVoxelSpace.x() > 0.49f && positionVoxelSpace.x() < 0.5f); + CPPUNIT_ASSERT(positionVoxelSpace.y() > -0.5f && positionVoxelSpace.y() < -0.49f); + CPPUNIT_ASSERT(positionVoxelSpace.z() == -0.5f); // on border value is stored at -0.5f + + positionAfterFixed16 = Vec3f(transform->indexToWorld( + positionVoxelSpace + ijk.asVec3d())); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterFixed16.x(), positionBefore.x(), tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterFixed16.y(), positionBefore.y(), tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterFixed16.z(), positionBefore.z(), tolerance); + } + + // at this precision null codec == fixed-point 16-bit codec + + CPPUNIT_ASSERT_EQUAL(positionAfterNull.x(), positionAfterFixed16.x()); + CPPUNIT_ASSERT_EQUAL(positionAfterNull.y(), positionAfterFixed16.y()); + CPPUNIT_ASSERT_EQUAL(positionAfterNull.z(), positionAfterFixed16.z()); + } + + { // test values near to origin + const double voxelSize = 0.5; + const float halfVoxelSize = 0.25f; + auto transform = math::Transform::createLinearTransform(voxelSize); + + float onBorder = 0.0f+halfVoxelSize; + float beforeBorder = std::nextafterf(onBorder, /*to=*/0.0f); + float afterBorder = std::nextafterf(onBorder, /*to=*/2000.0f); + + const Vec3f positionBefore(beforeBorder, afterBorder, onBorder); + + std::vector points{positionBefore}; + PointAttributeVector wrapper(points); + auto pointIndexGrid = tools::createPointIndexGrid( + wrapper, *transform); + + Vec3f positionAfterNull; + Vec3f positionAfterFixed16; + + { // null codec + auto points = createPointDataGrid( + *pointIndexGrid, wrapper, *transform); + + auto leafIter = points->tree().cbeginLeaf(); + auto indexIter = leafIter->beginIndexOn(); + auto handle = AttributeHandle(leafIter->constAttributeArray("P")); + + const auto& ijk = indexIter.getCoord(); + + CPPUNIT_ASSERT_EQUAL(ijk.x(), 0); + CPPUNIT_ASSERT_EQUAL(ijk.y(), 1); + CPPUNIT_ASSERT_EQUAL(ijk.z(), 1); // on border value is stored in the higher voxel + + const Vec3f positionVoxelSpace = handle.get(*indexIter); + + // voxel-space range: -0.5f >= value > 0.5f + + CPPUNIT_ASSERT(positionVoxelSpace.x() > 0.49f && positionVoxelSpace.x() < 0.5f); + CPPUNIT_ASSERT(positionVoxelSpace.y() > -0.5f && positionVoxelSpace.y() < -0.49f); + CPPUNIT_ASSERT(positionVoxelSpace.z() == -0.5f); // on border value is stored at -0.5f + + positionAfterNull = Vec3f(transform->indexToWorld(positionVoxelSpace + ijk.asVec3d())); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterNull.x(), positionBefore.x(), tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterNull.y(), positionBefore.y(), tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterNull.z(), positionBefore.z(), tolerance); + } + + { // fixed 16-bit codec - at this precision, this codec results in lossy compression + auto points = createPointDataGrid, PointDataGrid>( + *pointIndexGrid, wrapper, *transform); + + auto leafIter = points->tree().cbeginLeaf(); + auto indexIter = leafIter->beginIndexOn(); + auto handle = AttributeHandle(leafIter->constAttributeArray("P")); + + const auto& ijk = indexIter.getCoord(); + + CPPUNIT_ASSERT_EQUAL(ijk.x(), 0); + CPPUNIT_ASSERT_EQUAL(ijk.y(), 1); + CPPUNIT_ASSERT_EQUAL(ijk.z(), 1); // on border value is stored in the higher voxel + + const Vec3f positionVoxelSpace = handle.get(*indexIter); + + // voxel-space range: -0.5f >= value > 0.5f + + CPPUNIT_ASSERT(positionVoxelSpace.x() == 0.5f); // before border is clamped to 0.5f + CPPUNIT_ASSERT(positionVoxelSpace.y() == -0.5f); // after border is clamped to -0.5f + CPPUNIT_ASSERT(positionVoxelSpace.z() == -0.5f); // on border is stored at -0.5f + + positionAfterFixed16 = Vec3f(transform->indexToWorld( + positionVoxelSpace + ijk.asVec3d())); + + // reduce tolerance to handle lack of precision + + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterFixed16.x(), positionBefore.x(), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterFixed16.y(), positionBefore.y(), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(positionAfterFixed16.z(), positionBefore.z(), tolerance); + } + + // only z matches precisely due to lossy compression + + CPPUNIT_ASSERT(positionAfterNull.x() != positionAfterFixed16.x()); + CPPUNIT_ASSERT(positionAfterNull.y() != positionAfterFixed16.y()); + CPPUNIT_ASSERT_EQUAL(positionAfterNull.z(), positionAfterFixed16.z()); + } +} diff --git a/openvdb/unittest/TestPointCount.cc b/openvdb/unittest/TestPointCount.cc new file mode 100644 index 00000000..7f520861 --- /dev/null +++ b/openvdb/unittest/TestPointCount.cc @@ -0,0 +1,849 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include + +#include +#include +#include + +#include +#include // for std::remove() +#include // for std::getenv() +#include +#include + +#ifdef _MSC_VER +#include +#endif + +using namespace openvdb; +using namespace openvdb::points; + +class TestPointCount: public CppUnit::TestCase +{ +public: + + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointCount); + CPPUNIT_TEST(testCount); + CPPUNIT_TEST(testGroup); + CPPUNIT_TEST(testOffsets); + CPPUNIT_TEST(testCountGrid); + CPPUNIT_TEST_SUITE_END(); + + void testCount(); + void testGroup(); + void testOffsets(); + void testCountGrid(); + +}; // class TestPointCount + +using LeafType = PointDataTree::LeafNodeType; +using ValueType = LeafType::ValueType; + + +struct NotZeroFilter +{ + NotZeroFilter() = default; + static bool initialized() { return true; } + template + void reset(const LeafT&) { } + template + bool valid(const IterT& iter) const { + return *iter != 0; + } +}; + +void +TestPointCount::testCount() +{ + // create a tree and check there are no points + + PointDataGrid::Ptr grid = createGrid(); + PointDataTree& tree = grid->tree(); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(0)); + + // add a new leaf to a tree and re-test + + LeafType* leafPtr = tree.touchLeaf(openvdb::Coord(0, 0, 0)); + LeafType& leaf(*leafPtr); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(0)); + + // now manually set some offsets + + leaf.setOffsetOn(0, 4); + leaf.setOffsetOn(1, 7); + + ValueVoxelCIter voxelIter = leaf.beginValueVoxel(openvdb::Coord(0, 0, 0)); + + IndexIter testIter(voxelIter, NullFilter()); + + leaf.beginIndexVoxel(openvdb::Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(int(*leaf.beginIndexVoxel(openvdb::Coord(0, 0, 0))), 0); + CPPUNIT_ASSERT_EQUAL(int(leaf.beginIndexVoxel(openvdb::Coord(0, 0, 0)).end()), 4); + + CPPUNIT_ASSERT_EQUAL(int(*leaf.beginIndexVoxel(openvdb::Coord(0, 0, 1))), 4); + CPPUNIT_ASSERT_EQUAL(int(leaf.beginIndexVoxel(openvdb::Coord(0, 0, 1)).end()), 7); + + // test filtered, index voxel iterator + + CPPUNIT_ASSERT_EQUAL(int(*leaf.beginIndexVoxel(openvdb::Coord(0, 0, 0), NotZeroFilter())), 1); + CPPUNIT_ASSERT_EQUAL(int(leaf.beginIndexVoxel(openvdb::Coord(0, 0, 0), NotZeroFilter()).end()), 4); + + { + LeafType::IndexVoxelIter iter = leaf.beginIndexVoxel(openvdb::Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(int(*iter), 0); + CPPUNIT_ASSERT_EQUAL(int(iter.end()), 4); + + LeafType::IndexVoxelIter iter2 = leaf.beginIndexVoxel(openvdb::Coord(0, 0, 1)); + + CPPUNIT_ASSERT_EQUAL(int(*iter2), 4); + CPPUNIT_ASSERT_EQUAL(int(iter2.end()), 7); + + CPPUNIT_ASSERT_EQUAL(iterCount(iter2), Index64(7 - 4)); + + // check pointCount ignores active/inactive state + + leaf.setValueOff(1); + + LeafType::IndexVoxelIter iter3 = leaf.beginIndexVoxel(openvdb::Coord(0, 0, 1)); + + CPPUNIT_ASSERT_EQUAL(iterCount(iter3), Index64(7 - 4)); + + leaf.setValueOn(1); + } + + // one point per voxel + + for (unsigned int i = 0; i < LeafType::SIZE; i++) { + leaf.setOffsetOn(i, i); + } + + CPPUNIT_ASSERT_EQUAL(leaf.pointCount(), Index64(LeafType::SIZE - 1)); + CPPUNIT_ASSERT_EQUAL(leaf.onPointCount(), Index64(LeafType::SIZE - 1)); + CPPUNIT_ASSERT_EQUAL(leaf.offPointCount(), Index64(0)); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(LeafType::SIZE - 1)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, ActiveFilter()), Index64(LeafType::SIZE - 1)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, InactiveFilter()), Index64(0)); + + // manually de-activate two voxels + + leaf.setValueOff(100); + leaf.setValueOff(101); + + CPPUNIT_ASSERT_EQUAL(leaf.pointCount(), Index64(LeafType::SIZE - 1)); + CPPUNIT_ASSERT_EQUAL(leaf.onPointCount(), Index64(LeafType::SIZE - 3)); + CPPUNIT_ASSERT_EQUAL(leaf.offPointCount(), Index64(2)); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(LeafType::SIZE - 1)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, ActiveFilter()), Index64(LeafType::SIZE - 3)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, InactiveFilter()), Index64(2)); + + // one point per every other voxel and de-activate empty voxels + + unsigned sum = 0; + + for (unsigned int i = 0; i < LeafType::SIZE; i++) { + leaf.setOffsetOn(i, sum); + if (i % 2 == 0) sum++; + } + + leaf.updateValueMask(); + + CPPUNIT_ASSERT_EQUAL(leaf.pointCount(), Index64(LeafType::SIZE / 2)); + CPPUNIT_ASSERT_EQUAL(leaf.onPointCount(), Index64(LeafType::SIZE / 2)); + CPPUNIT_ASSERT_EQUAL(leaf.offPointCount(), Index64(0)); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(LeafType::SIZE / 2)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, ActiveFilter()), Index64(LeafType::SIZE / 2)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, InactiveFilter()), Index64(0)); + + // add a new non-empty leaf and check totalPointCount is correct + + LeafType* leaf2Ptr = tree.touchLeaf(openvdb::Coord(0, 0, 8)); + LeafType& leaf2(*leaf2Ptr); + + // on adding, tree now obtains ownership and is reponsible for deletion + + for (unsigned int i = 0; i < LeafType::SIZE; i++) { + leaf2.setOffsetOn(i, i); + } + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(LeafType::SIZE / 2 + LeafType::SIZE - 1)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, ActiveFilter()), Index64(LeafType::SIZE / 2 + LeafType::SIZE - 1)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, InactiveFilter()), Index64(0)); +} + + +void +TestPointCount::testGroup() +{ + using namespace openvdb::math; + + using Descriptor = AttributeSet::Descriptor; + + // four points in the same leaf + + std::vector positions{{1, 1, 1}, {1, 2, 1}, {2, 1, 1}, {2, 2, 1}}; + + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + PointDataGrid::Ptr grid = createPointDataGrid(positions, *transform); + PointDataTree& tree = grid->tree(); + + // setup temp directory + + std::string tempDir; + if (const char* dir = std::getenv("TMPDIR")) tempDir = dir; +#ifdef _MSC_VER + if (tempDir.empty()) { + char tempDirBuffer[MAX_PATH+1]; + int tempDirLen = GetTempPath(MAX_PATH+1, tempDirBuffer); + CPPUNIT_ASSERT(tempDirLen > 0 && tempDirLen <= MAX_PATH); + tempDir = tempDirBuffer; + } +#else + if (tempDir.empty()) tempDir = P_tmpdir; +#endif + + std::string filename; + + // check one leaf + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), Index32(1)); + + // retrieve first and last leaf attribute sets + + PointDataTree::LeafIter leafIter = tree.beginLeaf(); + const AttributeSet& firstAttributeSet = leafIter->attributeSet(); + + // ensure zero groups + CPPUNIT_ASSERT_EQUAL(firstAttributeSet.descriptor().groupMap().size(), size_t(0)); + + {// add an empty group + appendGroup(tree, "test"); + + CPPUNIT_ASSERT_EQUAL(firstAttributeSet.descriptor().groupMap().size(), size_t(1)); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(4)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, ActiveFilter()), Index64(4)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, InactiveFilter()), Index64(0)); + CPPUNIT_ASSERT_EQUAL(leafIter->pointCount(), Index64(4)); + CPPUNIT_ASSERT_EQUAL(leafIter->onPointCount(), Index64(4)); + CPPUNIT_ASSERT_EQUAL(leafIter->offPointCount(), Index64(0)); + + // no points found when filtered by the empty group + + CPPUNIT_ASSERT_EQUAL(pointCount(tree, GroupFilter("test", firstAttributeSet)), Index64(0)); + CPPUNIT_ASSERT_EQUAL(leafIter->groupPointCount("test"), Index64(0)); + } + + { // assign two points to the group, test offsets and point counts + const Descriptor::GroupIndex index = firstAttributeSet.groupIndex("test"); + + CPPUNIT_ASSERT(index.first != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(index.first < firstAttributeSet.size()); + + AttributeArray& array = leafIter->attributeArray(index.first); + + CPPUNIT_ASSERT(isGroup(array)); + + GroupAttributeArray& groupArray = GroupAttributeArray::cast(array); + + groupArray.set(0, GroupType(1) << index.second); + groupArray.set(3, GroupType(1) << index.second); + + // only two out of four points should be found when group filtered + + GroupFilter firstGroupFilter("test", firstAttributeSet); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree, GroupFilter("test", firstAttributeSet)), Index64(2)); + CPPUNIT_ASSERT_EQUAL(leafIter->groupPointCount("test"), Index64(2)); + + { + CPPUNIT_ASSERT_EQUAL(pointCount(tree, BinaryFilter( + firstGroupFilter, ActiveFilter())), Index64(2)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, BinaryFilter( + firstGroupFilter, InactiveFilter())), Index64(0)); + } + + CPPUNIT_ASSERT_NO_THROW(leafIter->validateOffsets()); + + // manually modify offsets so one of the points is marked as inactive + + std::vector offsets, modifiedOffsets; + offsets.resize(PointDataTree::LeafNodeType::SIZE); + modifiedOffsets.resize(PointDataTree::LeafNodeType::SIZE); + + for (Index n = 0; n < PointDataTree::LeafNodeType::NUM_VALUES; n++) { + const unsigned offset = leafIter->getValue(n); + offsets[n] = offset; + modifiedOffsets[n] = offset > 0 ? offset - 1 : offset; + } + + leafIter->setOffsets(modifiedOffsets); + + // confirm that validation fails + + CPPUNIT_ASSERT_THROW(leafIter->validateOffsets(), openvdb::ValueError); + + // replace offsets with original offsets but leave value mask + + leafIter->setOffsets(offsets, /*updateValueMask=*/ false); + + // confirm that validation now succeeds + + CPPUNIT_ASSERT_NO_THROW(leafIter->validateOffsets()); + + // ensure active / inactive point counts are correct + + CPPUNIT_ASSERT_EQUAL(pointCount(tree, GroupFilter("test", firstAttributeSet)), Index64(2)); + CPPUNIT_ASSERT_EQUAL(leafIter->groupPointCount("test"), Index64(2)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, BinaryFilter( + firstGroupFilter, ActiveFilter())), Index64(1)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, BinaryFilter( + firstGroupFilter, InactiveFilter())), Index64(1)); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(4)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, ActiveFilter()), Index64(3)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, InactiveFilter()), Index64(1)); + + // write out grid to a temp file + { + filename = tempDir + "/openvdb_test_point_load"; + + io::File fileOut(filename); + + GridCPtrVec grids{grid}; + + fileOut.write(grids); + } + + // test point count of a delay-loaded grid + { + io::File fileIn(filename); + fileIn.open(); + + GridPtrVecPtr grids = fileIn.getGrids(); + + fileIn.close(); + + CPPUNIT_ASSERT_EQUAL(grids->size(), size_t(1)); + + PointDataGrid::Ptr inputGrid = GridBase::grid((*grids)[0]); + + CPPUNIT_ASSERT(inputGrid); + + PointDataTree& inputTree = inputGrid->tree(); + const auto& attributeSet = inputTree.cbeginLeaf()->attributeSet(); + + GroupFilter groupFilter("test", attributeSet); + + bool inCoreOnly = true; + + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, NullFilter(), inCoreOnly), Index64(0)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, ActiveFilter(), inCoreOnly), Index64(0)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, InactiveFilter(), inCoreOnly), Index64(0)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, groupFilter, inCoreOnly), Index64(0)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, BinaryFilter( + groupFilter, ActiveFilter()), inCoreOnly), Index64(0)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, BinaryFilter( + groupFilter, InactiveFilter()), inCoreOnly), Index64(0)); + + inCoreOnly = false; + + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, NullFilter(), inCoreOnly), Index64(4)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, ActiveFilter(), inCoreOnly), Index64(3)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, InactiveFilter(), inCoreOnly), Index64(1)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, groupFilter, inCoreOnly), Index64(2)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, BinaryFilter( + groupFilter, ActiveFilter()), inCoreOnly), Index64(1)); + CPPUNIT_ASSERT_EQUAL(pointCount(inputTree, BinaryFilter( + groupFilter, InactiveFilter()), inCoreOnly), Index64(1)); + } + + // update the value mask and confirm point counts once again + + leafIter->updateValueMask(); + + CPPUNIT_ASSERT_NO_THROW(leafIter->validateOffsets()); + + auto& attributeSet = tree.cbeginLeaf()->attributeSet(); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree, GroupFilter("test", attributeSet)), Index64(2)); + CPPUNIT_ASSERT_EQUAL(leafIter->groupPointCount("test"), Index64(2)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, BinaryFilter( + firstGroupFilter, ActiveFilter())), Index64(2)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, BinaryFilter( + firstGroupFilter, InactiveFilter())), Index64(0)); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(4)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, ActiveFilter()), Index64(4)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, InactiveFilter()), Index64(0)); + } + + // create a tree with multiple leaves + + positions.emplace_back(20, 1, 1); + positions.emplace_back(1, 20, 1); + positions.emplace_back(1, 1, 20); + + grid = createPointDataGrid(positions, *transform); + PointDataTree& tree2 = grid->tree(); + + CPPUNIT_ASSERT_EQUAL(tree2.leafCount(), Index32(4)); + + leafIter = tree2.beginLeaf(); + + appendGroup(tree2, "test"); + + { // assign two points to the group + const auto& attributeSet = leafIter->attributeSet(); + const Descriptor::GroupIndex index = attributeSet.groupIndex("test"); + + CPPUNIT_ASSERT(index.first != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(index.first < attributeSet.size()); + + AttributeArray& array = leafIter->attributeArray(index.first); + + CPPUNIT_ASSERT(isGroup(array)); + + GroupAttributeArray& groupArray = GroupAttributeArray::cast(array); + + groupArray.set(0, GroupType(1) << index.second); + groupArray.set(3, GroupType(1) << index.second); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree2, GroupFilter("test", attributeSet)), Index64(2)); + CPPUNIT_ASSERT_EQUAL(leafIter->groupPointCount("test"), Index64(2)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree2), Index64(7)); + } + + ++leafIter; + + CPPUNIT_ASSERT(leafIter); + + { // assign another point to the group in a different leaf + const auto& attributeSet = leafIter->attributeSet(); + const Descriptor::GroupIndex index = attributeSet.groupIndex("test"); + + CPPUNIT_ASSERT(index.first != AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(index.first < leafIter->attributeSet().size()); + + AttributeArray& array = leafIter->attributeArray(index.first); + + CPPUNIT_ASSERT(isGroup(array)); + + GroupAttributeArray& groupArray = GroupAttributeArray::cast(array); + + groupArray.set(0, GroupType(1) << index.second); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree2, GroupFilter("test", attributeSet)), Index64(3)); + CPPUNIT_ASSERT_EQUAL(leafIter->groupPointCount("test"), Index64(1)); + CPPUNIT_ASSERT_EQUAL(pointCount(tree2), Index64(7)); + } +} + + +void +TestPointCount::testOffsets() +{ + using namespace openvdb::math; + + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + // five points across four leafs + + std::vector positions{{1, 1, 1}, {1, 101, 1}, {2, 101, 1}, {101, 1, 1}, {101, 101, 1}}; + + PointDataGrid::Ptr grid = createPointDataGrid(positions, *transform); + PointDataTree& tree = grid->tree(); + + { // all point offsets + std::vector offsets; + Index64 total = pointOffsets(offsets, tree); + + CPPUNIT_ASSERT_EQUAL(offsets.size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(offsets[0], Index64(1)); + CPPUNIT_ASSERT_EQUAL(offsets[1], Index64(3)); + CPPUNIT_ASSERT_EQUAL(offsets[2], Index64(4)); + CPPUNIT_ASSERT_EQUAL(offsets[3], Index64(5)); + CPPUNIT_ASSERT_EQUAL(total, Index64(5)); + } + + { // all point offsets when using a non-existant exclude group + + std::vector offsets; + + std::vector includeGroups; + std::vector excludeGroups{"empty"}; + + MultiGroupFilter filter(includeGroups, excludeGroups, tree.cbeginLeaf()->attributeSet()); + Index64 total = pointOffsets(offsets, tree, filter); + + CPPUNIT_ASSERT_EQUAL(offsets.size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(offsets[0], Index64(1)); + CPPUNIT_ASSERT_EQUAL(offsets[1], Index64(3)); + CPPUNIT_ASSERT_EQUAL(offsets[2], Index64(4)); + CPPUNIT_ASSERT_EQUAL(offsets[3], Index64(5)); + CPPUNIT_ASSERT_EQUAL(total, Index64(5)); + } + + appendGroup(tree, "test"); + + // add one point to the group from the leaf that contains two points + + PointDataTree::LeafIter iter = ++tree.beginLeaf(); + GroupWriteHandle groupHandle = iter->groupWriteHandle("test"); + groupHandle.set(0, true); + + { // include this group + std::vector offsets; + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + + MultiGroupFilter filter(includeGroups, excludeGroups, tree.cbeginLeaf()->attributeSet()); + Index64 total = pointOffsets(offsets, tree, filter); + + CPPUNIT_ASSERT_EQUAL(offsets.size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(offsets[0], Index64(0)); + CPPUNIT_ASSERT_EQUAL(offsets[1], Index64(1)); + CPPUNIT_ASSERT_EQUAL(offsets[2], Index64(1)); + CPPUNIT_ASSERT_EQUAL(offsets[3], Index64(1)); + CPPUNIT_ASSERT_EQUAL(total, Index64(1)); + } + + { // exclude this group + std::vector offsets; + + std::vector includeGroups; + std::vector excludeGroups{"test"}; + + MultiGroupFilter filter(includeGroups, excludeGroups, tree.cbeginLeaf()->attributeSet()); + Index64 total = pointOffsets(offsets, tree, filter); + + CPPUNIT_ASSERT_EQUAL(offsets.size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(offsets[0], Index64(1)); + CPPUNIT_ASSERT_EQUAL(offsets[1], Index64(2)); + CPPUNIT_ASSERT_EQUAL(offsets[2], Index64(3)); + CPPUNIT_ASSERT_EQUAL(offsets[3], Index64(4)); + CPPUNIT_ASSERT_EQUAL(total, Index64(4)); + } + + // setup temp directory + + std::string tempDir; + if (const char* dir = std::getenv("TMPDIR")) tempDir = dir; +#ifdef _MSC_VER + if (tempDir.empty()) { + char tempDirBuffer[MAX_PATH+1]; + int tempDirLen = GetTempPath(MAX_PATH+1, tempDirBuffer); + CPPUNIT_ASSERT(tempDirLen > 0 && tempDirLen <= MAX_PATH); + tempDir = tempDirBuffer; + } +#else + if (tempDir.empty()) tempDir = P_tmpdir; +#endif + + std::string filename; + + // write out grid to a temp file + { + filename = tempDir + "/openvdb_test_point_load"; + + io::File fileOut(filename); + + GridCPtrVec grids{grid}; + + fileOut.write(grids); + } + + // test point offsets for a delay-loaded grid + { + io::File fileIn(filename); + fileIn.open(); + + GridPtrVecPtr grids = fileIn.getGrids(); + + fileIn.close(); + + CPPUNIT_ASSERT_EQUAL(grids->size(), size_t(1)); + + PointDataGrid::Ptr inputGrid = GridBase::grid((*grids)[0]); + + CPPUNIT_ASSERT(inputGrid); + + PointDataTree& inputTree = inputGrid->tree(); + + std::vector offsets; + std::vector includeGroups; + std::vector excludeGroups; + + MultiGroupFilter filter(includeGroups, excludeGroups, inputTree.cbeginLeaf()->attributeSet()); + Index64 total = pointOffsets(offsets, inputTree, filter, /*inCoreOnly=*/true); + + CPPUNIT_ASSERT_EQUAL(offsets.size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(offsets[0], Index64(0)); + CPPUNIT_ASSERT_EQUAL(offsets[1], Index64(0)); + CPPUNIT_ASSERT_EQUAL(offsets[2], Index64(0)); + CPPUNIT_ASSERT_EQUAL(offsets[3], Index64(0)); + CPPUNIT_ASSERT_EQUAL(total, Index64(0)); + + offsets.clear(); + + total = pointOffsets(offsets, inputTree, filter, /*inCoreOnly=*/false); + + CPPUNIT_ASSERT_EQUAL(offsets.size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(offsets[0], Index64(1)); + CPPUNIT_ASSERT_EQUAL(offsets[1], Index64(3)); + CPPUNIT_ASSERT_EQUAL(offsets[2], Index64(4)); + CPPUNIT_ASSERT_EQUAL(offsets[3], Index64(5)); + CPPUNIT_ASSERT_EQUAL(total, Index64(5)); + } + std::remove(filename.c_str()); +} + + +namespace { + +// sum all voxel values +template +inline Index64 +voxelSum(const GridT& grid) +{ + Index64 total = 0; + for (auto iter = grid.cbeginValueOn(); iter; ++iter) { + total += static_cast(*iter); + } + return total; +} + +// Generate random points by uniformly distributing points on a unit-sphere. +inline void +genPoints(std::vector& positions, const int numPoints, const double scale) +{ + // init + math::Random01 randNumber(0); + const int n = int(std::sqrt(double(numPoints))); + const double xScale = (2.0 * M_PI) / double(n); + const double yScale = M_PI / double(n); + + double x, y, theta, phi; + Vec3R pos; + + positions.reserve(n*n); + + // loop over a [0 to n) x [0 to n) grid. + for (int a = 0; a < n; ++a) { + for (int b = 0; b < n; ++b) { + + // jitter, move to random pos. inside the current cell + x = double(a) + randNumber(); + y = double(b) + randNumber(); + + // remap to a lat/long map + theta = y * yScale; // [0 to PI] + phi = x * xScale; // [0 to 2PI] + + // convert to cartesian coordinates on a unit sphere. + // spherical coordinate triplet (r=1, theta, phi) + pos[0] = static_cast(std::sin(theta)*std::cos(phi)*scale); + pos[1] = static_cast(std::sin(theta)*std::sin(phi)*scale); + pos[2] = static_cast(std::cos(theta)*scale); + + positions.push_back(pos); + } + } +} + +} // namespace + + +void +TestPointCount::testCountGrid() +{ + using namespace openvdb::math; + + { // five points + std::vector positions{ {1, 1, 1}, + {1, 101, 1}, + {2, 101, 1}, + {101, 1, 1}, + {101, 101, 1}}; + + { // in five voxels + + math::Transform::Ptr transform(math::Transform::createLinearTransform(1.0f)); + PointDataGrid::Ptr points = createPointDataGrid(positions, *transform); + + // generate a count grid with the same transform + + Int32Grid::Ptr count = pointCountGrid(*points); + + CPPUNIT_ASSERT_EQUAL(count->activeVoxelCount(), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(count->evalActiveVoxelBoundingBox(), points->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count), pointCount(points->tree())); + } + + { // in four voxels + + math::Transform::Ptr transform(math::Transform::createLinearTransform(10.0f)); + PointDataGrid::Ptr points = createPointDataGrid(positions, *transform); + + // generate a count grid with the same transform + + Int32Grid::Ptr count = pointCountGrid(*points); + + CPPUNIT_ASSERT_EQUAL(count->activeVoxelCount(), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(count->evalActiveVoxelBoundingBox(), points->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count), pointCount(points->tree())); + } + + { // in one voxel + + math::Transform::Ptr transform(math::Transform::createLinearTransform(1000.0f)); + PointDataGrid::Ptr points = createPointDataGrid(positions, *transform); + + // generate a count grid with the same transform + + Int32Grid::Ptr count = pointCountGrid(*points); + + CPPUNIT_ASSERT_EQUAL(count->activeVoxelCount(), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(count->evalActiveVoxelBoundingBox(), points->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count), pointCount(points->tree())); + } + + { // in four voxels, Int64 grid + + math::Transform::Ptr transform(math::Transform::createLinearTransform(10.0f)); + PointDataGrid::Ptr points = createPointDataGrid(positions, *transform); + + // generate a count grid with the same transform + + Int64Grid::Ptr count = pointCountGrid(*points); + + CPPUNIT_ASSERT_EQUAL(count->activeVoxelCount(), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(count->evalActiveVoxelBoundingBox(), points->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count), pointCount(points->tree())); + } + + { // in four voxels, float grid + + math::Transform::Ptr transform(math::Transform::createLinearTransform(10.0f)); + PointDataGrid::Ptr points = createPointDataGrid(positions, *transform); + + // generate a count grid with the same transform + + FloatGrid::Ptr count = pointCountGrid(*points); + + CPPUNIT_ASSERT_EQUAL(count->activeVoxelCount(), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(count->evalActiveVoxelBoundingBox(), points->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count), pointCount(points->tree())); + } + + { // in four voxels + + math::Transform::Ptr transform(math::Transform::createLinearTransform(10.0f)); + const PointAttributeVector pointList(positions); + tools::PointIndexGrid::Ptr pointIndexGrid = + tools::createPointIndexGrid(pointList, *transform); + + PointDataGrid::Ptr points = + createPointDataGrid(*pointIndexGrid, + pointList, *transform); + + auto& tree = points->tree(); + + // assign point 3 to new group "test" + + appendGroup(tree, "test"); + + std::vector groups{0,0,1,0,0}; + + setGroup(tree, pointIndexGrid->tree(), groups, "test"); + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + + // generate a count grid with the same transform + + MultiGroupFilter filter(includeGroups, excludeGroups, + tree.cbeginLeaf()->attributeSet()); + Int32Grid::Ptr count = pointCountGrid(*points, filter); + + CPPUNIT_ASSERT_EQUAL(count->activeVoxelCount(), Index64(1)); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count), Index64(1)); + + MultiGroupFilter filter2(excludeGroups, includeGroups, + tree.cbeginLeaf()->attributeSet()); + count = pointCountGrid(*points, filter2); + + CPPUNIT_ASSERT_EQUAL(count->activeVoxelCount(), Index64(4)); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count), Index64(4)); + } + } + + { // 40,000 points on a unit sphere + std::vector positions; + const size_t total = 40000; + genPoints(positions, total, /*scale=*/100.0); + CPPUNIT_ASSERT_EQUAL(positions.size(), total); + + math::Transform::Ptr transform1(math::Transform::createLinearTransform(1.0f)); + math::Transform::Ptr transform5(math::Transform::createLinearTransform(5.0f)); + + PointDataGrid::Ptr points1 = + createPointDataGrid(positions, *transform1); + PointDataGrid::Ptr points5 = + createPointDataGrid(positions, *transform5); + + CPPUNIT_ASSERT(points1->activeVoxelCount() != points5->activeVoxelCount()); + CPPUNIT_ASSERT(points1->evalActiveVoxelBoundingBox() != points5->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(pointCount(points1->tree()), pointCount(points5->tree())); + + { // generate count grids with the same transform + + Int32Grid::Ptr count1 = pointCountGrid(*points1); + + CPPUNIT_ASSERT_EQUAL(count1->activeVoxelCount(), points1->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(count1->evalActiveVoxelBoundingBox(), points1->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count1), pointCount(points1->tree())); + + Int32Grid::Ptr count5 = pointCountGrid(*points5); + + CPPUNIT_ASSERT_EQUAL(count5->activeVoxelCount(), points5->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(count5->evalActiveVoxelBoundingBox(), points5->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count5), pointCount(points5->tree())); + } + + { // generate count grids with differing transforms + + Int32Grid::Ptr count1 = pointCountGrid(*points5, *transform1); + + CPPUNIT_ASSERT_EQUAL(count1->activeVoxelCount(), points1->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(count1->evalActiveVoxelBoundingBox(), points1->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count1), pointCount(points5->tree())); + + Int32Grid::Ptr count5 = pointCountGrid(*points1, *transform5); + + CPPUNIT_ASSERT_EQUAL(count5->activeVoxelCount(), points5->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(count5->evalActiveVoxelBoundingBox(), points5->evalActiveVoxelBoundingBox()); + CPPUNIT_ASSERT_EQUAL(voxelSum(*count5), pointCount(points1->tree())); + } + } +} + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointCount); diff --git a/openvdb/unittest/TestPointDataLeaf.cc b/openvdb/unittest/TestPointDataLeaf.cc new file mode 100644 index 00000000..3214934b --- /dev/null +++ b/openvdb/unittest/TestPointDataLeaf.cc @@ -0,0 +1,1550 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace openvdb; +using namespace openvdb::points; + + +class TestPointDataLeaf: public CppUnit::TestCase +{ +public: + + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointDataLeaf); + CPPUNIT_TEST(testEmptyLeaf); + CPPUNIT_TEST(testOffsets); + CPPUNIT_TEST(testSetValue); + CPPUNIT_TEST(testMonotonicity); + CPPUNIT_TEST(testAttributes); + CPPUNIT_TEST(testTopologyCopy); + CPPUNIT_TEST(testEquivalence); + CPPUNIT_TEST(testIterators); + CPPUNIT_TEST(testReadWriteCompression); + CPPUNIT_TEST(testIO); + CPPUNIT_TEST(testSwap); + CPPUNIT_TEST(testCopyOnWrite); + CPPUNIT_TEST(testCopyDescriptor); + CPPUNIT_TEST_SUITE_END(); + + void testEmptyLeaf(); + void testOffsets(); + void testSetValue(); + void testMonotonicity(); + void testAttributes(); + void testTopologyCopy(); + void testEquivalence(); + void testIterators(); + void testReadWriteCompression(); + void testIO(); + void testSwap(); + void testCopyOnWrite(); + void testCopyDescriptor(); + +private: + +}; // class TestPointDataLeaf + +using LeafType = PointDataTree::LeafNodeType; +using ValueType = LeafType::ValueType; +using BufferType = LeafType::Buffer; + +namespace { + +bool +matchingNamePairs(const openvdb::NamePair& lhs, + const openvdb::NamePair& rhs) +{ + if (lhs.first != rhs.first) return false; + if (lhs.second != rhs.second) return false; + + return true; +} + +bool +zeroLeafValues(const LeafType* leafNode) +{ + for (openvdb::Index i = 0; i < LeafType::SIZE; i++) { + if (leafNode->buffer().getValue(i) != LeafType::ValueType(0)) return false; + } + + return true; +} + +bool +noAttributeData(const LeafType* leafNode) +{ + const AttributeSet& attributeSet = leafNode->attributeSet(); + + return attributeSet.size() == 0 && attributeSet.descriptor().size() == 0; +} + +bool +monotonicOffsets(const LeafType& leafNode) +{ + int previous = -1; + + for (auto iter = leafNode.cbeginValueOn(); iter; ++iter) { + if (previous > int(*iter)) return false; + previous = int(*iter); + } + + return true; +} + +// (borrowed from PointIndexGrid unit test) + +class PointList +{ +public: + using PosType = openvdb::Vec3R; + using value_type = openvdb::Vec3R; + + PointList(const std::vector& points) + : mPoints(&points) + { + } + + size_t size() const { + return mPoints->size(); + } + + void getPos(size_t n, openvdb::Vec3R& xyz) const { + xyz = (*mPoints)[n]; + } + +protected: + std::vector const * const mPoints; +}; // PointList + +// Generate random points by uniformly distributing points +// on a unit-sphere. +// (borrowed from PointIndexGrid unit test) +std::vector genPoints(const int numPoints) +{ + // init + openvdb::math::Random01 randNumber(0); + const int n = int(std::sqrt(double(numPoints))); + const double xScale = (2.0 * M_PI) / double(n); + const double yScale = M_PI / double(n); + + double x, y, theta, phi; + + std::vector points; + points.reserve(n*n); + + // loop over a [0 to n) x [0 to n) grid. + for (int a = 0; a < n; ++a) { + for (int b = 0; b < n; ++b) { + + // jitter, move to random pos. inside the current cell + x = double(a) + randNumber(); + y = double(b) + randNumber(); + + // remap to a lat/long map + theta = y * yScale; // [0 to PI] + phi = x * xScale; // [0 to 2PI] + + // convert to cartesian coordinates on a unit sphere. + // spherical coordinate triplet (r=1, theta, phi) + points.emplace_back( std::sin(theta)*std::cos(phi), + std::sin(theta)*std::sin(phi), + std::cos(theta) ); + } + } + + return points; +} + +} // namespace + +void +TestPointDataLeaf::testEmptyLeaf() +{ + // empty leaf construction + + { + LeafType* leafNode = new LeafType(); + + CPPUNIT_ASSERT(leafNode); + CPPUNIT_ASSERT(leafNode->isEmpty()); + CPPUNIT_ASSERT(!leafNode->buffer().empty()); + CPPUNIT_ASSERT(zeroLeafValues(leafNode)); + CPPUNIT_ASSERT(noAttributeData(leafNode)); + CPPUNIT_ASSERT(leafNode->origin() == openvdb::Coord(0, 0, 0)); + + delete leafNode; + } + + // empty leaf with non-zero origin construction + + { + openvdb::Coord coord(20, 30, 40); + + LeafType* leafNode = new LeafType(coord); + + CPPUNIT_ASSERT(leafNode); + CPPUNIT_ASSERT(leafNode->isEmpty()); + CPPUNIT_ASSERT(!leafNode->buffer().empty()); + CPPUNIT_ASSERT(zeroLeafValues(leafNode)); + CPPUNIT_ASSERT(noAttributeData(leafNode)); + + CPPUNIT_ASSERT(leafNode->origin() == openvdb::Coord(16, 24, 40)); + + delete leafNode; + } +} + + +void +TestPointDataLeaf::testOffsets() +{ + // offsets for one point per voxel (active = true) + + { + LeafType* leafNode = new LeafType(); + + for (openvdb::Index i = 0; i < LeafType::SIZE; i++) { + leafNode->setOffsetOn(i, i); + } + + CPPUNIT_ASSERT(leafNode->getValue(10) == 10); + CPPUNIT_ASSERT(leafNode->isDense()); + + delete leafNode; + } + + // offsets for one point per voxel (active = false) + + { + LeafType* leafNode = new LeafType(); + + for (openvdb::Index i = 0; i < LeafType::SIZE; i++) { + leafNode->setOffsetOnly(i, i); + } + + CPPUNIT_ASSERT(leafNode->getValue(10) == 10); + CPPUNIT_ASSERT(leafNode->isEmpty()); + + delete leafNode; + } + + // test bulk offset replacement without activity mask update + + { + LeafType* leafNode = new LeafType(); + + for (openvdb::Index i = 0; i < LeafType::SIZE; ++i) { + leafNode->setOffsetOn(i, 10); + } + + std::vector newOffsets(LeafType::SIZE); + + leafNode->setOffsets(newOffsets, /*updateValueMask*/false); + + const LeafType::NodeMaskType& valueMask = leafNode->getValueMask(); + for (openvdb::Index i = 0; i < LeafType::SIZE; ++i ) { + CPPUNIT_ASSERT(valueMask.isOn(i)); + } + + delete leafNode; + } + + // test bulk offset replacement with activity mask update + + { + LeafType* leafNode = new LeafType(); + + for (openvdb::Index i = 0; i < LeafType::SIZE; ++i) { + leafNode->setOffsetOn(i, 10); + } + + std::vector newOffsets(LeafType::SIZE); + + leafNode->setOffsets(newOffsets, /*updateValueMask*/true); + + const LeafType::NodeMaskType& valueMask = leafNode->getValueMask(); + for (openvdb::Index i = 0; i < LeafType::SIZE; ++i ) { + CPPUNIT_ASSERT(valueMask.isOff(i)); + } + + delete leafNode; + } + + // ensure bulk offset replacement fails when vector size doesn't equal number of voxels + + { + LeafType* leafNode = new LeafType(); + + std::vector newOffsets; + CPPUNIT_ASSERT_THROW(leafNode->setOffsets(newOffsets), openvdb::ValueError); + + delete leafNode; + } + + // test offset validation + + { + using AttributeVec3s = TypedAttributeArray; + using AttributeS = TypedAttributeArray; + using Descriptor = AttributeSet::Descriptor; + + // empty Descriptor should throw on leaf node initialize + auto emptyDescriptor = std::make_shared(); + LeafType* emptyLeafNode = new LeafType(); + CPPUNIT_ASSERT_THROW(emptyLeafNode->initializeAttributes(emptyDescriptor, 5), + openvdb::IndexError); + + // create a non-empty Descriptor + Descriptor::Ptr descriptor = Descriptor::create(AttributeVec3s::attributeType()); + + // ensure validateOffsets succeeds for monotonically increasing offsets that fully + // utilise the underlying attribute arrays + + { + const size_t numAttributes = 1; + LeafType* leafNode = new LeafType(); + leafNode->initializeAttributes(descriptor, numAttributes); + + descriptor = descriptor->duplicateAppend("density", AttributeS::attributeType()); + leafNode->appendAttribute(leafNode->attributeSet().descriptor(), + descriptor, descriptor->find("density")); + + std::vector offsets(LeafType::SIZE); + offsets.back() = numAttributes; + leafNode->setOffsets(offsets); + + CPPUNIT_ASSERT_NO_THROW(leafNode->validateOffsets()); + delete leafNode; + } + + // ensure validateOffsets detects non-monotonic offset values + + { + LeafType* leafNode = new LeafType(); + + std::vector offsets(LeafType::SIZE); + *offsets.begin() = 1; + leafNode->setOffsets(offsets); + + CPPUNIT_ASSERT_THROW(leafNode->validateOffsets(), openvdb::ValueError); + delete leafNode; + } + + // ensure validateOffsets detects inconsistent attribute array sizes + + { + descriptor = Descriptor::create(AttributeVec3s::attributeType()); + + const size_t numAttributes = 1; + LeafType* leafNode = new LeafType(); + leafNode->initializeAttributes(descriptor, numAttributes); + + descriptor = descriptor->duplicateAppend("density", AttributeS::attributeType()); + leafNode->appendAttribute(leafNode->attributeSet().descriptor(), + descriptor, descriptor->find("density")); + + AttributeSet* newSet = new AttributeSet(leafNode->attributeSet(), numAttributes); + newSet->replace("density", AttributeS::create(numAttributes+1)); + leafNode->replaceAttributeSet(newSet); + + std::vector offsets(LeafType::SIZE); + offsets.back() = numAttributes; + leafNode->setOffsets(offsets); + + CPPUNIT_ASSERT_THROW(leafNode->validateOffsets(), openvdb::ValueError); + delete leafNode; + } + + // ensure validateOffsets detects unused attributes (e.g. final voxel offset not + // equal to size of attribute arrays) + + { + descriptor = Descriptor::create(AttributeVec3s::attributeType()); + + const size_t numAttributes = 1; + LeafType* leafNode = new LeafType(); + leafNode->initializeAttributes(descriptor, numAttributes); + + descriptor = descriptor->duplicateAppend("density", AttributeS::attributeType()); + leafNode->appendAttribute(leafNode->attributeSet().descriptor(), + descriptor, descriptor->find("density")); + + std::vector offsets(LeafType::SIZE); + offsets.back() = numAttributes - 1; + leafNode->setOffsets(offsets); + + CPPUNIT_ASSERT_THROW(leafNode->validateOffsets(), openvdb::ValueError); + delete leafNode; + } + + // ensure validateOffsets detects out-of-bounds offset values + + { + descriptor = Descriptor::create(AttributeVec3s::attributeType()); + + const size_t numAttributes = 1; + LeafType* leafNode = new LeafType(); + leafNode->initializeAttributes(descriptor, numAttributes); + + descriptor = descriptor->duplicateAppend("density", AttributeS::attributeType()); + leafNode->appendAttribute(leafNode->attributeSet().descriptor(), + descriptor, descriptor->find("density")); + + std::vector offsets(LeafType::SIZE); + offsets.back() = numAttributes + 1; + leafNode->setOffsets(offsets); + + CPPUNIT_ASSERT_THROW(leafNode->validateOffsets(), openvdb::ValueError); + delete leafNode; + } + } +} + + +void +TestPointDataLeaf::testSetValue() +{ + // the following tests are not run when in debug mode due to assertions firing +#ifdef NDEBUG + LeafType leaf(openvdb::Coord(0, 0, 0)); + + openvdb::Coord xyz(0, 0, 0); + openvdb::Index index(LeafType::coordToOffset(xyz)); + + // ensure all non-modifiable operations are no-ops + + leaf.setValueOnly(xyz, 10); + leaf.setValueOnly(index, 10); + leaf.setValueOff(xyz, 10); + leaf.setValueOff(index, 10); + leaf.setValueOn(xyz, 10); + leaf.setValueOn(index, 10); + + struct Local { static inline void op(unsigned int& n) { n = 10; } }; + + leaf.modifyValue(xyz, Local::op); + leaf.modifyValue(index, Local::op); + leaf.modifyValueAndActiveState(xyz, Local::op); + + CPPUNIT_ASSERT_EQUAL(0, int(leaf.getValue(xyz))); +#endif +} + + +void +TestPointDataLeaf::testMonotonicity() +{ + LeafType leaf(openvdb::Coord(0, 0, 0)); + + // assign aggregate values and activate all non-even coordinate sums + + unsigned sum = 0; + + for (unsigned int i = 0; i < LeafType::DIM; i++) { + for (unsigned int j = 0; j < LeafType::DIM; j++) { + for (unsigned int k = 0; k < LeafType::DIM; k++) { + if (((i + j + k) % 2) == 0) continue; + + leaf.setOffsetOn(LeafType::coordToOffset(openvdb::Coord(i, j, k)), sum++); + } + } + } + + CPPUNIT_ASSERT(monotonicOffsets(leaf)); + + // manually change a value and ensure offsets become non-monotonic + + leaf.setOffsetOn(500, 4); + + CPPUNIT_ASSERT(!monotonicOffsets(leaf)); +} + + +void +TestPointDataLeaf::testAttributes() +{ + using AttributeVec3s = TypedAttributeArray; + using AttributeI = TypedAttributeArray; + + // create a descriptor + + using Descriptor = AttributeSet::Descriptor; + + Descriptor::Ptr descrA = Descriptor::create(AttributeVec3s::attributeType()); + + // create a leaf and initialize attributes using this descriptor + + LeafType leaf(openvdb::Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(leaf.attributeSet().size(), size_t(0)); + + leaf.initializeAttributes(descrA, /*arrayLength=*/100); + + descrA = descrA->duplicateAppend("id", AttributeI::attributeType()); + leaf.appendAttribute(leaf.attributeSet().descriptor(), descrA, descrA->find("id")); + + CPPUNIT_ASSERT_EQUAL(leaf.attributeSet().size(), size_t(2)); + + { + const AttributeArray* array = leaf.attributeSet().get(/*pos=*/0); + + CPPUNIT_ASSERT_EQUAL(array->size(), Index(100)); + } + + // manually set a voxel + + leaf.setOffsetOn(LeafType::SIZE - 1, 10); + + CPPUNIT_ASSERT(!zeroLeafValues(&leaf)); + + // neither dense nor empty + + CPPUNIT_ASSERT(!leaf.isDense()); + CPPUNIT_ASSERT(!leaf.isEmpty()); + + // clear the attributes and check voxel values are zero but value mask is not touched + + leaf.clearAttributes(/*updateValueMask=*/ false); + + CPPUNIT_ASSERT(!leaf.isDense()); + CPPUNIT_ASSERT(!leaf.isEmpty()); + + CPPUNIT_ASSERT_EQUAL(leaf.attributeSet().size(), size_t(2)); + CPPUNIT_ASSERT(zeroLeafValues(&leaf)); + + // call clearAttributes again, updating the value mask and check it is now inactive + + leaf.clearAttributes(); + + CPPUNIT_ASSERT(leaf.isEmpty()); + + // ensure arrays are uniform + + const AttributeArray* array0 = leaf.attributeSet().get(/*pos=*/0); + const AttributeArray* array1 = leaf.attributeSet().get(/*pos=*/1); + + CPPUNIT_ASSERT_EQUAL(array0->size(), Index(1)); + CPPUNIT_ASSERT_EQUAL(array1->size(), Index(1)); + + // test leaf returns expected result for hasAttribute() + + CPPUNIT_ASSERT(leaf.hasAttribute(/*pos*/0)); + CPPUNIT_ASSERT(leaf.hasAttribute("P")); + + CPPUNIT_ASSERT(leaf.hasAttribute(/*pos*/1)); + CPPUNIT_ASSERT(leaf.hasAttribute("id")); + + CPPUNIT_ASSERT(!leaf.hasAttribute(/*pos*/2)); + CPPUNIT_ASSERT(!leaf.hasAttribute("test")); + + // test underlying attributeArray can be accessed by name and index, + // and that their types are as expected. + + const LeafType* constLeaf = &leaf; + + CPPUNIT_ASSERT(matchingNamePairs(leaf.attributeArray(/*pos*/0).type(), + AttributeVec3s::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(leaf.attributeArray("P").type(), + AttributeVec3s::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(leaf.attributeArray(/*pos*/1).type(), + AttributeI::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(leaf.attributeArray("id").type(), + AttributeI::attributeType())); + + CPPUNIT_ASSERT(matchingNamePairs(constLeaf->attributeArray(/*pos*/0).type(), + AttributeVec3s::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(constLeaf->attributeArray("P").type(), + AttributeVec3s::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(constLeaf->attributeArray(/*pos*/1).type(), + AttributeI::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(constLeaf->attributeArray("id").type(), + AttributeI::attributeType())); + + // check invalid pos or name throws + + CPPUNIT_ASSERT_THROW(leaf.attributeArray(/*pos=*/3), openvdb::LookupError); + CPPUNIT_ASSERT_THROW(leaf.attributeArray("not_there"), openvdb::LookupError); + + CPPUNIT_ASSERT_THROW(constLeaf->attributeArray(/*pos=*/3), openvdb::LookupError); + CPPUNIT_ASSERT_THROW(constLeaf->attributeArray("not_there"), openvdb::LookupError); + + // test leaf can be successfully cast to TypedAttributeArray and check types + + CPPUNIT_ASSERT(matchingNamePairs(leaf.attributeArray(/*pos=*/0).type(), + AttributeVec3s::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(leaf.attributeArray("P").type(), + AttributeVec3s::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(leaf.attributeArray(/*pos=*/1).type(), + AttributeI::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(leaf.attributeArray("id").type(), + AttributeI::attributeType())); + + CPPUNIT_ASSERT(matchingNamePairs(constLeaf->attributeArray(/*pos=*/0).type(), + AttributeVec3s::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(constLeaf->attributeArray("P").type(), + AttributeVec3s::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(constLeaf->attributeArray(/*pos=*/1).type(), + AttributeI::attributeType())); + CPPUNIT_ASSERT(matchingNamePairs(constLeaf->attributeArray("id").type(), + AttributeI::attributeType())); + + // check invalid pos or name throws + + CPPUNIT_ASSERT_THROW(leaf.attributeArray(/*pos=*/2), openvdb::LookupError); + CPPUNIT_ASSERT_THROW(leaf.attributeArray("test"), openvdb::LookupError); + + CPPUNIT_ASSERT_THROW(constLeaf->attributeArray(/*pos=*/2), openvdb::LookupError); + CPPUNIT_ASSERT_THROW(constLeaf->attributeArray("test"), openvdb::LookupError); + + // check memory usage = attribute set + base leaf + + // leaf.initializeAttributes(descrA, /*arrayLength=*/100); + + const LeafType::BaseLeaf& baseLeaf = static_cast(leaf); + + const Index64 memUsage = baseLeaf.memUsage() + leaf.attributeSet().memUsage(); + + CPPUNIT_ASSERT_EQUAL(memUsage, leaf.memUsage()); +} + + +void +TestPointDataLeaf::testTopologyCopy() +{ + // test topology copy from a float Leaf + + { + using FloatLeaf = openvdb::FloatTree::LeafNodeType; + + // create a float leaf and activate some values + + FloatLeaf floatLeaf(openvdb::Coord(0, 0, 0)); + + floatLeaf.setValueOn(1); + floatLeaf.setValueOn(4); + floatLeaf.setValueOn(7); + floatLeaf.setValueOn(8); + + CPPUNIT_ASSERT_EQUAL(floatLeaf.onVoxelCount(), Index64(4)); + + // validate construction of a PointDataLeaf using a TopologyCopy + + LeafType leaf(floatLeaf, 0, openvdb::TopologyCopy()); + + CPPUNIT_ASSERT_EQUAL(leaf.onVoxelCount(), Index64(4)); + + LeafType leaf2(openvdb::Coord(8, 8, 8)); + + leaf2.setValueOn(1); + leaf2.setValueOn(4); + leaf2.setValueOn(7); + + CPPUNIT_ASSERT(!leaf.hasSameTopology(&leaf2)); + + leaf2.setValueOn(8); + + CPPUNIT_ASSERT(leaf.hasSameTopology(&leaf2)); + + // validate construction of a PointDataLeaf using an Off-On TopologyCopy + + LeafType leaf3(floatLeaf, 1, 2, openvdb::TopologyCopy()); + + CPPUNIT_ASSERT_EQUAL(leaf3.onVoxelCount(), Index64(4)); + } + + // test topology copy from a PointIndexLeaf + + { + // generate points + // (borrowed from PointIndexGrid unit test) + + const float voxelSize = 0.01f; + const openvdb::math::Transform::Ptr transform = + openvdb::math::Transform::createLinearTransform(voxelSize); + + std::vector points = genPoints(40000); + + PointList pointList(points); + + // construct point index grid + + using PointIndexGrid = openvdb::tools::PointIndexGrid; + + PointIndexGrid::Ptr pointGridPtr = + openvdb::tools::createPointIndexGrid(pointList, *transform); + + auto iter = pointGridPtr->tree().cbeginLeaf(); + + CPPUNIT_ASSERT(iter); + + // check that the active voxel counts match for all leaves + + for ( ; iter; ++iter) { + LeafType leaf(*iter); + + CPPUNIT_ASSERT_EQUAL(iter->onVoxelCount(), leaf.onVoxelCount()); + } + } +} + + +void +TestPointDataLeaf::testEquivalence() +{ + using AttributeVec3s = TypedAttributeArray; + using AttributeF = TypedAttributeArray; + using AttributeI = TypedAttributeArray; + + // create a descriptor + + using Descriptor = AttributeSet::Descriptor; + + Descriptor::Ptr descrA = Descriptor::create(AttributeVec3s::attributeType()); + + // create a leaf and initialize attributes using this descriptor + + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.initializeAttributes(descrA, /*arrayLength=*/100); + + descrA = descrA->duplicateAppend("density", AttributeF::attributeType()); + leaf.appendAttribute(leaf.attributeSet().descriptor(), descrA, descrA->find("density")); + + descrA = descrA->duplicateAppend("id", AttributeI::attributeType()); + leaf.appendAttribute(leaf.attributeSet().descriptor(), descrA, descrA->find("id")); + + // manually activate some voxels + + leaf.setValueOn(1); + leaf.setValueOn(4); + leaf.setValueOn(7); + + // manually change some values in the density array + + TypedAttributeArray& attr = + TypedAttributeArray::cast(leaf.attributeArray("density")); + + attr.set(0, 5.0f); + attr.set(50, 2.0f); + attr.set(51, 8.1f); + + // check deep copy construction (topology and attributes) + + { + LeafType leaf2(leaf); + + CPPUNIT_ASSERT_EQUAL(leaf.onVoxelCount(), leaf2.onVoxelCount()); + CPPUNIT_ASSERT(leaf.hasSameTopology(&leaf2)); + + CPPUNIT_ASSERT_EQUAL(leaf.attributeSet().size(), leaf2.attributeSet().size()); + CPPUNIT_ASSERT_EQUAL(leaf.attributeSet().get(0)->size(), + leaf2.attributeSet().get(0)->size()); + } + + // check equivalence + + { + LeafType leaf2(leaf); + + CPPUNIT_ASSERT(leaf == leaf2); + + leaf2.setOrigin(openvdb::Coord(0, 8, 0)); + + CPPUNIT_ASSERT(leaf != leaf2); + } + + { + LeafType leaf2(leaf); + + CPPUNIT_ASSERT(leaf == leaf2); + + leaf2.setValueOn(10); + + CPPUNIT_ASSERT(leaf != leaf2); + } +} + + +void +TestPointDataLeaf::testIterators() +{ + using AttributeVec3s = TypedAttributeArray; + using AttributeF = TypedAttributeArray; + + // create a descriptor + + using Descriptor = AttributeSet::Descriptor; + + Descriptor::Ptr descrA = Descriptor::create(AttributeVec3s::attributeType()); + + // create a leaf and initialize attributes using this descriptor + + const size_t size = LeafType::NUM_VOXELS; + + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.initializeAttributes(descrA, /*arrayLength=*/size/2); + + descrA = descrA->duplicateAppend("density", AttributeF::attributeType()); + leaf.appendAttribute(leaf.attributeSet().descriptor(), descrA, descrA->find("density")); + + { // uniform monotonic offsets, only even active + int offset = 0; + + for (Index i = 0; i < size; i++) + { + if ((i % 2) == 0) { + leaf.setOffsetOn(i, ++offset); + } + else { + leaf.setOffsetOnly(i, ++offset); + leaf.setValueOff(i); + } + } + } + + { // test index on + LeafType::IndexOnIter iterOn(leaf.beginIndexOn()); + CPPUNIT_ASSERT_EQUAL(iterCount(iterOn), Index64(size/2)); + for (int i = 0; iterOn; ++iterOn, i += 2) { + CPPUNIT_ASSERT_EQUAL(*iterOn, Index32(i)); + } + } + + { // test index off + LeafType::IndexOffIter iterOff(leaf.beginIndexOff()); + CPPUNIT_ASSERT_EQUAL(iterCount(iterOff), Index64(size/2)); + for (int i = 1; iterOff; ++iterOff, i += 2) { + CPPUNIT_ASSERT_EQUAL(*iterOff, Index32(i)); + } + } + + { // test index all + LeafType::IndexAllIter iterAll(leaf.beginIndexAll()); + CPPUNIT_ASSERT_EQUAL(iterCount(iterAll), Index64(size)); + for (int i = 0; iterAll; ++iterAll, ++i) { + CPPUNIT_ASSERT_EQUAL(*iterAll, Index32(i)); + } + } + +} + + +void +TestPointDataLeaf::testReadWriteCompression() +{ + using namespace openvdb; + + util::NodeMask<3> valueMask; + util::NodeMask<3> childMask; + + io::StreamMetadata::Ptr nullMetadata; + io::StreamMetadata::Ptr streamMetadata(new io::StreamMetadata); + + { // simple read/write test + std::stringstream ss; + + Index count = 8*8*8; + std::unique_ptr srcBuf(new PointDataIndex32[count]); + + for (Index i = 0; i < count; i++) srcBuf[i] = i; + + { + io::writeCompressedValues(ss, srcBuf.get(), count, valueMask, childMask, false); + + std::unique_ptr destBuf(new PointDataIndex32[count]); + + io::readCompressedValues(ss, destBuf.get(), count, valueMask, false); + + for (Index i = 0; i < count; i++) { + CPPUNIT_ASSERT_EQUAL(srcBuf.get()[i], destBuf.get()[i]); + } + } + + const char* charBuffer = reinterpret_cast(srcBuf.get()); + size_t referenceBytes = + compression::bloscCompressedSize(charBuffer, count*sizeof(PointDataIndex32)); + + { + ss.str(""); + + io::setStreamMetadataPtr(ss, streamMetadata); + + io::writeCompressedValuesSize(ss, srcBuf.get(), count); + io::writeCompressedValues(ss, srcBuf.get(), count, valueMask, childMask, false); + int magic = 1924674; + ss.write(reinterpret_cast(&magic), sizeof(int)); + + std::unique_ptr destBuf(new PointDataIndex32[count]); + + uint16_t size; + ss.read(reinterpret_cast(&size), sizeof(uint16_t)); + if (size == std::numeric_limits::max()) size = 0; + + CPPUNIT_ASSERT_EQUAL(size_t(size), referenceBytes); + + io::readCompressedValues(ss, destBuf.get(), count, valueMask, false); + + int magic2; + ss.read(reinterpret_cast(&magic2), sizeof(int)); + + CPPUNIT_ASSERT_EQUAL(magic, magic2); + + for (Index i = 0; i < count; i++) { + CPPUNIT_ASSERT_EQUAL(srcBuf.get()[i], destBuf.get()[i]); + } + + io::setStreamMetadataPtr(ss, nullMetadata); + } + + { // repeat but using nullptr for destination to force seek behaviour + ss.str(""); + + io::setStreamMetadataPtr(ss, streamMetadata); + + io::writeCompressedValuesSize(ss, srcBuf.get(), count); + io::writeCompressedValues(ss, srcBuf.get(), count, valueMask, childMask, false); + int magic = 3829250; + ss.write(reinterpret_cast(&magic), sizeof(int)); + + uint16_t size; + ss.read(reinterpret_cast(&size), sizeof(uint16_t)); + uint16_t actualSize(size); + if (size == std::numeric_limits::max()) actualSize = 0; + + CPPUNIT_ASSERT_EQUAL(size_t(actualSize), referenceBytes); + + streamMetadata->setPass(size); + + PointDataIndex32* forceSeek = nullptr; + io::readCompressedValues(ss, forceSeek, count, valueMask, false); + + int magic2; + ss.read(reinterpret_cast(&magic2), sizeof(int)); + + CPPUNIT_ASSERT_EQUAL(magic, magic2); + + io::setStreamMetadataPtr(ss, nullMetadata); + } + +#ifndef OPENVDB_USE_BLOSC + { // write to indicate Blosc compression + std::stringstream ssInvalid; + + uint16_t bytes16(100); // clamp to 16-bit unsigned integer + ssInvalid.write(reinterpret_cast(&bytes16), sizeof(uint16_t)); + + std::unique_ptr destBuf(new PointDataIndex32[count]); + CPPUNIT_ASSERT_THROW(io::readCompressedValues(ssInvalid, destBuf.get(), + count, valueMask, false), RuntimeError); + } +#endif + +#ifdef OPENVDB_USE_BLOSC + { // mis-matching destination bytes cause decompression failures + std::unique_ptr destBuf(new PointDataIndex32[count]); + + ss.str(""); + io::writeCompressedValues(ss, srcBuf.get(), count, valueMask, childMask, false); + CPPUNIT_ASSERT_THROW(io::readCompressedValues(ss, destBuf.get(), + count+1, valueMask, false), RuntimeError); + + ss.str(""); + io::writeCompressedValues(ss, srcBuf.get(), count, valueMask, childMask, false); + CPPUNIT_ASSERT_THROW(io::readCompressedValues(ss, destBuf.get(), + 1, valueMask, false), RuntimeError); + } +#endif + + { // seek + ss.str(""); + + io::writeCompressedValues(ss, srcBuf.get(), count, valueMask, childMask, false); + + int test(10772832); + ss.write(reinterpret_cast(&test), sizeof(int)); + + PointDataIndex32* buf = nullptr; + + io::readCompressedValues(ss, buf, count, valueMask, false); + int test2; + ss.read(reinterpret_cast(&test2), sizeof(int)); + + CPPUNIT_ASSERT_EQUAL(test, test2); + } + } + + { // two values for non-compressible example + std::stringstream ss; + + Index count = 2; + std::unique_ptr srcBuf(new PointDataIndex32[count]); + + for (Index i = 0; i < count; i++) srcBuf[i] = i; + + io::writeCompressedValues(ss, srcBuf.get(), count, valueMask, childMask, false); + + std::unique_ptr destBuf(new PointDataIndex32[count]); + + io::readCompressedValues(ss, destBuf.get(), count, valueMask, false); + + for (Index i = 0; i < count; i++) { + CPPUNIT_ASSERT_EQUAL(srcBuf.get()[i], destBuf.get()[i]); + } + } + + { // throw at limit of 16-bit + std::stringstream ss; + PointDataIndex32* buf = nullptr; + Index count = std::numeric_limits::max(); + + CPPUNIT_ASSERT_THROW(io::writeCompressedValues(ss, buf, count, valueMask, childMask, false), + IoError); + CPPUNIT_ASSERT_THROW(io::readCompressedValues(ss, buf, count, valueMask, false), IoError); + } +} + + +void +TestPointDataLeaf::testIO() +{ + using AttributeVec3s = TypedAttributeArray; + using AttributeF = TypedAttributeArray; + + // create a descriptor + + using Descriptor = AttributeSet::Descriptor; + + Descriptor::Ptr descrA = Descriptor::create(AttributeVec3s::attributeType()); + + // create a leaf and initialize attributes using this descriptor + + const size_t size = LeafType::NUM_VOXELS; + + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.initializeAttributes(descrA, /*arrayLength=*/size/2); + + descrA = descrA->duplicateAppend("density", AttributeF::attributeType()); + leaf.appendAttribute(leaf.attributeSet().descriptor(), descrA, descrA->find("density")); + + // manually activate some voxels + + leaf.setOffsetOn(1, 10); + leaf.setOffsetOn(4, 20); + leaf.setOffsetOn(7, 5); + + // manually change some values in the density array + + TypedAttributeArray& attr = + TypedAttributeArray::cast(leaf.attributeArray("density")); + + attr.set(0, 5.0f); + attr.set(50, 2.0f); + attr.set(51, 8.1f); + + // read and write topology to disk + + { + LeafType leaf2(openvdb::Coord(0, 0, 0)); + + std::ostringstream ostr(std::ios_base::binary); + leaf.writeTopology(ostr); + + std::istringstream istr(ostr.str(), std::ios_base::binary); + leaf2.readTopology(istr); + + // check topology matches + + CPPUNIT_ASSERT_EQUAL(leaf.onVoxelCount(), leaf2.onVoxelCount()); + CPPUNIT_ASSERT(leaf2.isValueOn(4)); + CPPUNIT_ASSERT(!leaf2.isValueOn(5)); + + // check only topology (values and attributes still empty) + + CPPUNIT_ASSERT_EQUAL(leaf2.getValue(4), ValueType(0)); + CPPUNIT_ASSERT_EQUAL(leaf2.attributeSet().size(), size_t(0)); + } + + // read and write buffers to disk + + { + LeafType leaf2(openvdb::Coord(0, 0, 0)); + + io::StreamMetadata::Ptr streamMetadata(new io::StreamMetadata); + + std::ostringstream ostr(std::ios_base::binary); + io::setStreamMetadataPtr(ostr, streamMetadata); + io::setDataCompression(ostr, io::COMPRESS_BLOSC); + leaf.writeTopology(ostr); + for (Index b = 0; b < leaf.buffers(); b++) { + uint32_t pass = (uint32_t(leaf.buffers()) << 16) | uint32_t(b); + streamMetadata->setPass(pass); + leaf.writeBuffers(ostr); + } + { // error checking + streamMetadata->setPass(1000); + leaf.writeBuffers(ostr); + + io::StreamMetadata::Ptr meta; + io::setStreamMetadataPtr(ostr, meta); + CPPUNIT_ASSERT_THROW(leaf.writeBuffers(ostr), openvdb::IoError); + } + + std::istringstream istr(ostr.str(), std::ios_base::binary); + io::setStreamMetadataPtr(istr, streamMetadata); + io::setDataCompression(istr, io::COMPRESS_BLOSC); + + // Since the input stream doesn't include a VDB header with file format version info, + // tag the input stream explicitly with the current version number. + io::setCurrentVersion(istr); + + leaf2.readTopology(istr); + for (Index b = 0; b < leaf.buffers(); b++) { + uint32_t pass = (uint32_t(leaf.buffers()) << 16) | uint32_t(b); + streamMetadata->setPass(pass); + leaf2.readBuffers(istr); + } + + // check topology matches + + CPPUNIT_ASSERT_EQUAL(leaf.onVoxelCount(), leaf2.onVoxelCount()); + CPPUNIT_ASSERT(leaf2.isValueOn(4)); + CPPUNIT_ASSERT(!leaf2.isValueOn(5)); + + // check only topology (values and attributes still empty) + + CPPUNIT_ASSERT_EQUAL(leaf2.getValue(4), ValueType(20)); + CPPUNIT_ASSERT_EQUAL(leaf2.attributeSet().size(), size_t(2)); + } + + { // test multi-buffer IO + // create a new grid with a single origin leaf + + PointDataGrid::Ptr grid = PointDataGrid::create(); + grid->setName("points"); + grid->tree().addLeaf(new LeafType(leaf)); + + openvdb::GridCPtrVec grids; + grids.push_back(grid); + + // write to file + + { + io::File file("leaf.vdb"); + file.write(grids); + file.close(); + } + + { // read grids from file (using delayed loading) + PointDataGrid::Ptr gridFromDisk; + + { + io::File file("leaf.vdb"); + file.open(); + openvdb::GridBase::Ptr baseGrid = file.readGrid("points"); + file.close(); + + gridFromDisk = openvdb::gridPtrCast(baseGrid); + } + + LeafType* leafFromDisk = gridFromDisk->tree().probeLeaf(openvdb::Coord(0, 0, 0)); + CPPUNIT_ASSERT(leafFromDisk); + + CPPUNIT_ASSERT(leaf == *leafFromDisk); + } + + { // read grids from file and pre-fetch + PointDataGrid::Ptr gridFromDisk; + + { + io::File file("leaf.vdb"); + file.open(); + openvdb::GridBase::Ptr baseGrid = file.readGrid("points"); + file.close(); + + gridFromDisk = openvdb::gridPtrCast(baseGrid); + } + + LeafType* leafFromDisk = gridFromDisk->tree().probeLeaf(openvdb::Coord(0, 0, 0)); + CPPUNIT_ASSERT(leafFromDisk); + + const AttributeVec3s& position( + AttributeVec3s::cast(leafFromDisk->constAttributeArray("P"))); + const AttributeF& density( + AttributeF::cast(leafFromDisk->constAttributeArray("density"))); + + CPPUNIT_ASSERT(leafFromDisk->buffer().isOutOfCore()); +#if OPENVDB_USE_BLOSC + CPPUNIT_ASSERT(position.isOutOfCore()); + CPPUNIT_ASSERT(density.isOutOfCore()); +#else + // delayed-loading is only available on attribute arrays when using Blosc + CPPUNIT_ASSERT(!position.isOutOfCore()); + CPPUNIT_ASSERT(!density.isOutOfCore()); +#endif + + // prefetch voxel data only + prefetch(gridFromDisk->tree(), /*position=*/false, /*attributes=*/false); + + // ensure out-of-core data is now in-core after pre-fetching + + CPPUNIT_ASSERT(!leafFromDisk->buffer().isOutOfCore()); +#if OPENVDB_USE_BLOSC + CPPUNIT_ASSERT(position.isOutOfCore()); + CPPUNIT_ASSERT(density.isOutOfCore()); +#else + CPPUNIT_ASSERT(!position.isOutOfCore()); + CPPUNIT_ASSERT(!density.isOutOfCore()); +#endif + + { // re-open + io::File file("leaf.vdb"); + file.open(); + openvdb::GridBase::Ptr baseGrid = file.readGrid("points"); + file.close(); + + gridFromDisk = openvdb::gridPtrCast(baseGrid); + } + + leafFromDisk = gridFromDisk->tree().probeLeaf(openvdb::Coord(0, 0, 0)); + CPPUNIT_ASSERT(leafFromDisk); + + const AttributeVec3s& position2( + AttributeVec3s::cast(leafFromDisk->constAttributeArray("P"))); + const AttributeF& density2( + AttributeF::cast(leafFromDisk->constAttributeArray("density"))); + + // prefetch voxel and position attribute data + prefetch(gridFromDisk->tree(), /*position=*/true, /*attribute=*/false); + + // ensure out-of-core voxel and position data is now in-core after pre-fetching + + CPPUNIT_ASSERT(!leafFromDisk->buffer().isOutOfCore()); + CPPUNIT_ASSERT(!position2.isOutOfCore()); +#if OPENVDB_USE_BLOSC + CPPUNIT_ASSERT(density2.isOutOfCore()); +#else + CPPUNIT_ASSERT(!density2.isOutOfCore()); +#endif + + { // re-open + io::File file("leaf.vdb"); + file.open(); + openvdb::GridBase::Ptr baseGrid = file.readGrid("points"); + file.close(); + + gridFromDisk = openvdb::gridPtrCast(baseGrid); + } + + leafFromDisk = gridFromDisk->tree().probeLeaf(openvdb::Coord(0, 0, 0)); + CPPUNIT_ASSERT(leafFromDisk); + + const AttributeVec3s& position3( + AttributeVec3s::cast(leafFromDisk->constAttributeArray("P"))); + const AttributeF& density3( + AttributeF::cast(leafFromDisk->constAttributeArray("density"))); + + // prefetch all data + prefetch(gridFromDisk->tree()); + + // ensure out-of-core voxel and position data is now in-core after pre-fetching + + CPPUNIT_ASSERT(!leafFromDisk->buffer().isOutOfCore()); + CPPUNIT_ASSERT(!position3.isOutOfCore()); + CPPUNIT_ASSERT(!density3.isOutOfCore()); + } + + remove("leaf.vdb"); + } + + { // test multi-buffer IO with varying attribute storage per-leaf + // create a new grid with three leaf nodes + + PointDataGrid::Ptr grid = PointDataGrid::create(); + grid->setName("points"); + + Descriptor::Ptr descrB = Descriptor::create(AttributeVec3s::attributeType()); + + // create leaf nodes and initialize attributes using this descriptor + + LeafType leaf0(openvdb::Coord(0, 0, 0)); + LeafType leaf1(openvdb::Coord(0, 8, 0)); + LeafType leaf2(openvdb::Coord(0, 0, 8)); + + leaf0.initializeAttributes(descrB, /*arrayLength=*/2); + leaf1.initializeAttributes(descrB, /*arrayLength=*/2); + leaf2.initializeAttributes(descrB, /*arrayLength=*/2); + + descrB = descrB->duplicateAppend("density", AttributeF::attributeType()); + size_t index = descrB->find("density"); + + // append density attribute to leaf 0 and leaf 2 (not leaf 1) + + leaf0.appendAttribute(leaf0.attributeSet().descriptor(), descrB, index); + leaf2.appendAttribute(leaf2.attributeSet().descriptor(), descrB, index); + + // manually change some values in the density array for leaf 0 and leaf 2 + + TypedAttributeArray& attr0 = + TypedAttributeArray::cast(leaf0.attributeArray("density")); + + attr0.set(0, 2.0f); + attr0.set(1, 2.0f); + + attr0.compact(); + + // compact only the attribute array in the second leaf + + TypedAttributeArray& attr2 = + TypedAttributeArray::cast(leaf2.attributeArray("density")); + + attr2.set(0, 5.0f); + attr2.set(1, 5.0f); + + attr2.compact(); + + CPPUNIT_ASSERT(attr0.isUniform()); + CPPUNIT_ASSERT(attr2.isUniform()); + + grid->tree().addLeaf(new LeafType(leaf0)); + grid->tree().addLeaf(new LeafType(leaf1)); + grid->tree().addLeaf(new LeafType(leaf2)); + + openvdb::GridCPtrVec grids; + grids.push_back(grid); + + { // write to file + io::File file("leaf.vdb"); + file.write(grids); + file.close(); + } + + { // read grids from file (using delayed loading) + PointDataGrid::Ptr gridFromDisk; + + { + io::File file("leaf.vdb"); + file.open(); + openvdb::GridBase::Ptr baseGrid = file.readGrid("points"); + file.close(); + + gridFromDisk = openvdb::gridPtrCast(baseGrid); + } + + LeafType* leafFromDisk = gridFromDisk->tree().probeLeaf(openvdb::Coord(0, 0, 0)); + CPPUNIT_ASSERT(leafFromDisk); + CPPUNIT_ASSERT(leaf0 == *leafFromDisk); + + leafFromDisk = gridFromDisk->tree().probeLeaf(openvdb::Coord(0, 8, 0)); + CPPUNIT_ASSERT(leafFromDisk); + CPPUNIT_ASSERT(leaf1 == *leafFromDisk); + + leafFromDisk = gridFromDisk->tree().probeLeaf(openvdb::Coord(0, 0, 8)); + CPPUNIT_ASSERT(leafFromDisk); + CPPUNIT_ASSERT(leaf2 == *leafFromDisk); + } + + remove("leaf.vdb"); + } +} + + +void +TestPointDataLeaf::testSwap() +{ + using AttributeVec3s = TypedAttributeArray; + using AttributeF = TypedAttributeArray; + using AttributeI = TypedAttributeArray; + + // create a descriptor + + using Descriptor = AttributeSet::Descriptor; + + Descriptor::Ptr descrA = Descriptor::create(AttributeVec3s::attributeType()); + + // create a leaf and initialize attributes using this descriptor + + const Index initialArrayLength = 100; + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.initializeAttributes(descrA, /*arrayLength=*/initialArrayLength); + + descrA = descrA->duplicateAppend("density", AttributeF::attributeType()); + leaf.appendAttribute(leaf.attributeSet().descriptor(), descrA, descrA->find("density")); + + descrA = descrA->duplicateAppend("id", AttributeI::attributeType()); + leaf.appendAttribute(leaf.attributeSet().descriptor(), descrA, descrA->find("id")); + + // swap out the underlying attribute set with a new attribute set with a matching + // descriptor + + CPPUNIT_ASSERT_EQUAL(initialArrayLength, leaf.attributeSet().get("density")->size()); + CPPUNIT_ASSERT_EQUAL(initialArrayLength, leaf.attributeSet().get("id")->size()); + + descrA = Descriptor::create(AttributeVec3s::attributeType()); + + const Index newArrayLength = initialArrayLength / 2; + + AttributeSet* newAttributeSet(new AttributeSet(descrA, /*arrayLength*/newArrayLength)); + + newAttributeSet->appendAttribute("density", AttributeF::attributeType()); + newAttributeSet->appendAttribute("id", AttributeI::attributeType()); + + leaf.replaceAttributeSet(newAttributeSet); + + CPPUNIT_ASSERT_EQUAL(newArrayLength, leaf.attributeSet().get("density")->size()); + CPPUNIT_ASSERT_EQUAL(newArrayLength, leaf.attributeSet().get("id")->size()); + + // ensure we refuse to swap when the attribute set is null + + CPPUNIT_ASSERT_THROW(leaf.replaceAttributeSet(nullptr), openvdb::ValueError); + + // ensure we refuse to swap when the descriptors do not match, + // unless we explicitly allow a mismatch. + + Descriptor::Ptr descrB = Descriptor::create(AttributeVec3s::attributeType()); + AttributeSet* attributeSet = new AttributeSet(descrB, newArrayLength); + + attributeSet->appendAttribute("extra", AttributeF::attributeType()); + + CPPUNIT_ASSERT_THROW(leaf.replaceAttributeSet(attributeSet), openvdb::ValueError); + + leaf.replaceAttributeSet(attributeSet, true); + CPPUNIT_ASSERT_EQUAL(const_cast(&leaf.attributeSet()), attributeSet); +} + +void +TestPointDataLeaf::testCopyOnWrite() +{ + using AttributeVec3s = TypedAttributeArray; + using AttributeF = TypedAttributeArray; + + // create a descriptor + + using Descriptor = AttributeSet::Descriptor; + + Descriptor::Ptr descrA = Descriptor::create(AttributeVec3s::attributeType()); + + // create a leaf and initialize attributes using this descriptor + + const Index initialArrayLength = 100; + LeafType leaf(openvdb::Coord(0, 0, 0)); + leaf.initializeAttributes(descrA, /*arrayLength=*/initialArrayLength); + + descrA = descrA->duplicateAppend("density", AttributeF::attributeType()); + leaf.appendAttribute(leaf.attributeSet().descriptor(), descrA, descrA->find("density")); + + const AttributeSet& attributeSet = leaf.attributeSet(); + + CPPUNIT_ASSERT_EQUAL(attributeSet.size(), size_t(2)); + + // ensure attribute arrays are shared between leaf nodes until write + + const LeafType leafCopy(leaf); + + const AttributeSet& attributeSetCopy = leafCopy.attributeSet(); + + CPPUNIT_ASSERT(attributeSet.isShared(/*pos=*/1)); + CPPUNIT_ASSERT(attributeSetCopy.isShared(/*pos=*/1)); + + // test that from a const leaf, accesses to the attribute arrays do not + // make then unique + + const AttributeArray* constArray = attributeSetCopy.getConst(/*pos=*/1); + CPPUNIT_ASSERT(constArray); + + CPPUNIT_ASSERT(attributeSet.isShared(/*pos=*/1)); + CPPUNIT_ASSERT(attributeSetCopy.isShared(/*pos=*/1)); + + constArray = attributeSetCopy.get(/*pos=*/1); + + CPPUNIT_ASSERT(attributeSet.isShared(/*pos=*/1)); + CPPUNIT_ASSERT(attributeSetCopy.isShared(/*pos=*/1)); + + constArray = &(leafCopy.attributeArray(/*pos=*/1)); + + CPPUNIT_ASSERT(attributeSet.isShared(/*pos=*/1)); + CPPUNIT_ASSERT(attributeSetCopy.isShared(/*pos=*/1)); + + constArray = &(leafCopy.attributeArray("density")); + + CPPUNIT_ASSERT(attributeSet.isShared(/*pos=*/1)); + CPPUNIT_ASSERT(attributeSetCopy.isShared(/*pos=*/1)); + + // test makeUnique is called from non const getters + + AttributeArray* attributeArray = &(leaf.attributeArray(/*pos=*/1)); + CPPUNIT_ASSERT(attributeArray); + + CPPUNIT_ASSERT(!attributeSet.isShared(/*pos=*/1)); + CPPUNIT_ASSERT(!attributeSetCopy.isShared(/*pos=*/1)); +} + + +void +TestPointDataLeaf::testCopyDescriptor() +{ + using AttributeVec3s = TypedAttributeArray; + using AttributeS = TypedAttributeArray; + + using LeafNode = PointDataTree::LeafNodeType; + + PointDataTree tree; + + LeafNode* leaf = tree.touchLeaf(openvdb::Coord(0, 0, 0)); + LeafNode* leaf2 = tree.touchLeaf(openvdb::Coord(0, 8, 0)); + + // create a descriptor + + using Descriptor = AttributeSet::Descriptor; + + Descriptor::Inserter names; + names.add("density", AttributeS::attributeType()); + + Descriptor::Ptr descrA = Descriptor::create(AttributeVec3s::attributeType()); + + // initialize attributes using this descriptor + + leaf->initializeAttributes(descrA, /*arrayLength=*/100); + leaf2->initializeAttributes(descrA, /*arrayLength=*/50); + + // copy the PointDataTree and ensure that descriptors are shared + + PointDataTree tree2(tree); + + CPPUNIT_ASSERT_EQUAL(tree2.leafCount(), openvdb::Index32(2)); + + descrA->setGroup("test", size_t(1)); + + PointDataTree::LeafCIter iter2 = tree2.cbeginLeaf(); + CPPUNIT_ASSERT(iter2->attributeSet().descriptor().hasGroup("test")); + ++iter2; + CPPUNIT_ASSERT(iter2->attributeSet().descriptor().hasGroup("test")); + + // call makeDescriptorUnique and ensure that descriptors are no longer shared + + Descriptor::Ptr newDescriptor = makeDescriptorUnique(tree2); + CPPUNIT_ASSERT(newDescriptor); + + descrA->setGroup("test2", size_t(2)); + + iter2 = tree2.cbeginLeaf(); + CPPUNIT_ASSERT(!iter2->attributeSet().descriptor().hasGroup("test2")); + ++iter2; + CPPUNIT_ASSERT(!iter2->attributeSet().descriptor().hasGroup("test2")); +} + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointDataLeaf); diff --git a/openvdb/unittest/TestPointDelete.cc b/openvdb/unittest/TestPointDelete.cc new file mode 100644 index 00000000..551d8cdb --- /dev/null +++ b/openvdb/unittest/TestPointDelete.cc @@ -0,0 +1,249 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#include +#endif + +using namespace openvdb::points; + +class TestPointDelete: public CppUnit::TestCase +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointDelete); + CPPUNIT_TEST(testDeleteFromGroups); + + CPPUNIT_TEST_SUITE_END(); + + void testDeleteFromGroups(); +}; // class TestPointDelete + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointDelete); + +//////////////////////////////////////// + +void +TestPointDelete::testDeleteFromGroups() +{ + using openvdb::math::Vec3s; + using openvdb::tools::PointIndexGrid; + using openvdb::Index64; + + const float voxelSize(1.0); + openvdb::math::Transform::Ptr transform(openvdb::math::Transform::createLinearTransform(voxelSize)); + + const std::vector positions6Points = { + {1, 1, 1}, + {1, 2, 1}, + {2, 1, 1}, + {2, 2, 1}, + {100, 100, 100}, + {100, 101, 100} + }; + const PointAttributeVector pointList6Points(positions6Points); + + { + // delete from a tree with 2 leaves, checking that group membership is updated as + // expected + + PointIndexGrid::Ptr pointIndexGrid = + openvdb::tools::createPointIndexGrid(pointList6Points, *transform); + + PointDataGrid::Ptr grid = + createPointDataGrid(*pointIndexGrid, pointList6Points, + *transform); + PointDataTree& tree = grid->tree(); + + // first test will delete 3 groups, with the third one empty. + + appendGroup(tree, "test1"); + appendGroup(tree, "test2"); + appendGroup(tree, "test3"); + appendGroup(tree, "test4"); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(6)); + + std::vector membership1{1, 0, 0, 0, 0, 1}; + + setGroup(tree, pointIndexGrid->tree(), membership1, "test1"); + + std::vector membership2{0, 0, 1, 1, 0, 1}; + + setGroup(tree, pointIndexGrid->tree(), membership2, "test2"); + + std::vector groupsToDelete{"test1", "test2", "test3"}; + + deleteFromGroups(tree, groupsToDelete); + + // 4 points should have been deleted, so only 2 remain + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(2)); + + // check that first three groups are deleted but the last is not + + const PointDataTree::LeafCIter leafIterAfterDeletion = tree.cbeginLeaf(); + + AttributeSet attributeSetAfterDeletion = leafIterAfterDeletion->attributeSet(); + AttributeSet::Descriptor& descriptor = attributeSetAfterDeletion.descriptor(); + + CPPUNIT_ASSERT(!descriptor.hasGroup("test1")); + CPPUNIT_ASSERT(!descriptor.hasGroup("test2")); + CPPUNIT_ASSERT(!descriptor.hasGroup("test3")); + CPPUNIT_ASSERT(descriptor.hasGroup("test4")); + } + + { + // check deletion from a single leaf tree and that attribute values are preserved + // correctly after deletion + + std::vector positions4Points = { + {1, 1, 1}, + {1, 2, 1}, + {2, 1, 1}, + {2, 2, 1}, + }; + + const PointAttributeVector pointList4Points(positions4Points); + + PointIndexGrid::Ptr pointIndexGrid = + openvdb::tools::createPointIndexGrid(pointList4Points, *transform); + + PointDataGrid::Ptr grid = + createPointDataGrid(*pointIndexGrid, + pointList4Points, *transform); + PointDataTree& tree = grid->tree(); + + appendGroup(tree, "test"); + appendAttribute(tree, "testAttribute", TypedAttributeArray::attributeType()); + + CPPUNIT_ASSERT(tree.beginLeaf()); + + const PointDataTree::LeafIter leafIter = tree.beginLeaf(); + + AttributeWriteHandle + testAttributeWriteHandle(leafIter->attributeArray("testAttribute")); + + for(int i = 0; i < 4; i++) { + testAttributeWriteHandle.set(i,i+1); + } + + std::vector membership{0, 1, 1, 0}; + setGroup(tree, pointIndexGrid->tree(), membership, "test"); + + deleteFromGroup(tree, "test"); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(2)); + + const PointDataTree::LeafCIter leafIterAfterDeletion = tree.cbeginLeaf(); + const AttributeSet attributeSetAfterDeletion = leafIterAfterDeletion->attributeSet(); + const AttributeSet::Descriptor& descriptor = attributeSetAfterDeletion.descriptor(); + + CPPUNIT_ASSERT(descriptor.find("testAttribute") != AttributeSet::INVALID_POS); + + AttributeHandle testAttributeHandle(*attributeSetAfterDeletion.get("testAttribute")); + + CPPUNIT_ASSERT_EQUAL(1, testAttributeHandle.get(0)); + CPPUNIT_ASSERT_EQUAL(4, testAttributeHandle.get(1)); + } + + { + // test the invert flag using data similar to that used in the first test + + PointIndexGrid::Ptr pointIndexGrid = + openvdb::tools::createPointIndexGrid(pointList6Points, *transform); + PointDataGrid::Ptr grid = + createPointDataGrid(*pointIndexGrid, pointList6Points, + *transform); + PointDataTree& tree = grid->tree(); + + appendGroup(tree, "test1"); + appendGroup(tree, "test2"); + appendGroup(tree, "test3"); + appendGroup(tree, "test4"); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(6)); + + std::vector membership1{1, 0, 1, 1, 0, 1}; + + setGroup(tree, pointIndexGrid->tree(), membership1, "test1"); + + std::vector membership2{0, 0, 1, 1, 0, 1}; + + setGroup(tree, pointIndexGrid->tree(), membership2, "test2"); + + std::vector groupsToDelete{"test1", "test3"}; + + deleteFromGroups(tree, groupsToDelete, /*invert=*/ true); + + const PointDataTree::LeafCIter leafIterAfterDeletion = tree.cbeginLeaf(); + const AttributeSet attributeSetAfterDeletion = leafIterAfterDeletion->attributeSet(); + const AttributeSet::Descriptor& descriptor = attributeSetAfterDeletion.descriptor(); + + // no groups should be dropped when invert = true + CPPUNIT_ASSERT_EQUAL(static_cast(descriptor.groupMap().size()), + static_cast(4)); + + // 4 points should remain since test1 and test3 have 4 members between then + CPPUNIT_ASSERT_EQUAL(static_cast(pointCount(tree)), + static_cast(4)); + } + + { + // similar to first test, but don't drop groups + + PointIndexGrid::Ptr pointIndexGrid = + openvdb::tools::createPointIndexGrid(pointList6Points, *transform); + + PointDataGrid::Ptr grid = + createPointDataGrid(*pointIndexGrid, pointList6Points, + *transform); + PointDataTree& tree = grid->tree(); + + // first test will delete 3 groups, with the third one empty. + + appendGroup(tree, "test1"); + appendGroup(tree, "test2"); + appendGroup(tree, "test3"); + appendGroup(tree, "test4"); + + std::vector membership1{1, 0, 0, 0, 0, 1}; + + setGroup(tree, pointIndexGrid->tree(), membership1, "test1"); + + std::vector membership2{0, 0, 1, 1, 0, 1}; + + setGroup(tree, pointIndexGrid->tree(), membership2, "test2"); + + std::vector groupsToDelete{"test1", "test2", "test3"}; + + deleteFromGroups(tree, groupsToDelete, /*invert=*/ false, /*drop=*/ false); + + // 4 points should have been deleted, so only 2 remain + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(2)); + + // check that first three groups are deleted but the last is not + + const PointDataTree::LeafCIter leafIterAfterDeletion = tree.cbeginLeaf(); + + AttributeSet attributeSetAfterDeletion = leafIterAfterDeletion->attributeSet(); + AttributeSet::Descriptor& descriptor = attributeSetAfterDeletion.descriptor(); + + // all group should still be present + + CPPUNIT_ASSERT(descriptor.hasGroup("test1")); + CPPUNIT_ASSERT(descriptor.hasGroup("test2")); + CPPUNIT_ASSERT(descriptor.hasGroup("test3")); + CPPUNIT_ASSERT(descriptor.hasGroup("test4")); + } + +} diff --git a/openvdb/unittest/TestPointGroup.cc b/openvdb/unittest/TestPointGroup.cc new file mode 100644 index 00000000..e76c86cc --- /dev/null +++ b/openvdb/unittest/TestPointGroup.cc @@ -0,0 +1,699 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + +#include // for std::remove() +#include // for std::getenv() +#include +#include +#include +#include + +#ifdef _MSC_VER +#include +#endif + +using namespace openvdb; +using namespace openvdb::points; + +class TestPointGroup: public CppUnit::TestCase +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointGroup); + CPPUNIT_TEST(testDescriptor); + CPPUNIT_TEST(testAppendDrop); + CPPUNIT_TEST(testCompact); + CPPUNIT_TEST(testSet); + CPPUNIT_TEST(testFilter); + + CPPUNIT_TEST_SUITE_END(); + + void testDescriptor(); + void testAppendDrop(); + void testCompact(); + void testSet(); + void testFilter(); +}; // class TestPointGroup + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointGroup); + +//////////////////////////////////////// + + +class FirstFilter +{ +public: + static bool initialized() { return true; } + + static index::State state() { return index::PARTIAL; } + template + static index::State state(const LeafT&) { return index::PARTIAL; } + + template void reset(const LeafT&) { } + + template bool valid(const IterT& iter) const + { + return *iter == 0; + } +}; // class FirstFilter + + +//////////////////////////////////////// + + +namespace { + + bool testStringVector(std::vector& input) + { + return input.size() == 0; + } + + bool testStringVector(std::vector& input, const Name& name1) + { + if (input.size() != 1) return false; + if (input[0] != name1) return false; + return true; + } + + bool testStringVector(std::vector& input, const Name& name1, const Name& name2) + { + if (input.size() != 2) return false; + if (input[0] != name1) return false; + if (input[1] != name2) return false; + return true; + } + +} // namespace + + +void +TestPointGroup::testDescriptor() +{ + // test missing groups deletion + + { // no groups, empty Descriptor + std::vector groups; + AttributeSet::Descriptor descriptor; + deleteMissingPointGroups(groups, descriptor); + CPPUNIT_ASSERT(testStringVector(groups)); + } + + { // one group, empty Descriptor + std::vector groups{"group1"}; + AttributeSet::Descriptor descriptor; + deleteMissingPointGroups(groups, descriptor); + CPPUNIT_ASSERT(testStringVector(groups)); + } + + { // one group, Descriptor with same group + std::vector groups{"group1"}; + AttributeSet::Descriptor descriptor; + descriptor.setGroup("group1", 0); + deleteMissingPointGroups(groups, descriptor); + CPPUNIT_ASSERT(testStringVector(groups, "group1")); + } + + { // one group, Descriptor with different group + std::vector groups{"group1"}; + AttributeSet::Descriptor descriptor; + descriptor.setGroup("group2", 0); + deleteMissingPointGroups(groups, descriptor); + CPPUNIT_ASSERT(testStringVector(groups)); + } + + { // three groups, Descriptor with three groups, one different + std::vector groups{"group1", "group3", "group4"}; + AttributeSet::Descriptor descriptor; + descriptor.setGroup("group1", 0); + descriptor.setGroup("group2", 0); + descriptor.setGroup("group4", 0); + deleteMissingPointGroups(groups, descriptor); + CPPUNIT_ASSERT(testStringVector(groups, "group1", "group4")); + } +} + + +//////////////////////////////////////// + + +void +TestPointGroup::testAppendDrop() +{ + std::vector positions{{1, 1, 1}, {1, 10, 1}, {10, 1, 1}, {10, 10, 1}}; + + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + PointDataGrid::Ptr grid = createPointDataGrid(positions, *transform); + PointDataTree& tree = grid->tree(); + + // check one leaf per point + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), Index32(4)); + + // retrieve first and last leaf attribute sets + + PointDataTree::LeafCIter leafIter = tree.cbeginLeaf(); + const AttributeSet& attributeSet = leafIter->attributeSet(); + + ++leafIter; + ++leafIter; + ++leafIter; + + const AttributeSet& attributeSet4 = leafIter->attributeSet(); + + { // throw on append or drop an empty group + CPPUNIT_ASSERT_THROW(appendGroup(tree, ""), openvdb::KeyError); + CPPUNIT_ASSERT_THROW(dropGroup(tree, ""), openvdb::KeyError); + } + + { // append a group + appendGroup(tree, "test"); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(1)); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test")); + CPPUNIT_ASSERT(attributeSet4.descriptor().hasGroup("test")); + } + + { // append a group with non-unique name (repeat the append) + appendGroup(tree, "test"); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(1)); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test")); + CPPUNIT_ASSERT(attributeSet4.descriptor().hasGroup("test")); + } + + { // append multiple groups + std::vector names{"test2", "test3"}; + + appendGroups(tree, names); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(3)); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test")); + CPPUNIT_ASSERT(attributeSet4.descriptor().hasGroup("test")); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test2")); + CPPUNIT_ASSERT(attributeSet4.descriptor().hasGroup("test2")); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test3")); + CPPUNIT_ASSERT(attributeSet4.descriptor().hasGroup("test3")); + } + + { // append to a copy + PointDataTree tree2(tree); + + appendGroup(tree2, "copy1"); + + CPPUNIT_ASSERT(!attributeSet.descriptor().hasGroup("copy1")); + CPPUNIT_ASSERT(tree2.beginLeaf()->attributeSet().descriptor().hasGroup("copy1")); + } + + { // drop a group + dropGroup(tree, "test2"); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(2)); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test")); + CPPUNIT_ASSERT(attributeSet4.descriptor().hasGroup("test")); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test3")); + CPPUNIT_ASSERT(attributeSet4.descriptor().hasGroup("test3")); + } + + { // drop multiple groups + std::vector names{"test", "test3"}; + + dropGroups(tree, names); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(0)); + } + + { // drop a copy + appendGroup(tree, "copy2"); + + PointDataTree tree2(tree); + + dropGroup(tree2, "copy2"); + + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("copy2")); + CPPUNIT_ASSERT(!tree2.beginLeaf()->attributeSet().descriptor().hasGroup("copy2")); + + dropGroup(tree, "copy2"); + } + + { // set group membership + appendGroup(tree, "test"); + + setGroup(tree, "test", true); + + GroupFilter filter("test", tree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter), Index64(4)); + + setGroup(tree, "test", false); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter), Index64(0)); + + dropGroup(tree, "test"); + } + + { // drop all groups + appendGroup(tree, "test"); + appendGroup(tree, "test2"); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(2)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().count(GroupAttributeArray::attributeType()), size_t(1)); + + dropGroups(tree); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(0)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().count(GroupAttributeArray::attributeType()), size_t(0)); + } + + { // check that newly added groups have empty group membership + + // recreate the grid with 3 points in one leaf + + positions = {{1, 1, 1}, {1, 2, 1}, {2, 1, 1}}; + grid = createPointDataGrid(positions, *transform); + PointDataTree& newTree = grid->tree(); + + appendGroup(newTree, "test"); + + // test that a completely new group (with a new group attribute) + // has empty membership + + CPPUNIT_ASSERT(newTree.cbeginLeaf()); + GroupFilter filter("test", newTree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(newTree, filter), Index64(0)); + + // check that membership in a group that was not created with a + // new attribute array is still empty. + // we will append a second group, set its membership, then + // drop it and append a new group with the same name again + + appendGroup(newTree, "test2"); + + PointDataTree::LeafIter leafIter2 = newTree.beginLeaf(); + CPPUNIT_ASSERT(leafIter2); + + GroupWriteHandle test2Handle = leafIter2->groupWriteHandle("test2"); + + test2Handle.set(0, true); + test2Handle.set(2, true); + + GroupFilter filter2("test2", newTree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(newTree, filter2), Index64(2)); + + // drop and re-add group + + dropGroup(newTree, "test2"); + appendGroup(newTree, "test2"); + + // check that group is fully cleared and does not have previously existing data + + CPPUNIT_ASSERT_EQUAL(pointCount(newTree, filter2), Index64(0)); + } + +} + + +void +TestPointGroup::testCompact() +{ + std::vector positions{{1, 1, 1}}; + + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + PointDataGrid::Ptr grid = createPointDataGrid(positions, *transform); + PointDataTree& tree = grid->tree(); + + // check one leaf + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), Index32(1)); + + // retrieve first and last leaf attribute sets + + PointDataTree::LeafCIter leafIter = tree.cbeginLeaf(); + const AttributeSet& attributeSet = leafIter->attributeSet(); + + std::stringstream ss; + + { // append nine groups + for (int i = 0; i < 8; i++) { + ss.str(""); + ss << "test" << i; + appendGroup(tree, ss.str()); + } + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(8)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().count(GroupAttributeArray::attributeType()), size_t(1)); + + appendGroup(tree, "test8"); + + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test0")); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test7")); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test8")); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(9)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().count(GroupAttributeArray::attributeType()), size_t(2)); + } + + { // drop first attribute then compact + dropGroup(tree, "test5", /*compact=*/false); + + CPPUNIT_ASSERT(!attributeSet.descriptor().hasGroup("test5")); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(8)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().count(GroupAttributeArray::attributeType()), size_t(2)); + + compactGroups(tree); + + CPPUNIT_ASSERT(!attributeSet.descriptor().hasGroup("test5")); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test7")); + CPPUNIT_ASSERT(attributeSet.descriptor().hasGroup("test8")); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(8)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().count(GroupAttributeArray::attributeType()), size_t(1)); + } + + { // append seventeen groups, drop most of them, then compact + for (int i = 0; i < 17; i++) { + ss.str(""); + ss << "test" << i; + appendGroup(tree, ss.str()); + } + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(17)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().count(GroupAttributeArray::attributeType()), size_t(3)); + + // delete all but 0, 5, 9, 15 + + for (int i = 0; i < 17; i++) { + if (i == 0 || i == 5 || i == 9 || i == 15) continue; + ss.str(""); + ss << "test" << i; + dropGroup(tree, ss.str(), /*compact=*/false); + } + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().count(GroupAttributeArray::attributeType()), size_t(3)); + + // make a copy + + PointDataTree tree2(tree); + + // compact - should now occupy one attribute + + compactGroups(tree); + + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().groupMap().size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(attributeSet.descriptor().count(GroupAttributeArray::attributeType()), size_t(1)); + + // check descriptor has been deep copied + + CPPUNIT_ASSERT_EQUAL(tree2.cbeginLeaf()->attributeSet().descriptor().groupMap().size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(tree2.cbeginLeaf()->attributeSet().descriptor().count(GroupAttributeArray::attributeType()), size_t(3)); + } +} + + +void +TestPointGroup::testSet() +{ + // four points in the same leaf + + std::vector positions = { + {1, 1, 1}, + {1, 2, 1}, + {2, 1, 1}, + {2, 2, 1}, + {100, 100, 100}, + {100, 101, 100} + }; + + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + const PointAttributeVector pointList(positions); + + openvdb::tools::PointIndexGrid::Ptr pointIndexGrid = + openvdb::tools::createPointIndexGrid(pointList, *transform); + + PointDataGrid::Ptr grid = createPointDataGrid(*pointIndexGrid, pointList, *transform); + PointDataTree& tree = grid->tree(); + + appendGroup(tree, "test"); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(6)); + GroupFilter filter("test", tree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter), Index64(0)); + + // copy tree for descriptor sharing test + + PointDataTree tree2(tree); + + std::vector membership{1, 0, 1, 1, 0, 1}; + + // test add to group + + setGroup(tree, "test", true); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter), Index64(6)); + + // test nothing is done if the index tree contains no valid indices + + tools::PointIndexGrid::Ptr tmpIndexGrid = tools::PointIndexGrid::create(); + setGroup(tree, tmpIndexGrid->tree(), {0,0,0,0,0,0}, "test", /*remove*/true); + CPPUNIT_ASSERT_EQUAL(Index64(6), pointCount(tree, filter)); + + // test throw on out of range index + + auto indexLeaf = tmpIndexGrid->tree().touchLeaf(tree.cbeginLeaf()->origin()); + indexLeaf->indices().emplace_back(membership.size()); + CPPUNIT_ASSERT_THROW(setGroup(tree, tmpIndexGrid->tree(), membership, "test"), IndexError); + CPPUNIT_ASSERT_EQUAL(Index64(6), pointCount(tree, filter)); + + // test remove flag + + setGroup(tree, pointIndexGrid->tree(), membership, "test", /*remove*/false); + CPPUNIT_ASSERT_EQUAL(Index64(6), pointCount(tree, filter)); + + setGroup(tree, pointIndexGrid->tree(), membership, "test", /*remove*/true); + CPPUNIT_ASSERT_EQUAL(Index64(4), pointCount(tree, filter)); + + setGroup(tree, pointIndexGrid->tree(), {0,1,0,0,1,0}, "test", /*remove*/false); + CPPUNIT_ASSERT_EQUAL(Index64(6), pointCount(tree, filter)); + + setGroup(tree, pointIndexGrid->tree(), membership, "test", /*remove*/true); + + // check that descriptor remains shared + + appendGroup(tree2, "copy1"); + + CPPUNIT_ASSERT(!tree.cbeginLeaf()->attributeSet().descriptor().hasGroup("copy1")); + + dropGroup(tree2, "copy1"); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(6)); + GroupFilter filter2("test", tree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter2), Index64(4)); + + { // IO + // setup temp directory + + std::string tempDir; + if (const char* dir = std::getenv("TMPDIR")) tempDir = dir; +#ifdef _MSC_VER + if (tempDir.empty()) { + char tempDirBuffer[MAX_PATH+1]; + int tempDirLen = GetTempPath(MAX_PATH+1, tempDirBuffer); + CPPUNIT_ASSERT(tempDirLen > 0 && tempDirLen <= MAX_PATH); + tempDir = tempDirBuffer; + } +#else + if (tempDir.empty()) tempDir = P_tmpdir; +#endif + + std::string filename; + + // write out grid to a temp file + { + filename = tempDir + "/openvdb_test_point_load"; + + io::File fileOut(filename); + + GridCPtrVec grids{grid}; + + fileOut.write(grids); + } + + // read test groups + { + io::File fileIn(filename); + fileIn.open(); + + GridPtrVecPtr grids = fileIn.getGrids(); + + fileIn.close(); + + CPPUNIT_ASSERT_EQUAL(grids->size(), size_t(1)); + + PointDataGrid::Ptr inputGrid = GridBase::grid((*grids)[0]); + PointDataTree& treex = inputGrid->tree(); + + CPPUNIT_ASSERT(treex.cbeginLeaf()); + + const PointDataGrid::TreeType::LeafNodeType& leaf = *treex.cbeginLeaf(); + + const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor(); + + CPPUNIT_ASSERT(descriptor.hasGroup("test")); + CPPUNIT_ASSERT_EQUAL(descriptor.groupMap().size(), size_t(1)); + + CPPUNIT_ASSERT_EQUAL(pointCount(treex), Index64(6)); + GroupFilter filter3("test", leaf.attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(treex, filter3), Index64(4)); + } + std::remove(filename.c_str()); + } +} + + +void +TestPointGroup::testFilter() +{ + const float voxelSize(1.0); + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + PointDataGrid::Ptr grid; + + { // four points in the same leaf + std::vector positions = { + {1, 1, 1}, + {1, 2, 1}, + {2, 1, 1}, + {2, 2, 1}, + {100, 100, 100}, + {100, 101, 100} + }; + + const PointAttributeVector pointList(positions); + + openvdb::tools::PointIndexGrid::Ptr pointIndexGrid = + openvdb::tools::createPointIndexGrid(pointList, *transform); + + grid = createPointDataGrid(*pointIndexGrid, pointList, *transform); + } + + PointDataTree& tree = grid->tree(); + + { // first point filter + appendGroup(tree, "first"); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(6)); + GroupFilter filter("first", tree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter), Index64(0)); + + FirstFilter filter2; + + setGroupByFilter(tree, "first", filter2); + + auto iter = tree.cbeginLeaf(); + + for ( ; iter; ++iter) { + CPPUNIT_ASSERT_EQUAL(iter->groupPointCount("first"), Index64(1)); + } + + GroupFilter filter3("first", tree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter3), Index64(2)); + } + + const openvdb::BBoxd bbox(openvdb::Vec3d(0, 1.5, 0), openvdb::Vec3d(101, 100.5, 101)); + + { // bbox filter + appendGroup(tree, "bbox"); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(6)); + GroupFilter filter("bbox", tree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter), Index64(0)); + + BBoxFilter filter2(*transform, bbox); + + setGroupByFilter(tree, "bbox", filter2); + + GroupFilter filter3("bbox", tree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter3), Index64(3)); + } + + { // first point filter and bbox filter (intersection of the above two filters) + appendGroup(tree, "first_bbox"); + + CPPUNIT_ASSERT_EQUAL(pointCount(tree), Index64(6)); + GroupFilter filter("first_bbox", tree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter), Index64(0)); + + using FirstBBoxFilter = BinaryFilter; + + FirstFilter firstFilter; + BBoxFilter bboxFilter(*transform, bbox); + FirstBBoxFilter filter2(firstFilter, bboxFilter); + + setGroupByFilter(tree, "first_bbox", filter2); + + GroupFilter filter3("first_bbox", tree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(tree, filter3), Index64(1)); + + std::vector positions; + + for (auto iter = tree.cbeginLeaf(); iter; ++iter) { + GroupFilter filterx("first_bbox", iter->attributeSet()); + auto filterIndexIter = iter->beginIndexOn(filterx); + + auto handle = AttributeHandle::create(iter->attributeArray("P")); + + for ( ; filterIndexIter; ++filterIndexIter) { + const openvdb::Coord ijk = filterIndexIter.getCoord(); + positions.push_back(handle->get(*filterIndexIter) + ijk.asVec3d()); + } + } + + CPPUNIT_ASSERT_EQUAL(positions.size(), size_t(1)); + CPPUNIT_ASSERT_EQUAL(positions[0], Vec3f(100, 100, 100)); + } + + { // add 1000 points in three leafs (positions aren't important) + + std::vector positions(1000, {1, 1, 1}); + positions.insert(positions.end(), 1000, {1, 1, 9}); + positions.insert(positions.end(), 1000, {9, 9, 9}); + + const PointAttributeVector pointList(positions); + + openvdb::tools::PointIndexGrid::Ptr pointIndexGrid = + openvdb::tools::createPointIndexGrid(pointList, *transform); + + grid = createPointDataGrid(*pointIndexGrid, pointList, *transform); + + PointDataTree& newTree = grid->tree(); + + CPPUNIT_ASSERT_EQUAL(pointCount(newTree), Index64(3000)); + + // random - maximum + + appendGroup(newTree, "random_maximum"); + + const Index64 target = 1001; + + setGroupByRandomTarget(newTree, "random_maximum", target); + + GroupFilter filter("random_maximum", newTree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(newTree, filter), target); + + // random - percentage + + appendGroup(newTree, "random_percentage"); + + setGroupByRandomPercentage(newTree, "random_percentage", 33.333333f); + + GroupFilter filter2("random_percentage", newTree.cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(pointCount(newTree, filter2), Index64(1000)); + } +} diff --git a/openvdb/unittest/TestPointIndexGrid.cc b/openvdb/unittest/TestPointIndexGrid.cc new file mode 100644 index 00000000..17f83939 --- /dev/null +++ b/openvdb/unittest/TestPointIndexGrid.cc @@ -0,0 +1,316 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + +#include +#include +#include +#include "util.h" // for genPoints + + +struct TestPointIndexGrid: public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(TestPointIndexGrid); + CPPUNIT_TEST(testPointIndexGrid); + CPPUNIT_TEST(testPointIndexFilter); + CPPUNIT_TEST(testWorldSpaceSearchAndUpdate); + CPPUNIT_TEST_SUITE_END(); + + void testPointIndexGrid(); + void testPointIndexFilter(); + void testWorldSpaceSearchAndUpdate(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointIndexGrid); + +//////////////////////////////////////// + +namespace { + +class PointList +{ +public: + typedef openvdb::Vec3R PosType; + + PointList(const std::vector& points) + : mPoints(&points) + { + } + + size_t size() const { + return mPoints->size(); + } + + void getPos(size_t n, PosType& xyz) const { + xyz = (*mPoints)[n]; + } + +protected: + std::vector const * const mPoints; +}; // PointList + + +template +bool hasDuplicates(const std::vector& items) +{ + std::vector vec(items); + std::sort(vec.begin(), vec.end()); + + size_t duplicates = 0; + for (size_t n = 1, N = vec.size(); n < N; ++n) { + if (vec[n] == vec[n-1]) ++duplicates; + } + return duplicates != 0; +} + + +template +struct WeightedAverageAccumulator { + typedef T ValueType; + WeightedAverageAccumulator(T const * const array, const T radius) + : mValues(array), mInvRadius(1.0/radius), mWeightSum(0.0), mValueSum(0.0) {} + + void reset() { mWeightSum = mValueSum = T(0.0); } + + void operator()(const T distSqr, const size_t pointIndex) { + const T weight = T(1.0) - openvdb::math::Sqrt(distSqr) * mInvRadius; + mWeightSum += weight; + mValueSum += weight * mValues[pointIndex]; + } + + T result() const { return mWeightSum > T(0.0) ? mValueSum / mWeightSum : T(0.0); } + +private: + T const * const mValues; + const T mInvRadius; + T mWeightSum, mValueSum; +}; // struct WeightedAverageAccumulator + +} // namespace + + + +//////////////////////////////////////// + + +void +TestPointIndexGrid::testPointIndexGrid() +{ + const float voxelSize = 0.01f; + const openvdb::math::Transform::Ptr transform = + openvdb::math::Transform::createLinearTransform(voxelSize); + + // generate points + + std::vector points; + unittest_util::genPoints(40000, points); + + PointList pointList(points); + + + // construct data structure + typedef openvdb::tools::PointIndexGrid PointIndexGrid; + + PointIndexGrid::Ptr pointGridPtr = + openvdb::tools::createPointIndexGrid(pointList, *transform); + + openvdb::CoordBBox bbox; + pointGridPtr->tree().evalActiveVoxelBoundingBox(bbox); + + // coord bbox search + + typedef PointIndexGrid::ConstAccessor ConstAccessor; + typedef openvdb::tools::PointIndexIterator<> PointIndexIterator; + + ConstAccessor acc = pointGridPtr->getConstAccessor(); + PointIndexIterator it(bbox, acc); + + CPPUNIT_ASSERT(it.test()); + CPPUNIT_ASSERT_EQUAL(points.size(), it.size()); + + // fractional bbox search + + openvdb::BBoxd region(bbox.min().asVec3d(), bbox.max().asVec3d()); + + // points are bucketed in a cell-centered fashion, we need to pad the + // coordinate range to get the same search region in the fractional bbox. + region.expand(voxelSize * 0.5); + + it.searchAndUpdate(region, acc, pointList, *transform); + + CPPUNIT_ASSERT(it.test()); + CPPUNIT_ASSERT_EQUAL(points.size(), it.size()); + + { + std::vector vec; + vec.reserve(it.size()); + for (; it; ++it) { + vec.push_back(*it); + } + + CPPUNIT_ASSERT_EQUAL(vec.size(), it.size()); + CPPUNIT_ASSERT(!hasDuplicates(vec)); + } + + // radial search + openvdb::Vec3d center = region.getCenter(); + double radius = region.extents().x() * 0.5; + it.searchAndUpdate(center, radius, acc, pointList, *transform); + + CPPUNIT_ASSERT(it.test()); + CPPUNIT_ASSERT_EQUAL(points.size(), it.size()); + + { + std::vector vec; + vec.reserve(it.size()); + for (; it; ++it) { + vec.push_back(*it); + } + + CPPUNIT_ASSERT_EQUAL(vec.size(), it.size()); + CPPUNIT_ASSERT(!hasDuplicates(vec)); + } + + + center = region.min(); + it.searchAndUpdate(center, radius, acc, pointList, *transform); + + CPPUNIT_ASSERT(it.test()); + + { + std::vector vec; + vec.reserve(it.size()); + for (; it; ++it) { + vec.push_back(*it); + } + + CPPUNIT_ASSERT_EQUAL(vec.size(), it.size()); + CPPUNIT_ASSERT(!hasDuplicates(vec)); + + // check that no points where missed. + + std::vector indexMask(points.size(), 0); + for (size_t n = 0, N = vec.size(); n < N; ++n) { + indexMask[vec[n]] = 1; + } + + const double r2 = radius * radius; + openvdb::Vec3R v; + for (size_t n = 0, N = indexMask.size(); n < N; ++n) { + v = center - transform->worldToIndex(points[n]); + if (indexMask[n] == 0) { + CPPUNIT_ASSERT(!(v.lengthSqr() < r2)); + } else { + CPPUNIT_ASSERT(v.lengthSqr() < r2); + } + } + } + + + // Check partitioning + + CPPUNIT_ASSERT(openvdb::tools::isValidPartition(pointList, *pointGridPtr)); + + points[10000].x() += 1.5; // manually modify a few points. + points[20000].x() += 1.5; + points[30000].x() += 1.5; + + CPPUNIT_ASSERT(!openvdb::tools::isValidPartition(pointList, *pointGridPtr)); + + PointIndexGrid::Ptr pointGrid2Ptr = + openvdb::tools::getValidPointIndexGrid(pointList, pointGridPtr); + + CPPUNIT_ASSERT(openvdb::tools::isValidPartition(pointList, *pointGrid2Ptr)); +} + + +void +TestPointIndexGrid::testPointIndexFilter() +{ + // generate points + const float voxelSize = 0.01f; + const size_t pointCount = 10000; + const openvdb::math::Transform::Ptr transform = + openvdb::math::Transform::createLinearTransform(voxelSize); + + std::vector points; + unittest_util::genPoints(pointCount, points); + + PointList pointList(points); + + // construct data structure + typedef openvdb::tools::PointIndexGrid PointIndexGrid; + + PointIndexGrid::Ptr pointGridPtr = + openvdb::tools::createPointIndexGrid(pointList, *transform); + + + std::vector pointDensity(pointCount, 1.0); + + openvdb::tools::PointIndexFilter + filter(pointList, pointGridPtr->tree(), pointGridPtr->transform()); + + const double radius = 3.0 * voxelSize; + + WeightedAverageAccumulator + accumulator(&pointDensity.front(), radius); + + double sum = 0.0; + for (size_t n = 0, N = points.size(); n < N; ++n) { + accumulator.reset(); + filter.searchAndApply(points[n], radius, accumulator); + sum += accumulator.result(); + } + + CPPUNIT_ASSERT_DOUBLES_EQUAL(sum, double(points.size()), 1e-6); +} + + +void +TestPointIndexGrid::testWorldSpaceSearchAndUpdate() +{ + // Create random particles in a cube. + openvdb::math::Rand01<> rnd(0); + + const size_t N = 1000000; + std::vector pos; + pos.reserve(N); + + // Create a box to query points. + openvdb::BBoxd wsBBox(openvdb::Vec3d(0.25), openvdb::Vec3d(0.75)); + + std::set indexListA; + + for (size_t i = 0; i < N; ++i) { + openvdb::Vec3d p(rnd(), rnd(), rnd()); + pos.push_back(p); + + if (wsBBox.isInside(p)) { + indexListA.insert(i); + } + } + + // Create a point index grid + const double dx = 0.025; + openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform(dx); + + PointList pointArray(pos); + openvdb::tools::PointIndexGrid::Ptr pointIndexGrid + = openvdb::tools::createPointIndexGrid(pointArray, *transform); + + // Search for points within the box. + openvdb::tools::PointIndexGrid::ConstAccessor acc = pointIndexGrid->getConstAccessor(); + + openvdb::tools::PointIndexIterator pointIndexIter; + pointIndexIter.worldSpaceSearchAndUpdate(wsBBox, acc, pointArray, pointIndexGrid->transform()); + + std::set indexListB; + for (; pointIndexIter; ++pointIndexIter) { + indexListB.insert(*pointIndexIter); + } + + CPPUNIT_ASSERT_EQUAL(indexListA.size(), indexListB.size()); +} + diff --git a/openvdb/unittest/TestPointMask.cc b/openvdb/unittest/TestPointMask.cc new file mode 100644 index 00000000..9d9c9f37 --- /dev/null +++ b/openvdb/unittest/TestPointMask.cc @@ -0,0 +1,352 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace openvdb; +using namespace openvdb::points; + +class TestPointMask: public CppUnit::TestCase +{ +public: + + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointMask); + CPPUNIT_TEST(testMask); + CPPUNIT_TEST(testMaskDeformer); + CPPUNIT_TEST_SUITE_END(); + + void testMask(); + void testMaskDeformer(); + +}; // class TestPointMask + + +void +TestPointMask::testMask() +{ + std::vector positions = { + {1, 1, 1}, + {1, 5, 1}, + {2, 1, 1}, + {2, 2, 1}, + }; + + const PointAttributeVector pointList(positions); + + const float voxelSize = 0.1f; + openvdb::math::Transform::Ptr transform( + openvdb::math::Transform::createLinearTransform(voxelSize)); + + tools::PointIndexGrid::Ptr pointIndexGrid = + tools::createPointIndexGrid(pointList, *transform); + + PointDataGrid::Ptr points = + createPointDataGrid(*pointIndexGrid, + pointList, *transform); + + { // simple topology copy + auto mask = convertPointsToMask(*points); + + CPPUNIT_ASSERT_EQUAL(points->tree().activeVoxelCount(), Index64(4)); + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(4)); + } + + { // mask grid instead of bool grid + auto mask = convertPointsToMask(*points); + + CPPUNIT_ASSERT_EQUAL(points->tree().activeVoxelCount(), Index64(4)); + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(4)); + } + + { // identical transform + auto mask = convertPointsToMask(*points, *transform); + + CPPUNIT_ASSERT_EQUAL(points->tree().activeVoxelCount(), Index64(4)); + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(4)); + } + + // assign point 3 to new group "test" + + appendGroup(points->tree(), "test"); + + std::vector groups{0,0,1,0}; + + setGroup(points->tree(), pointIndexGrid->tree(), groups, "test"); + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + + { // convert in turn "test" and not "test" + MultiGroupFilter filter(includeGroups, excludeGroups, + points->tree().cbeginLeaf()->attributeSet()); + auto mask = convertPointsToMask(*points, filter); + + CPPUNIT_ASSERT_EQUAL(points->tree().activeVoxelCount(), Index64(4)); + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(1)); + + MultiGroupFilter filter2(excludeGroups, includeGroups, + points->tree().cbeginLeaf()->attributeSet()); + mask = convertPointsToMask(*points, filter2); + + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(3)); + } + + { // use a much larger voxel size that splits the points into two regions + const float newVoxelSize(4); + openvdb::math::Transform::Ptr newTransform( + openvdb::math::Transform::createLinearTransform(newVoxelSize)); + + auto mask = convertPointsToMask(*points, *newTransform); + + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(2)); + + MultiGroupFilter filter(includeGroups, excludeGroups, + points->tree().cbeginLeaf()->attributeSet()); + mask = convertPointsToMask(*points, *newTransform, filter); + + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(1)); + + MultiGroupFilter filter2(excludeGroups, includeGroups, + points->tree().cbeginLeaf()->attributeSet()); + mask = convertPointsToMask(*points, *newTransform, filter2); + + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(2)); + } +} + + +struct StaticVoxelDeformer +{ + StaticVoxelDeformer(const Vec3d& position) + : mPosition(position) { } + + template + void reset(LeafT& /*leaf*/, size_t /*idx*/) { } + + template + void apply(Vec3d& position, IterT&) const { position = mPosition; } + +private: + Vec3d mPosition; +}; + +template +struct YOffsetDeformer +{ + YOffsetDeformer(const Vec3d& offset) : mOffset(offset) { } + + template + void reset(LeafT& /*leaf*/, size_t /*idx*/) { } + + template + void apply(Vec3d& position, IterT&) const { position += mOffset; } + + Vec3d mOffset; +}; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace points { + +// configure both voxel deformers to be applied in index-space + +template<> +struct DeformerTraits { + static const bool IndexSpace = true; +}; + +template<> +struct DeformerTraits> { + static const bool IndexSpace = true; +}; + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +void +TestPointMask::testMaskDeformer() +{ + // This test validates internal functionality that is used in various applications, such as + // building masks and producing count grids. Note that by convention, methods that live + // in an "internal" namespace are typically not promoted as part of the public API + // and thus do not receive the same level of rigour in avoiding breaking API changes. + + std::vector positions = { + {1, 1, 1}, + {1, 5, 1}, + {2, 1, 1}, + {2, 2, 1}, + }; + + const PointAttributeVector pointList(positions); + + const float voxelSize = 0.1f; + openvdb::math::Transform::Ptr transform( + openvdb::math::Transform::createLinearTransform(voxelSize)); + + tools::PointIndexGrid::Ptr pointIndexGrid = + tools::createPointIndexGrid(pointList, *transform); + + PointDataGrid::Ptr points = + createPointDataGrid(*pointIndexGrid, + pointList, *transform); + + // assign point 3 to new group "test" + + appendGroup(points->tree(), "test"); + + std::vector groups{0,0,1,0}; + + setGroup(points->tree(), pointIndexGrid->tree(), groups, "test"); + + NullFilter nullFilter; + + { // null deformer + NullDeformer deformer; + + auto mask = point_mask_internal::convertPointsToScalar( + *points, *transform, nullFilter, deformer); + + auto mask2 = convertPointsToMask(*points); + + CPPUNIT_ASSERT_EQUAL(points->tree().activeVoxelCount(), Index64(4)); + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(4)); + CPPUNIT_ASSERT(mask->tree().hasSameTopology(mask2->tree())); + CPPUNIT_ASSERT(mask->tree().hasSameTopology(points->tree())); + } + + { // static voxel deformer + // collapse all points into a random voxel at (9, 13, 106) + StaticVoxelDeformer deformer(Vec3d(9, 13, 106)); + + auto mask = point_mask_internal::convertPointsToScalar( + *points, *transform, nullFilter, deformer); + + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(1)); + CPPUNIT_ASSERT(!mask->tree().cbeginLeaf()->isValueOn(Coord(9, 13, 105))); + CPPUNIT_ASSERT(mask->tree().cbeginLeaf()->isValueOn(Coord(9, 13, 106))); + } + + { // +y offset deformer + Vec3d offset(0, 41.7, 0); + YOffsetDeformer deformer(offset); + + auto mask = point_mask_internal::convertPointsToScalar( + *points, *transform, nullFilter, deformer); + + // (repeat with deformer configured as world-space) + YOffsetDeformer deformerWS(offset * voxelSize); + + auto maskWS = point_mask_internal::convertPointsToScalar( + *points, *transform, nullFilter, deformerWS); + + CPPUNIT_ASSERT_EQUAL(mask->tree().activeVoxelCount(), Index64(4)); + CPPUNIT_ASSERT_EQUAL(maskWS->tree().activeVoxelCount(), Index64(4)); + + std::vector maskVoxels; + std::vector maskVoxelsWS; + std::vector pointVoxels; + + for (auto leaf = mask->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + maskVoxels.emplace_back(iter.getCoord()); + } + } + + for (auto leaf = maskWS->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + maskVoxelsWS.emplace_back(iter.getCoord()); + } + } + + for (auto leaf = points->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + pointVoxels.emplace_back(iter.getCoord()); + } + } + + std::sort(maskVoxels.begin(), maskVoxels.end()); + std::sort(maskVoxelsWS.begin(), maskVoxelsWS.end()); + std::sort(pointVoxels.begin(), pointVoxels.end()); + + CPPUNIT_ASSERT_EQUAL(maskVoxels.size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(maskVoxelsWS.size(), size_t(4)); + CPPUNIT_ASSERT_EQUAL(pointVoxels.size(), size_t(4)); + + for (int i = 0; i < int(pointVoxels.size()); i++) { + Coord newCoord(pointVoxels[i]); + newCoord.x() = static_cast(newCoord.x() + offset.x()); + newCoord.y() = static_cast(math::Round(newCoord.y() + offset.y())); + newCoord.z() = static_cast(newCoord.z() + offset.z()); + CPPUNIT_ASSERT_EQUAL(maskVoxels[i], newCoord); + CPPUNIT_ASSERT_EQUAL(maskVoxelsWS[i], newCoord); + } + + // use a different transform to verify deformers and transforms can be used together + + const float newVoxelSize = 0.02f; + openvdb::math::Transform::Ptr newTransform( + openvdb::math::Transform::createLinearTransform(newVoxelSize)); + + auto mask2 = point_mask_internal::convertPointsToScalar( + *points, *newTransform, nullFilter, deformer); + + CPPUNIT_ASSERT_EQUAL(mask2->tree().activeVoxelCount(), Index64(4)); + + std::vector maskVoxels2; + + for (auto leaf = mask2->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + maskVoxels2.emplace_back(iter.getCoord()); + } + } + + std::sort(maskVoxels2.begin(), maskVoxels2.end()); + + for (int i = 0; i < int(maskVoxels.size()); i++) { + Coord newCoord(pointVoxels[i]); + newCoord.x() = static_cast((newCoord.x() + offset.x()) * 5); + newCoord.y() = static_cast(math::Round((newCoord.y() + offset.y()) * 5)); + newCoord.z() = static_cast((newCoord.z() + offset.z()) * 5); + CPPUNIT_ASSERT_EQUAL(maskVoxels2[i], newCoord); + } + + // only use points in group "test" + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + MultiGroupFilter filter(includeGroups, excludeGroups, + points->tree().cbeginLeaf()->attributeSet()); + + auto mask3 = point_mask_internal::convertPointsToScalar( + *points, *transform, filter, deformer); + + CPPUNIT_ASSERT_EQUAL(mask3->tree().activeVoxelCount(), Index64(1)); + + for (auto leaf = mask3->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + Coord newCoord(pointVoxels[2]); + newCoord.x() = static_cast(newCoord.x() + offset.x()); + newCoord.y() = static_cast(math::Round(newCoord.y() + offset.y())); + newCoord.z() = static_cast(newCoord.z() + offset.z()); + CPPUNIT_ASSERT_EQUAL(iter.getCoord(), newCoord); + } + } + } +} + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointMask); diff --git a/openvdb/unittest/TestPointMove.cc b/openvdb/unittest/TestPointMove.cc new file mode 100644 index 00000000..4820c40a --- /dev/null +++ b/openvdb/unittest/TestPointMove.cc @@ -0,0 +1,1176 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include "util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace openvdb; +using namespace openvdb::points; + +class TestPointMove: public CppUnit::TestCase +{ +public: + + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointMove); + CPPUNIT_TEST(testCachedDeformer); + CPPUNIT_TEST(testMoveLocal); + CPPUNIT_TEST(testMoveGlobal); + CPPUNIT_TEST(testCustomDeformer); + CPPUNIT_TEST(testPointData); + CPPUNIT_TEST(testPointOrder); + CPPUNIT_TEST_SUITE_END(); + + void testCachedDeformer(); + void testMoveLocal(); + void testMoveGlobal(); + void testCustomDeformer(); + void testPointData(); + void testPointOrder(); +}; // class TestPointMove + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointMove); + + +//////////////////////////////////////// + + +namespace { + +struct OffsetDeformer +{ + OffsetDeformer(const Vec3d& _offset) + : offset(_offset){ } + + template + void reset(const LeafT&, size_t /*idx*/) { } + + template + void apply(Vec3d& position, const IndexIterT&) const + { + position += offset; + } + + Vec3d offset; +}; // struct OffsetDeformer + +template +struct OffsetFilteredDeformer +{ + OffsetFilteredDeformer(const Vec3d& _offset, + const FilterT& _filter) + : offset(_offset) + , filter(_filter) { } + + template + void reset(const LeafT& leaf, size_t /*idx*/) + { + filter.template reset(leaf); + } + + template + void apply(Vec3d& position, const IndexIterT& iter) const + { + if (!filter.template valid(iter)) return; + position += offset; + } + + //void finalize() const { } + + Vec3d offset; + FilterT filter; +}; // struct OffsetFilteredDeformer + + +PointDataGrid::Ptr +positionsToGrid(const std::vector& positions, const float voxelSize = 1.0) +{ + const PointAttributeVector pointList(positions); + + openvdb::math::Transform::Ptr transform( + openvdb::math::Transform::createLinearTransform(voxelSize)); + + tools::PointIndexGrid::Ptr pointIndexGrid = + tools::createPointIndexGrid(pointList, *transform); + + PointDataGrid::Ptr points = + createPointDataGrid(*pointIndexGrid, + pointList, *transform); + + // assign point 3 to new group "test" if more than 3 points + + if (positions.size() > 3) { + appendGroup(points->tree(), "test"); + + std::vector groups(positions.size(), 0); + groups[2] = 1; + + setGroup(points->tree(), pointIndexGrid->tree(), groups, "test"); + } + + return points; +} + + +std::vector +gridToPositions(const PointDataGrid::Ptr& points, bool sort = true) +{ + std::vector positions; + + for (auto leaf = points->tree().beginLeaf(); leaf; ++leaf) { + + const openvdb::points::AttributeArray& positionArray = + leaf->constAttributeArray("P"); + openvdb::points::AttributeHandle positionHandle(positionArray); + + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + openvdb::Vec3f voxelPosition = positionHandle.get(*iter); + openvdb::Vec3d xyz = iter.getCoord().asVec3d(); + openvdb::Vec3f worldPosition = points->transform().indexToWorld(voxelPosition + xyz); + + positions.push_back(worldPosition); + } + } + + if (sort) std::sort(positions.begin(), positions.end()); + return positions; +} + + +std::vector +applyOffset(const std::vector& positions, const Vec3s& offset) +{ + std::vector newPositions; + + for (const auto& it : positions) { + newPositions.emplace_back(it + offset); + } + + std::sort(newPositions.begin(), newPositions.end()); + return newPositions; +} + + +template +inline void +ASSERT_APPROX_EQUAL(const std::vector& a, const std::vector& b, + const Index lineNumber, const double /*tolerance*/ = 1e-6) +{ + std::stringstream ss; + ss << "Assertion Line Number: " << lineNumber; + + CPPUNIT_ASSERT_EQUAL_MESSAGE(ss.str(), a.size(), b.size()); + + for (int i = 0; i < a.size(); i++) { + CPPUNIT_ASSERT_MESSAGE(ss.str(), math::isApproxEqual(a[i], b[i])); + } +} + + +template +inline void +ASSERT_APPROX_EQUAL(const std::vector>& a, const std::vector>& b, + const Index lineNumber, const double /*tolerance*/ = 1e-6) +{ + std::stringstream ss; + ss << "Assertion Line Number: " << lineNumber; + + CPPUNIT_ASSERT_EQUAL_MESSAGE(ss.str(), a.size(), b.size()); + + for (size_t i = 0; i < a.size(); i++) { + CPPUNIT_ASSERT_MESSAGE(ss.str(), math::isApproxEqual(a[i], b[i])); + } +} + +struct NullObject { }; + +// A dummy iterator that can be used to match LeafIter and IndexIter interfaces +struct DummyIter +{ + DummyIter(Index _index): index(_index) { } + //Index pos() const { return index; } + Index operator*() const { return index; } + Index index; +}; + +struct OddIndexFilter +{ + static bool initialized() { return true; } + static index::State state() { return index::PARTIAL; } + template + static index::State state(const LeafT&) { return index::PARTIAL; } + + template + void reset(const LeafT&) { } + template + bool valid(const IterT& iter) const { + return ((*iter) % 2) == 1; + } +}; + +} // namespace + + +void +TestPointMove::testCachedDeformer() +{ + NullObject nullObject; + + // create an empty cache and CachedDeformer + CachedDeformer::Cache cache; + CPPUNIT_ASSERT(cache.leafs.empty()); + + CachedDeformer cachedDeformer(cache); + + // check initialization is as expected + CPPUNIT_ASSERT(cachedDeformer.mLeafVec == nullptr); + CPPUNIT_ASSERT(cachedDeformer.mLeafMap == nullptr); + + // throw when resetting cachedDeformer with an empty cache + CPPUNIT_ASSERT_THROW(cachedDeformer.reset(nullObject, size_t(0)), openvdb::IndexError); + + // manually create one leaf in the cache + cache.leafs.resize(1); + auto& leaf = cache.leafs[0]; + CPPUNIT_ASSERT(leaf.vecData.empty()); + CPPUNIT_ASSERT(leaf.mapData.empty()); + CPPUNIT_ASSERT_EQUAL(Index(0), leaf.totalSize); + + // reset should no longer throw and leaf vec pointer should now be non-null + CPPUNIT_ASSERT_NO_THROW(cachedDeformer.reset(nullObject, size_t(0))); + CPPUNIT_ASSERT(cachedDeformer.mLeafMap == nullptr); + CPPUNIT_ASSERT(cachedDeformer.mLeafVec != nullptr); + CPPUNIT_ASSERT(cachedDeformer.mLeafVec->empty()); + + // nothing stored in the cache so position is unchanged + DummyIter indexIter(0); + Vec3d position(0,0,0); + Vec3d newPosition(position); + cachedDeformer.apply(newPosition, indexIter); + CPPUNIT_ASSERT(math::isApproxEqual(position, newPosition)); + + // insert a new value into the leaf vector and verify tbe position is deformed + Vec3d deformedPosition(5,10,15); + leaf.vecData.push_back(deformedPosition); + cachedDeformer.apply(newPosition, indexIter); + CPPUNIT_ASSERT(math::isApproxEqual(deformedPosition, newPosition)); + + // insert a new value into the leaf map and verify the position is deformed as before + Vec3d newDeformedPosition(2,3,4); + leaf.mapData.insert({0, newDeformedPosition}); + newPosition.setZero(); + cachedDeformer.apply(newPosition, indexIter); + CPPUNIT_ASSERT(math::isApproxEqual(deformedPosition, newPosition)); + + // now reset the cached deformer and verify the value is updated + // (map has precedence over vector) + cachedDeformer.reset(nullObject, size_t(0)); + CPPUNIT_ASSERT(cachedDeformer.mLeafMap != nullptr); + CPPUNIT_ASSERT(cachedDeformer.mLeafVec == nullptr); + newPosition.setZero(); + cachedDeformer.apply(newPosition, indexIter); + CPPUNIT_ASSERT(math::isApproxEqual(newDeformedPosition, newPosition)); + + // four points, some same leaf, some different + const float voxelSize = 1.0f; + std::vector positions = { + {5, 2, 3}, + {2, 4, 1}, + {50, 5, 1}, + {3, 20, 1}, + }; + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + // evaluate with null deformer and no filter + + NullDeformer nullDeformer; + NullFilter nullFilter; + cachedDeformer.evaluate(*points, nullDeformer, nullFilter); + + CPPUNIT_ASSERT_EQUAL(size_t(points->tree().leafCount()), cache.leafs.size()); + + int leafIndex = 0; + for (auto leafIter = points->tree().cbeginLeaf(); leafIter; ++leafIter) { + for (auto iter = leafIter->beginIndexOn(); iter; ++iter) { + AttributeHandle handle(leafIter->constAttributeArray("P")); + Vec3f pos(handle.get(*iter) + iter.getCoord().asVec3s()); + Vec3f cachePosition = cache.leafs[leafIndex].vecData[*iter]; + CPPUNIT_ASSERT(math::isApproxEqual(pos, cachePosition)); + } + leafIndex++; + } + + // evaluate with Offset deformer and no filter + + Vec3d yOffset(1,2,3); + OffsetDeformer yOffsetDeformer(yOffset); + + cachedDeformer.evaluate(*points, yOffsetDeformer, nullFilter); + + CPPUNIT_ASSERT_EQUAL(size_t(points->tree().leafCount()), cache.leafs.size()); + + leafIndex = 0; + for (auto leafIter = points->tree().cbeginLeaf(); leafIter; ++leafIter) { + for (auto iter = leafIter->beginIndexOn(); iter; ++iter) { + AttributeHandle handle(leafIter->constAttributeArray("P")); + Vec3f pos(handle.get(*iter) + iter.getCoord().asVec3s() + yOffset); + Vec3f cachePosition = cache.leafs[leafIndex].vecData[*iter]; + CPPUNIT_ASSERT(math::isApproxEqual(pos, cachePosition)); + } + leafIndex++; + } + + // evaluate with Offset deformer and OddIndex filter + + OddIndexFilter oddFilter; + cachedDeformer.evaluate(*points, yOffsetDeformer, oddFilter); + + CPPUNIT_ASSERT_EQUAL(size_t(points->tree().leafCount()), cache.leafs.size()); + + leafIndex = 0; + for (auto leafIter = points->tree().cbeginLeaf(); leafIter; ++leafIter) { + for (auto iter = leafIter->beginIndexOn(); iter; ++iter) { + AttributeHandle handle(leafIter->constAttributeArray("P")); + Vec3f pos(handle.get(*iter) + iter.getCoord().asVec3s() + yOffset); + Vec3f cachePosition = cache.leafs[leafIndex].vecData[*iter]; + CPPUNIT_ASSERT(math::isApproxEqual(pos, cachePosition)); + } + leafIndex++; + } +} + + +void +TestPointMove::testMoveLocal() +{ + // This test is for points that only move locally, meaning that + // they remain in the leaf from which they originated + + { // single point, y offset, same voxel + const float voxelSize = 1.0f; + Vec3d offset(0, 0.1, 0); + OffsetDeformer deformer(offset); + + std::vector positions = { + {10, 10, 10}, + }; + + std::vector desiredPositions = applyOffset(positions, offset); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + movePoints(*points, deformer); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // two points, y offset, same voxel + const float voxelSize = 1.0f; + Vec3d offset(0, 0.1, 0); + OffsetDeformer deformer(offset); + + std::vector positions = { + {10, 10, 10}, + {10, 10.1f, 10}, + }; + + std::vector desiredPositions = applyOffset(positions, offset); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + movePoints(*points, deformer); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // two points, y offset, different voxels + const float voxelSize = 1.0f; + Vec3d offset(0, 0.1, 0); + OffsetDeformer deformer(offset); + + std::vector positions = { + {10, 10, 10}, + {10, 11, 10}, + }; + + std::vector desiredPositions = applyOffset(positions, offset); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + movePoints(*points, deformer); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // four points, y offset, same voxel, only third point is kept + const float voxelSize = 1.0f; + Vec3d offset(0, 0.1, 0); + OffsetDeformer deformer(offset); + + std::vector positions = { + {10, 10, 10}, + {10, 10.1f, 10}, + {10, 10.2f, 10}, + {10, 10.3f, 10}, + }; + + std::vector desiredPositions; + desiredPositions.emplace_back(positions[2]+offset); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + movePoints(*points, deformer, filter); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // four points, y offset, different voxels, only third point is kept + const float voxelSize = 1.0f; + Vec3d offset(0, 0.1, 0); + OffsetDeformer deformer(offset); + + std::vector positions = { + {10, 10, 10}, + {10, 11, 10}, + {10, 12, 10}, + {10, 13, 10}, + }; + + std::vector desiredPositions; + desiredPositions.emplace_back(positions[2]+offset); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + movePoints(*points, deformer, filter); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // four points, y offset, different voxels, only third point is moved + const float voxelSize = 1.0f; + Vec3d offset(0, 0.1, 0); + + std::vector positions = { + {10, 10, 10}, + {10, 11, 10}, + {10, 12, 10}, + {10, 13, 10}, + }; + + std::vector desiredPositions(positions); + desiredPositions[2] = Vec3s(positions[2] + offset); + + std::sort(desiredPositions.begin(), desiredPositions.end()); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + OffsetFilteredDeformer deformer(offset, filter); + movePoints(*points, deformer); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } +} + + +void +TestPointMove::testMoveGlobal() +{ + { // four points, all different leafs + const float voxelSize = 0.1f; + Vec3d offset(0, 10.1, 0); + OffsetDeformer deformer(offset); + + std::vector positions = { + {1, 1, 1}, + {1, 5.05f, 1}, + {2, 1, 1}, + {2, 2, 1}, + }; + + std::vector desiredPositions = applyOffset(positions, offset); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + movePoints(*points, deformer); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // four points, all different leafs, only third point is kept + const float voxelSize = 0.1f; + Vec3d offset(0, 10.1, 0); + OffsetDeformer deformer(offset); + + std::vector positions = { + {1, 1, 1}, + {1, 5.05f, 1}, + {2, 1, 1}, + {2, 2, 1}, + }; + + std::vector desiredPositions; + desiredPositions.emplace_back(positions[2]+offset); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + movePoints(*points, deformer, filter); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // four points, all different leafs, third point is deleted + const float voxelSize = 0.1f; + Vec3d offset(0, 10.1, 0); + OffsetDeformer deformer(offset); + + std::vector positions = { + {1, 1, 1}, + {1, 5.05f, 1}, + {2, 1, 1}, + {2, 2, 1}, + }; + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + std::vector desiredPositions; + desiredPositions.emplace_back(positions[0]+offset); + desiredPositions.emplace_back(positions[1]+offset); + desiredPositions.emplace_back(positions[3]+offset); + + std::vector includeGroups; + std::vector excludeGroups{"test"}; + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + movePoints(*points, deformer, filter); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // six points, some same leaf, some different + const float voxelSize = 1.0f; + Vec3d offset(0, 0.1, 0); + OffsetDeformer deformer(offset); + + std::vector positions = { + {1, 1, 1}, + {1.01f, 1.01f, 1.01f}, + {1, 5.05f, 1}, + {2, 1, 1}, + {2.01f, 1.01f, 1.01f}, + {2, 2, 1}, + }; + + std::vector desiredPositions = applyOffset(positions, offset); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + movePoints(*points, deformer); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // four points, all different leafs, only third point is moved + const float voxelSize = 0.1f; + Vec3d offset(0, 10.1, 0); + + std::vector positions = { + {1, 1, 1}, + {1, 5.05f, 1}, + {2, 1, 1}, + {2, 2, 1}, + }; + + std::vector desiredPositions(positions); + desiredPositions[2] = Vec3s(positions[2] + offset); + + std::sort(desiredPositions.begin(), desiredPositions.end()); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + OffsetFilteredDeformer deformer(offset, filter); + movePoints(*points, deformer); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + + { // four points, all different leafs, only third point is kept but not moved + const float voxelSize = 0.1f; + Vec3d offset(0, 10.1, 0); + + std::vector positions = { + {1, 1, 1}, + {1, 5.05f, 1}, + {2, 1, 1}, + {2, 2, 1}, + }; + + std::vector desiredPositions; + desiredPositions.emplace_back(positions[2]); + + std::sort(desiredPositions.begin(), desiredPositions.end()); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + // these groups mark which points are kept + + std::vector includeGroups{"test"}; + std::vector excludeGroups; + + // these groups mark which points are moved + + std::vector moveIncludeGroups; + std::vector moveExcludeGroups{"test"}; + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter moveFilter(moveIncludeGroups, moveExcludeGroups, leaf->attributeSet()); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + OffsetFilteredDeformer deformer(offset, moveFilter); + movePoints(*points, deformer, filter); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } +} + + +namespace { + +// Custom Deformer with reset and apply counters +struct CustomDeformer +{ + using LeafT = PointDataGrid::TreeType::LeafNodeType; + + CustomDeformer(const openvdb::Vec3d& offset, + tbb::atomic& resetCalls, + tbb::atomic& applyCalls) + : mOffset(offset) + , mResetCalls(resetCalls) + , mApplyCalls(applyCalls) { } + + template + void reset(const LeafT& /*leaf*/, size_t /*idx*/) + { + mResetCalls++; + } + + template + void apply(Vec3d& position, const IndexIterT&) const + { + // ensure reset has been called at least once + if (mResetCalls > 0) { + position += mOffset; + } + mApplyCalls++; + } + + const openvdb::Vec3d mOffset; + tbb::atomic& mResetCalls; + tbb::atomic& mApplyCalls; +}; // struct CustomDeformer + +// Custom Deformer that always returns the position supplied in the constructor +struct StaticDeformer +{ + StaticDeformer(const openvdb::Vec3d& position) + : mPosition(position) { } + + template + void reset(const LeafT& /*leaf*/, size_t /*idx*/) { } + + template + void apply(Vec3d& position, const IndexIterT&) const + { + position = mPosition; + } + + const openvdb::Vec3d mPosition; +}; // struct StaticDeformer + +} // namespace + +void +TestPointMove::testCustomDeformer() +{ + { // four points, some same leaf, some different, custom deformer + const float voxelSize = 1.0f; + Vec3d offset(4.5,3.2,1.85); + + std::vector positions = { + {5, 2, 3}, + {2, 4, 1}, + {50, 5, 1}, + {3, 20, 1}, + }; + + std::vector desiredPositions = applyOffset(positions, offset); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + PointDataGrid::Ptr cachedPoints = points->deepCopy(); + + const int leafCount = points->tree().leafCount(); + const int pointCount = int(positions.size()); + + tbb::atomic resetCalls, applyCalls; + resetCalls = 0; + applyCalls = 0; + + // this deformer applies an offset and tracks the number of calls + + CustomDeformer deformer(offset, resetCalls, applyCalls); + + movePoints(*points, deformer); + + CPPUNIT_ASSERT(2*leafCount == resetCalls); + CPPUNIT_ASSERT(2*pointCount == applyCalls); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + + // use CachedDeformer + + resetCalls = 0; + applyCalls = 0; + + CachedDeformer::Cache cache; + CachedDeformer cachedDeformer(cache); + NullFilter filter; + cachedDeformer.evaluate(*cachedPoints, deformer, filter); + + movePoints(*cachedPoints, cachedDeformer); + + CPPUNIT_ASSERT(leafCount == resetCalls); + CPPUNIT_ASSERT(pointCount == applyCalls); + + std::vector cachedPositions = gridToPositions(cachedPoints); + + ASSERT_APPROX_EQUAL(desiredPositions, cachedPositions, __LINE__); + } + + { + { // four points, some same leaf, some different, static deformer + const float voxelSize = 1.0f; + Vec3d newPosition(15.2,18.3,-100.9); + + std::vector positions = { + {5, 2, 3}, + {2, 4, 1}, + {50, 5, 1}, + {3, 20, 1}, + }; + + std::vector desiredPositions(positions.size(), newPosition); + + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + StaticDeformer deformer(newPosition); + + movePoints(*points, deformer); + + std::vector actualPositions = gridToPositions(points); + + ASSERT_APPROX_EQUAL(desiredPositions, actualPositions, __LINE__); + } + } +} + + +namespace { + +// Custom deformer that stores a map of current positions to new positions +struct AssignDeformer +{ + AssignDeformer(const std::map& _values) + : values(_values) { } + + template + void reset(const LeafT&, size_t /*idx*/) { } + + template + void apply(Vec3d& position, const IndexIterT&) const + { + position = values.at(position); + } + + std::map values; +}; // struct AssignDeformer + +} + + +void +TestPointMove::testPointData() +{ + // four points, some same leaf, some different + // spatial order is (1, 0, 3, 2) + + const float voxelSize = 1.0f; + std::vector positions = { + {5, 2, 3}, + {2, 4, 1}, + {50, 5, 1}, + {3, 20, 1}, + }; + + // simple reversing deformer + + std::map remap; + remap.insert({positions[0], positions[3]}); + remap.insert({positions[1], positions[2]}); + remap.insert({positions[2], positions[1]}); + remap.insert({positions[3], positions[0]}); + + AssignDeformer deformer(remap); + + { // reversing point positions results in the same iteration order due to spatial organisation + PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); + + std::vector initialPositions = gridToPositions(points, /*sort=*/false); + + std::vector includeGroups; + std::vector excludeGroups; + + movePoints(*points, deformer); + + std::vector finalPositions1 = gridToPositions(points, /*sort=*/false); + + ASSERT_APPROX_EQUAL(initialPositions, finalPositions1, __LINE__); + + // now we delete the third point while sorting, using the test group + + excludeGroups.push_back("test"); + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter filter(includeGroups, excludeGroups, leaf->attributeSet()); + movePoints(*points, deformer, filter); + + std::vector desiredPositions; + desiredPositions.emplace_back(positions[0]); + desiredPositions.emplace_back(positions[1]); + desiredPositions.emplace_back(positions[3]); + + std::vector finalPositions2 = gridToPositions(points, /*sort=*/false); + + std::sort(desiredPositions.begin(), desiredPositions.end()); + std::sort(finalPositions2.begin(), finalPositions2.end()); + + ASSERT_APPROX_EQUAL(desiredPositions, finalPositions2, __LINE__); + } + + { // additional point data - integer "id", float "pscale", "odd" and "even" groups + + std::vector id; + id.push_back(0); + id.push_back(1); + id.push_back(2); + id.push_back(3); + + std::vector radius; + radius.push_back(0.1f); + radius.push_back(0.15f); + radius.push_back(0.2f); + radius.push_back(0.5f); + + // manually construct point data grid instead of using positionsToGrid() + + const PointAttributeVector pointList(positions); + + openvdb::math::Transform::Ptr transform( + openvdb::math::Transform::createLinearTransform(voxelSize)); + + tools::PointIndexGrid::Ptr pointIndexGrid = + tools::createPointIndexGrid(pointList, *transform); + + PointDataGrid::Ptr points = + createPointDataGrid(*pointIndexGrid, + pointList, *transform); + auto idAttributeType = + openvdb::points::TypedAttributeArray::attributeType(); + openvdb::points::appendAttribute(points->tree(), "id", idAttributeType); + + // create a wrapper around the id vector + openvdb::points::PointAttributeVector idWrapper(id); + + openvdb::points::populateAttribute>( + points->tree(), pointIndexGrid->tree(), "id", idWrapper); + + // use fixed-point codec for radius + // note that this attribute type is not registered by default so needs to be + // explicitly registered. + using Codec = openvdb::points::FixedPointCodec; + openvdb::points::TypedAttributeArray::registerType(); + auto radiusAttributeType = + openvdb::points::TypedAttributeArray::attributeType(); + openvdb::points::appendAttribute(points->tree(), "pscale", radiusAttributeType); + + // create a wrapper around the radius vector + openvdb::points::PointAttributeVector radiusWrapper(radius); + + openvdb::points::populateAttribute>( + points->tree(), pointIndexGrid->tree(), "pscale", radiusWrapper); + + appendGroup(points->tree(), "odd"); + appendGroup(points->tree(), "even"); + appendGroup(points->tree(), "nonzero"); + + std::vector oddGroups(positions.size(), 0); + oddGroups[1] = 1; + oddGroups[3] = 1; + std::vector evenGroups(positions.size(), 0); + evenGroups[0] = 1; + evenGroups[2] = 1; + std::vector nonZeroGroups(positions.size(), 1); + nonZeroGroups[0] = 0; + + setGroup(points->tree(), pointIndexGrid->tree(), evenGroups, "even"); + setGroup(points->tree(), pointIndexGrid->tree(), oddGroups, "odd"); + setGroup(points->tree(), pointIndexGrid->tree(), nonZeroGroups, "nonzero"); + + movePoints(*points, deformer); + + // extract data + + std::vector id2; + std::vector radius2; + std::vector oddGroups2; + std::vector evenGroups2; + std::vector nonZeroGroups2; + + for (auto leaf = points->tree().cbeginLeaf(); leaf; ++leaf) { + + AttributeHandle idHandle(leaf->constAttributeArray("id")); + AttributeHandle pscaleHandle(leaf->constAttributeArray("pscale")); + GroupHandle oddHandle(leaf->groupHandle("odd")); + GroupHandle evenHandle(leaf->groupHandle("even")); + GroupHandle nonZeroHandle(leaf->groupHandle("nonzero")); + + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + id2.push_back(idHandle.get(*iter)); + radius2.push_back(pscaleHandle.get(*iter)); + oddGroups2.push_back(oddHandle.get(*iter) ? 1 : 0); + evenGroups2.push_back(evenHandle.get(*iter) ? 1 : 0); + nonZeroGroups2.push_back(nonZeroHandle.get(*iter) ? 1 : 0); + } + } + + // new reversed order is (2, 3, 0, 1) + + CPPUNIT_ASSERT_EQUAL(2, id2[0]); + CPPUNIT_ASSERT_EQUAL(3, id2[1]); + CPPUNIT_ASSERT_EQUAL(0, id2[2]); + CPPUNIT_ASSERT_EQUAL(1, id2[3]); + + CPPUNIT_ASSERT(math::isApproxEqual(radius[0], radius2[2], 1e-3f)); + CPPUNIT_ASSERT(math::isApproxEqual(radius[1], radius2[3], 1e-3f)); + CPPUNIT_ASSERT(math::isApproxEqual(radius[2], radius2[0], 1e-3f)); + CPPUNIT_ASSERT(math::isApproxEqual(radius[3], radius2[1], 1e-3f)); + + CPPUNIT_ASSERT_EQUAL(short(0), oddGroups2[0]); + CPPUNIT_ASSERT_EQUAL(short(1), oddGroups2[1]); + CPPUNIT_ASSERT_EQUAL(short(0), oddGroups2[2]); + CPPUNIT_ASSERT_EQUAL(short(1), oddGroups2[3]); + + CPPUNIT_ASSERT_EQUAL(short(1), evenGroups2[0]); + CPPUNIT_ASSERT_EQUAL(short(0), evenGroups2[1]); + CPPUNIT_ASSERT_EQUAL(short(1), evenGroups2[2]); + CPPUNIT_ASSERT_EQUAL(short(0), evenGroups2[3]); + + CPPUNIT_ASSERT_EQUAL(short(1), nonZeroGroups2[0]); + CPPUNIT_ASSERT_EQUAL(short(1), nonZeroGroups2[1]); + CPPUNIT_ASSERT_EQUAL(short(0), nonZeroGroups2[2]); + CPPUNIT_ASSERT_EQUAL(short(1), nonZeroGroups2[3]); + } + + { // larger data set with a cached deformer and group filtering + std::vector newPositions; + const int count = 10000; + unittest_util::genPoints(count, newPositions); + + // manually construct point data grid instead of using positionsToGrid() + + const PointAttributeVector pointList(newPositions); + + openvdb::math::Transform::Ptr transform( + openvdb::math::Transform::createLinearTransform(/*voxelSize=*/0.1)); + + tools::PointIndexGrid::Ptr pointIndexGrid = + tools::createPointIndexGrid(pointList, *transform); + + PointDataGrid::Ptr points = + createPointDataGrid(*pointIndexGrid, + pointList, *transform); + + appendGroup(points->tree(), "odd"); + + std::vector oddGroups(newPositions.size(), 0); + for (size_t i = 1; i < newPositions.size(); i += 2) { + oddGroups[i] = 1; + } + + setGroup(points->tree(), pointIndexGrid->tree(), oddGroups, "odd"); + + std::vector includeGroups{"odd"}; + std::vector excludeGroups; + + auto leaf = points->tree().cbeginLeaf(); + MultiGroupFilter advectFilter(includeGroups, excludeGroups, leaf->attributeSet()); + OffsetDeformer offsetDeformer(Vec3d(0, 1, 0)); + + CachedDeformer::Cache cache; + CachedDeformer cachedDeformer(cache); + + cachedDeformer.evaluate(*points, offsetDeformer, advectFilter); + + double ySumBefore = 0.0; + double ySumAfter = 0.0; + + for (auto leaf = points->tree().cbeginLeaf(); leaf; ++leaf) { + AttributeHandle handle(leaf->constAttributeArray("P")); + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + Vec3d position = handle.get(*iter) + iter.getCoord().asVec3s(); + position = transform->indexToWorld(position); + ySumBefore += position.y(); + } + } + + movePoints(*points, cachedDeformer); + + for (auto leaf = points->tree().cbeginLeaf(); leaf; ++leaf) { + AttributeHandle handle(leaf->constAttributeArray("P")); + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + Vec3d position = handle.get(*iter) + iter.getCoord().asVec3s(); + position = transform->indexToWorld(position); + ySumAfter += position.y(); + } + } + + // total increase in Y should be approximately count / 2 + // (only odd points are being moved 1.0 in Y) + double increaseInY = ySumAfter - ySumBefore; + + CPPUNIT_ASSERT_DOUBLES_EQUAL(increaseInY, static_cast(count) / 2.0, + /*tolerance=*/double(0.01)); + } +} + + +void +TestPointMove::testPointOrder() +{ + struct Local + { + using GridT = points::PointDataGrid; + + static void populate(std::vector& positions, const GridT& points, + const math::Transform& transform, bool /*threaded*/) + { + auto newPoints1 = points.deepCopy(); + + points::NullDeformer nullDeformer; + points::NullFilter nullFilter; + points::movePoints(*newPoints1, transform, nullDeformer, nullFilter); + + size_t totalPoints = points::pointCount(newPoints1->tree()); + + positions.reserve(totalPoints); + + for (auto leaf = newPoints1->tree().cbeginLeaf(); leaf; ++leaf) { + AttributeHandle handle(leaf->constAttributeArray("P")); + for (auto iter = leaf->beginIndexOn(); iter; ++iter) { + positions.push_back(handle.get(*iter)); + } + } + } + }; + + auto sourceTransform = math::Transform::createLinearTransform(/*voxelSize=*/0.1); + auto targetTransform = math::Transform::createLinearTransform(/*voxelSize=*/1.0); + + auto mask = MaskGrid::create(); + mask->setTransform(sourceTransform); + mask->denseFill(CoordBBox(Coord(-20,-20,-20), Coord(20,20,20)), true); + + auto points = points::denseUniformPointScatter(*mask, /*pointsPerVoxel=*/8); + + // three copies of the points, two multi-threaded and one single-threaded + std::vector positions1; + std::vector positions2; + std::vector positions3; + + Local::populate(positions1, *points, *targetTransform, true); + Local::populate(positions2, *points, *targetTransform, true); + Local::populate(positions3, *points, *targetTransform, false); + + // verify all sequences are identical to confirm that points are ordered deterministically + + ASSERT_APPROX_EQUAL(positions1, positions2, __LINE__); + ASSERT_APPROX_EQUAL(positions1, positions3, __LINE__); +} diff --git a/openvdb/unittest/TestPointPartitioner.cc b/openvdb/unittest/TestPointPartitioner.cc new file mode 100644 index 00000000..35b8981b --- /dev/null +++ b/openvdb/unittest/TestPointPartitioner.cc @@ -0,0 +1,98 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + +#include + + +class TestPointPartitioner: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestPointPartitioner); + CPPUNIT_TEST(testPartitioner); + CPPUNIT_TEST_SUITE_END(); + + void testPartitioner(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointPartitioner); + +//////////////////////////////////////// + +namespace { + +struct PointList { + typedef openvdb::Vec3s PosType; + + PointList(const std::vector& points) : mPoints(&points) {} + + size_t size() const { return mPoints->size(); } + + void getPos(size_t n, PosType& xyz) const { xyz = (*mPoints)[n]; } + +protected: + std::vector const * const mPoints; +}; // PointList + +} // namespace + +//////////////////////////////////////// + + +void +TestPointPartitioner::testPartitioner() +{ + const size_t pointCount = 10000; + const float voxelSize = 0.1f; + + std::vector points(pointCount, openvdb::Vec3s(0.f)); + for (size_t n = 1; n < pointCount; ++n) { + points[n].x() = points[n-1].x() + voxelSize; + } + + PointList pointList(points); + + const openvdb::math::Transform::Ptr transform = + openvdb::math::Transform::createLinearTransform(voxelSize); + + typedef openvdb::tools::UInt32PointPartitioner PointPartitioner; + + PointPartitioner::Ptr partitioner = + PointPartitioner::create(pointList, *transform); + + CPPUNIT_ASSERT(!partitioner->empty()); + + // The default interpretation should be cell-centered. + CPPUNIT_ASSERT(partitioner->usingCellCenteredTransform()); + + const size_t expectedPageCount = pointCount / (1u << PointPartitioner::LOG2DIM); + + CPPUNIT_ASSERT_EQUAL(expectedPageCount, partitioner->size()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), partitioner->origin(0)); + + PointPartitioner::IndexIterator it = partitioner->indices(0); + + CPPUNIT_ASSERT(it.test()); + CPPUNIT_ASSERT_EQUAL(it.size(), size_t(1 << PointPartitioner::LOG2DIM)); + + PointPartitioner::IndexIterator itB = partitioner->indices(0); + + CPPUNIT_ASSERT_EQUAL(++it, ++itB); + CPPUNIT_ASSERT(it != ++itB); + + std::vector indices; + + for (it.reset(); it; ++it) { + indices.push_back(*it); + } + + CPPUNIT_ASSERT_EQUAL(it.size(), indices.size()); + + size_t idx = 0; + for (itB.reset(); itB; ++itB) { + CPPUNIT_ASSERT_EQUAL(indices[idx++], *itB); + } +} + diff --git a/openvdb/unittest/TestPointSample.cc b/openvdb/unittest/TestPointSample.cc new file mode 100644 index 00000000..fc9f37ea --- /dev/null +++ b/openvdb/unittest/TestPointSample.cc @@ -0,0 +1,551 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include "util.h" +#include +#include + +using namespace openvdb; + +class TestPointSample: public CppUnit::TestCase +{ +public: + + void setUp() override { initialize(); } + void tearDown() override { uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointSample); + CPPUNIT_TEST(testPointSample); + CPPUNIT_TEST(testPointSampleWithGroups); + + CPPUNIT_TEST_SUITE_END(); + + void testPointSample(); + void testPointSampleWithGroups(); + +}; // class TestPointSample + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointSample); + + +namespace +{ + +/// Utility function to quickly create a very simple grid (with specified value type), set a value +/// at its origin and then create and sample to an attribute +/// +template +typename points::AttributeHandle::Ptr +testAttribute(points::PointDataGrid& points, const std::string& attributeName, + const math::Transform::Ptr xform, const ValueType& val) +{ + using TreeT = typename tree::Tree4::Type; + using GridT = Grid; + + typename GridT::Ptr grid = GridT::create(); + + grid->setTransform(xform); + grid->tree().setValue(Coord(0,0,0), val); + + points::boxSample(points, *grid, attributeName); + + return(points::AttributeHandle::create( + points.tree().cbeginLeaf()->attributeArray(attributeName))); +} + +} // anonymous namespace + + +void +TestPointSample::testPointSample() +{ + using points::PointDataGrid; + using points::NullCodec; + + const float voxelSize = 0.1f; + math::Transform::Ptr transform(math::Transform::createLinearTransform(voxelSize)); + + { + // check that all supported grid types can be sampled. + // This check will use very basic grids with a point at a cell-centered positions + + // create test point grid with a single point + + std::vector pointPositions{Vec3f(0.0f, 0.0f, 0.0f)}; + PointDataGrid::Ptr points = points::createPointDataGrid( + pointPositions, *transform); + + CPPUNIT_ASSERT(points); + + // bool + + points::AttributeHandle::Ptr boolHandle = + testAttribute(*points, "test_bool", transform, true); + + CPPUNIT_ASSERT(boolHandle->get(0)); + + // int16 + +#if (defined _MSC_VER) || (defined __INTEL_COMPILER) || (defined __clang__) + // GCC warns warns of narrowing conversions from int to int16_t, + // and GCC 4.8, at least, ignores the -Wconversion suppression pragma. + // So for now, skip this test if compiling with GCC. + points::AttributeHandle::Ptr int16Handle = + testAttribute(*points, "test_int16", transform, int16_t(10)); + + CPPUNIT_ASSERT_EQUAL(int16Handle->get(0), int16_t(10)); +#endif + + // int32 + + points::AttributeHandle::Ptr int32Handle = + testAttribute(*points, "test_Int32", transform, Int32(3)); + + CPPUNIT_ASSERT_EQUAL(Int32(3), int32Handle->get(0)); + + // int64 + + points::AttributeHandle::Ptr int64Handle = + testAttribute(*points, "test_Int64", transform, Int64(2)); + + CPPUNIT_ASSERT_EQUAL(Int64(2), int64Handle->get(0)); + + // double + + points::AttributeHandle::Ptr doubleHandle = + testAttribute(*points, "test_double", transform, 4.0); + + CPPUNIT_ASSERT_EQUAL(4.0, doubleHandle->get(0)); + + // Vec3i + + points::AttributeHandle::Ptr vec3iHandle = + testAttribute(*points, "test_vec3i", transform, math::Vec3i(9, 8, 7)); + + CPPUNIT_ASSERT_EQUAL(vec3iHandle->get(0), math::Vec3i(9, 8, 7)); + + // Vec3f + + points::AttributeHandle::Ptr vec3fHandle = + testAttribute(*points, "test_vec3f", transform, Vec3f(111.0f, 222.0f, 333.0f)); + + CPPUNIT_ASSERT_EQUAL(vec3fHandle->get(0), Vec3f(111.0f, 222.0f, 333.0f)); + + // Vec3d + + points::AttributeHandle::Ptr vec3dHandle = + testAttribute(*points, "test_vec3d", transform, Vec3d(1.0, 2.0, 3.0)); + + CPPUNIT_ASSERT(math::isApproxEqual(Vec3d(1.0, 2.0, 3.0), vec3dHandle->get(0))); + } + + { + // empty source grid + + std::vector pointPositions{Vec3f(0.0f, 0.0f, 0.0f)}; + + PointDataGrid::Ptr points = points::createPointDataGrid( + pointPositions, *transform); + + points::appendAttribute(points->tree(), "test"); + + VectorGrid::Ptr testGrid = VectorGrid::create(); + + points::boxSample(*points, *testGrid, "test"); + + points::AttributeHandle::Ptr handle = + points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("test")); + + CPPUNIT_ASSERT(math::isApproxEqual(Vec3f(0.0f, 0.0f, 0.0f), handle->get(0))); + } + + { + // empty point grid + + std::vector pointPositions; + PointDataGrid::Ptr points = points::createPointDataGrid( + pointPositions, *transform); + + CPPUNIT_ASSERT(points); + + FloatGrid::Ptr testGrid = FloatGrid::create(1.0); + + points::appendAttribute(points->tree(), "test"); + + CPPUNIT_ASSERT_NO_THROW(points::boxSample(*points, *testGrid, "test")); + } + + { + // exception if one tries to sample to "P" attribute + + std::vector pointPositions{Vec3f(0.0f, 0.0f, 0.0f)}; + + PointDataGrid::Ptr points = points::createPointDataGrid( + pointPositions, *transform); + + CPPUNIT_ASSERT(points); + + FloatGrid::Ptr testGrid = FloatGrid::create(1.0); + + CPPUNIT_ASSERT_THROW_MESSAGE("Cannot sample onto the \"P\" attribute", + points::boxSample(*points, *testGrid, "P"), RuntimeError); + + // name of the grid is used if no attribute is provided + + testGrid->setName("test_grid"); + + CPPUNIT_ASSERT(!points->tree().cbeginLeaf()->hasAttribute("test_grid")); + + points::boxSample(*points, *testGrid); + + CPPUNIT_ASSERT(points->tree().cbeginLeaf()->hasAttribute("test_grid")); + + // name fails if the grid is called "P" + + testGrid->setName("P"); + + CPPUNIT_ASSERT_THROW_MESSAGE("Cannot sample onto the \"P\" attribute", + points::boxSample(*points, *testGrid), RuntimeError); + } + + { + // test non-cell centered points with scalar data and matching transform + // use various sampling orders + + std::vector pointPositions{Vec3f(0.03f, 0.0f, 0.0f), Vec3f(0.11f, 0.03f, 0.0f)}; + + PointDataGrid::Ptr points = points::createPointDataGrid( + pointPositions, *transform); + + CPPUNIT_ASSERT(points); + + FloatGrid::Ptr testGrid = FloatGrid::create(); + + testGrid->setTransform(transform); + testGrid->tree().setValue(Coord(-1,0,0), -1.0f); + testGrid->tree().setValue(Coord(0,0,0), 1.0f); + testGrid->tree().setValue(Coord(1,0,0), 2.0f); + testGrid->tree().setValue(Coord(2,0,0), 4.0f); + testGrid->tree().setValue(Coord(0,1,0), 3.0f); + + points::appendAttribute(points->tree(), "test"); + points::AttributeHandle::Ptr handle = + points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("test")); + + CPPUNIT_ASSERT(handle.get()); + + FloatGrid::ConstAccessor testGridAccessor = testGrid->getConstAccessor(); + + // check nearest-neighbour sampling + + points::pointSample(*points, *testGrid, "test"); + + float expected = tools::PointSampler::sample(testGridAccessor, Vec3f(0.3f, 0.0f, 0.0f)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, handle->get(0), 1e-6); + + expected = tools::PointSampler::sample(testGridAccessor, Vec3f(1.1f, 0.3f, 0.0f)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, handle->get(1), 1e-6); + + // check tri-linear sampling + + points::boxSample(*points, *testGrid, "test"); + + expected = tools::BoxSampler::sample(testGridAccessor, Vec3f(0.3f, 0.0f, 0.0f)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, handle->get(0), 1e-6); + + expected = tools::BoxSampler::sample(testGridAccessor, Vec3f(1.1f, 0.3f, 0.0f)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, handle->get(1), 1e-6); + + // check tri-quadratic sampling + + points::quadraticSample(*points, *testGrid, "test"); + + expected = tools::QuadraticSampler::sample(testGridAccessor, Vec3f(0.3f, 0.0f, 0.0f)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, handle->get(0), 1e-6); + + expected = tools::QuadraticSampler::sample(testGridAccessor, Vec3f(1.1f, 0.3f, 0.0f)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, handle->get(1), 1e-6); + } + + { + // staggered grid and mismatching transforms + + std::vector pointPositions{Vec3f(0.03f, 0.0f, 0.0f), Vec3f(0.0f, 0.03f, 0.0f), + Vec3f(0.0f, 0.0f, 0.03f),}; + + PointDataGrid::Ptr points = + points::createPointDataGrid(pointPositions, + *transform); + + CPPUNIT_ASSERT(points); + + VectorGrid::Ptr testGrid = VectorGrid::create(); + + testGrid->setGridClass(GRID_STAGGERED); + testGrid->tree().setValue(Coord(0,0,0), Vec3f(1.0f, 2.0f, 3.0f)); + testGrid->tree().setValue(Coord(0,1,0), Vec3f(1.5f, 2.5f, 3.5f)); + testGrid->tree().setValue(Coord(0,0,1), Vec3f(2.0f, 3.0f, 4.0)); + + points::appendAttribute(points->tree(), "test"); + + points::AttributeHandle::Ptr handle = + points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("test")); + + CPPUNIT_ASSERT(handle.get()); + + Vec3fGrid::ConstAccessor testGridAccessor = testGrid->getConstAccessor(); + + // nearest-neighbour staggered sampling + + points::pointSample(*points, *testGrid, "test"); + + Vec3f expected = tools::StaggeredPointSampler::sample(testGridAccessor, + Vec3f(0.03f, 0.0f, 0.0f)); + + CPPUNIT_ASSERT(math::isApproxEqual(expected, handle->get(0))); + + expected = tools::StaggeredPointSampler::sample(testGridAccessor, Vec3f(0.0f, 0.03f, 0.0f)); + + CPPUNIT_ASSERT(math::isApproxEqual(expected, handle->get(1))); + + // tri-linear staggered sampling + + points::boxSample(*points, *testGrid, "test"); + + expected = tools::StaggeredBoxSampler::sample(testGridAccessor, + Vec3f(0.03f, 0.0f, 0.0f)); + + CPPUNIT_ASSERT(math::isApproxEqual(expected, handle->get(0))); + + expected = tools::StaggeredBoxSampler::sample(testGridAccessor, Vec3f(0.0f, 0.03f, 0.0f)); + + CPPUNIT_ASSERT(math::isApproxEqual(expected, handle->get(1))); + + // tri-quadratic staggered sampling + + points::quadraticSample(*points, *testGrid, "test"); + + expected = tools::StaggeredQuadraticSampler::sample(testGridAccessor, + Vec3f(0.03f, 0.0f, 0.0f)); + + CPPUNIT_ASSERT(math::isApproxEqual(expected, handle->get(0))); + + expected = tools::StaggeredQuadraticSampler::sample(testGridAccessor, + Vec3f(0.0f, 0.03f, 0.0f)); + + CPPUNIT_ASSERT(math::isApproxEqual(expected, handle->get(1))); + } + + { + // value type of grid and attribute type don't match + + std::vector pointPositions{Vec3f(0.3f, 0.0f, 0.0f)}; + + math::Transform::Ptr transform2(math::Transform::createLinearTransform(1.0f)); + PointDataGrid::Ptr points = + points::createPointDataGrid(pointPositions, + *transform2); + + CPPUNIT_ASSERT(points); + + FloatGrid::Ptr testFloatGrid = FloatGrid::create(); + + testFloatGrid->setTransform(transform2); + testFloatGrid->tree().setValue(Coord(0,0,0), 1.1f); + testFloatGrid->tree().setValue(Coord(1,0,0), 2.8f); + testFloatGrid->tree().setValue(Coord(0,1,0), 3.4f); + + points::appendAttribute(points->tree(), "testint"); + points::boxSample(*points, *testFloatGrid, "testint"); + points::AttributeHandle::Ptr handle = points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("testint")); + + CPPUNIT_ASSERT(handle.get()); + + FloatGrid::ConstAccessor testFloatGridAccessor = testFloatGrid->getConstAccessor(); + + // check against box sampler values + + const float sampledValue = tools::BoxSampler::sample(testFloatGridAccessor, + Vec3f(0.3f, 0.0f, 0.0f)); + const int expected = static_cast(math::Round(sampledValue)); + + CPPUNIT_ASSERT_EQUAL(expected, handle->get(0)); + + // check mismatching grid type using vector types + + Vec3fGrid::Ptr testVec3fGrid = Vec3fGrid::create(); + + testVec3fGrid->setTransform(transform2); + testVec3fGrid->tree().setValue(Coord(0,0,0), Vec3f(1.0f, 2.0f, 3.0f)); + testVec3fGrid->tree().setValue(Coord(1,0,0), Vec3f(1.5f, 2.5f, 3.5f)); + testVec3fGrid->tree().setValue(Coord(0,1,0), Vec3f(2.0f, 3.0f, 4.0f)); + + points::appendAttribute(points->tree(), "testvec3d"); + points::boxSample(*points, *testVec3fGrid, "testvec3d"); + points::AttributeHandle::Ptr handle2 = points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("testvec3d")); + + Vec3fGrid::ConstAccessor testVec3fGridAccessor = testVec3fGrid->getConstAccessor(); + const Vec3d expected2 = static_cast(tools::BoxSampler::sample(testVec3fGridAccessor, + Vec3f(0.3f, 0.0f, 0.0f))); + + CPPUNIT_ASSERT(math::isExactlyEqual(expected2, handle2->get(0))); + + // check implicit casting of types for sampling using sampleGrid() + + points::appendAttribute(points->tree(), "testvec3d2"); + points::sampleGrid(/*linear*/1, *points, *testVec3fGrid, "testvec3d2"); + points::AttributeHandle::Ptr handle3 = points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("testvec3d2")); + + CPPUNIT_ASSERT(math::isExactlyEqual(expected2, handle3->get(0))); + + // check explicit casting of types for sampling using sampleGrid() + + points::sampleGrid( + /*linear*/1, *points, *testVec3fGrid, "testvec3d3"); + points::AttributeHandle::Ptr handle4 = points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("testvec3d3")); + + CPPUNIT_ASSERT(math::isExactlyEqual(expected2, handle4->get(0))); + + // check invalid casting of types + + points::appendAttribute(points->tree(), "testfloat"); + + // The following is a substitute for CPPUNIT_ASSERT_THROW_MESSAGE(), + // which generates a compiler warning when the expected exception type + // is std::exception. + try { + points::boxSample(*points, *testVec3fGrid, "testfloat"); + CPPUNIT_FAIL("expected exception not thrown:" + " cannot sample a vec3s grid on to a float attribute"); + } catch (std::exception&) { + } catch (...) { + CPPUNIT_FAIL("expected std::exception or derived"); + } + + // check invalid existing attribute type (Vec4s attribute) + + points::TypedAttributeArray::registerType(); + points::appendAttribute(points->tree(), "testv4f"); + CPPUNIT_ASSERT_THROW(points::boxSample(*points, *testVec3fGrid, "testv4f"), TypeError); + } + + { // sample a non-standard grid type (a Vec4 grid) + using Vec4STree = tree::Tree4::Type; + using Vec4SGrid = Grid; + Vec4SGrid::registerGrid(); + points::TypedAttributeArray::registerType(); + + std::vector pointPositions{Vec3f(0.3f, 0.0f, 0.0f)}; + + math::Transform::Ptr transform2(math::Transform::createLinearTransform(1.0f)); + PointDataGrid::Ptr points = + points::createPointDataGrid(pointPositions, + *transform2); + + auto testVec4fGrid = Vec4SGrid::create(); + testVec4fGrid->setTransform(transform2); + testVec4fGrid->tree().setValue(Coord(0,0,0), Vec4s(1.0f, 2.0f, 3.0f, 4.0f)); + testVec4fGrid->tree().setValue(Coord(1,0,0), Vec4s(1.5f, 2.5f, 3.5f, 4.5f)); + testVec4fGrid->tree().setValue(Coord(0,1,0), Vec4s(2.0f, 3.0f, 4.0f, 5.0f)); + + points::boxSample(*points, *testVec4fGrid, "testvec4f"); + points::AttributeHandle::Ptr handle2 = points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("testvec4f")); + + Vec4SGrid::ConstAccessor testVec4fGridAccessor = testVec4fGrid->getConstAccessor(); + const Vec4s expected2 = static_cast(tools::BoxSampler::sample(testVec4fGridAccessor, + Vec3f(0.3f, 0.0f, 0.0f))); + + CPPUNIT_ASSERT(math::isExactlyEqual(expected2, handle2->get(0))); + } +} + +void +TestPointSample::testPointSampleWithGroups() +{ + using points::PointDataGrid; + + std::vector pointPositions{Vec3f(0.03f, 0.0f, 0.0f), Vec3f(0.0f, 0.03f, 0.0f), + Vec3f(0.0f, 0.0f, 0.0f)}; + + math::Transform::Ptr transform(math::Transform::createLinearTransform(0.1f)); + PointDataGrid::Ptr points = points::createPointDataGrid(pointPositions, *transform); + + CPPUNIT_ASSERT(points); + + DoubleGrid::Ptr testGrid = DoubleGrid::create(); + + testGrid->setTransform(transform); + testGrid->tree().setValue(Coord(0,0,0), 1.0); + testGrid->tree().setValue(Coord(1,0,0), 2.0); + testGrid->tree().setValue(Coord(0,1,0), 3.0); + + points::appendGroup(points->tree(), "group1"); + + auto leaf = points->tree().beginLeaf(); + + points::GroupWriteHandle group1Handle = leaf->groupWriteHandle("group1"); + + group1Handle.set(0, true); + group1Handle.set(1, false); + group1Handle.set(2, true); + + points::appendAttribute(points->tree(), "test_include"); + + std::vector includeGroups({"group1"}); + std::vector excludeGroups; + points::MultiGroupFilter filter1(includeGroups, excludeGroups, leaf->attributeSet()); + points::boxSample(*points, *testGrid, "test_include", filter1); + + points::AttributeHandle::Ptr handle = + points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("test_include")); + + DoubleGrid::ConstAccessor testGridAccessor = testGrid->getConstAccessor(); + + double expected = tools::BoxSampler::sample(testGridAccessor, Vec3f(0.3f, 0.0f, 0.0f)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, handle->get(0), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, handle->get(1), 1e-6); + + expected = tools::BoxSampler::sample(testGridAccessor, Vec3f(0.0f, 0.0f, 0.0f)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, handle->get(2), 1e-6); + + points::appendAttribute(points->tree(), "test_exclude"); + + // test with group treated as "exclusion" group + + points::MultiGroupFilter filter2(excludeGroups, includeGroups, leaf->attributeSet()); + points::boxSample(*points, *testGrid, "test_exclude", filter2); + + points::AttributeHandle::Ptr handle2 = + points::AttributeHandle::create( + points->tree().cbeginLeaf()->attributeArray("test_exclude")); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, handle2->get(0), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, handle2->get(2), 1e-6); + + expected = tools::BoxSampler::sample(testGridAccessor, Vec3f(0.0f, 0.3f, 0.0f)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, handle2->get(1), 1e-6); +} diff --git a/openvdb/unittest/TestPointScatter.cc b/openvdb/unittest/TestPointScatter.cc new file mode 100644 index 00000000..2787934a --- /dev/null +++ b/openvdb/unittest/TestPointScatter.cc @@ -0,0 +1,703 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +using namespace openvdb; +using namespace openvdb::points; + +class TestPointScatter: public CppUnit::TestCase +{ +public: + + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestPointScatter); + CPPUNIT_TEST(testUniformPointScatter); + CPPUNIT_TEST(testDenseUniformPointScatter); + CPPUNIT_TEST(testNonUniformPointScatter); + CPPUNIT_TEST_SUITE_END(); + + void testUniformPointScatter(); + void testDenseUniformPointScatter(); + void testNonUniformPointScatter(); + +}; // class TestPointScatter + + +void +TestPointScatter::testUniformPointScatter() +{ + const Index64 total = 50; + const math::CoordBBox boxBounds(math::Coord(-1), math::Coord(1)); // 27 voxels across 8 leaves + + // Test the free function for all default grid types - 50 points across 27 voxels + // ensures all voxels receive points + + { + BoolGrid grid; + grid.sparseFill(boxBounds, false, /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + DoubleGrid grid; + grid.sparseFill(boxBounds, 0.0, /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + FloatGrid grid; + grid.sparseFill(boxBounds, 0.0f, /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + Int32Grid grid; + grid.sparseFill(boxBounds, 0, /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + Int64Grid grid; + grid.sparseFill(boxBounds, 0, /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + MaskGrid grid; + grid.sparseFill(boxBounds, /*maskBuffer*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + StringGrid grid; + grid.sparseFill(boxBounds, "", /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + Vec3DGrid grid; + grid.sparseFill(boxBounds, Vec3d(), /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + Vec3IGrid grid; + grid.sparseFill(boxBounds, Vec3i(), /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + Vec3SGrid grid; + grid.sparseFill(boxBounds, Vec3f(), /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + { + PointDataGrid grid; + grid.sparseFill(boxBounds, 0, /*active*/true); + auto points = points::uniformPointScatter(grid, total); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + } + + // Test 0 produces empty grid + + { + BoolGrid grid; + grid.sparseFill(boxBounds, false, /*active*/true); + auto points = points::uniformPointScatter(grid, 0); + CPPUNIT_ASSERT(points->empty()); + } + + // Test single point scatter and topology + + { + BoolGrid grid; + grid.sparseFill(boxBounds, false, /*active*/true); + auto points = points::uniformPointScatter(grid, 1); + CPPUNIT_ASSERT_EQUAL(Index32(1), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), pointCount(points->tree())); + } + + // Test a grid containing tiles scatters correctly + + BoolGrid grid; + grid.tree().addTile(/*level*/1, math::Coord(0), /*value*/true, /*active*/true); + + const Index32 NUM_VALUES = BoolGrid::TreeType::LeafNodeType::NUM_VALUES; + + CPPUNIT_ASSERT_EQUAL(Index64(NUM_VALUES), grid.activeVoxelCount()); + + auto points = points::uniformPointScatter(grid, total); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + CPPUNIT_ASSERT_EQUAL(Index64(0), points->tree().activeTileCount()); +#endif + CPPUNIT_ASSERT_EQUAL(Index32(1), points->tree().leafCount()); + CPPUNIT_ASSERT(Index64(NUM_VALUES) > points->tree().activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + + // Explicitly check P attribute + + const auto* attributeSet = &(points->tree().cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(size_t(1), attributeSet->size()); + const auto* array = attributeSet->getConst(0); + CPPUNIT_ASSERT(array); + + using PositionArrayT = TypedAttributeArray; + CPPUNIT_ASSERT(array->isType()); + + size_t size = array->size(); + CPPUNIT_ASSERT_EQUAL(size_t(total), size); + + AttributeHandle::Ptr pHandle = + AttributeHandle::create(*array); + for (size_t i = 0; i < size; ++i) { + const Vec3f P = pHandle->get(Index(i)); + CPPUNIT_ASSERT(P[0] >=-0.5f); + CPPUNIT_ASSERT(P[0] <= 0.5f); + CPPUNIT_ASSERT(P[1] >=-0.5f); + CPPUNIT_ASSERT(P[1] <= 0.5f); + CPPUNIT_ASSERT(P[2] >=-0.5f); + CPPUNIT_ASSERT(P[2] <= 0.5f); + } + + // Test the rng seed + + const Vec3f firstPosition = pHandle->get(0); + points = points::uniformPointScatter(grid, total, /*seed*/1); + + attributeSet = &(points->tree().cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(size_t(1), attributeSet->size()); + + array = attributeSet->getConst(0); + CPPUNIT_ASSERT(array); + CPPUNIT_ASSERT(array->isType()); + + size = array->size(); + CPPUNIT_ASSERT_EQUAL(size_t(total), size); + pHandle = AttributeHandle::create(*array); + + const Vec3f secondPosition = pHandle->get(0); + CPPUNIT_ASSERT(!math::isExactlyEqual(firstPosition[0], secondPosition[0])); + CPPUNIT_ASSERT(!math::isExactlyEqual(firstPosition[1], secondPosition[1])); + CPPUNIT_ASSERT(!math::isExactlyEqual(firstPosition[2], secondPosition[2])); + + // Test spread + + points = points::uniformPointScatter(grid, total, /*seed*/1, /*spread*/0.2f); + + attributeSet = &(points->tree().cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(size_t(1), attributeSet->size()); + array = attributeSet->getConst(0); + CPPUNIT_ASSERT(array); + CPPUNIT_ASSERT(array->isType()); + + size = array->size(); + CPPUNIT_ASSERT_EQUAL(size_t(total), size); + + pHandle = AttributeHandle::create(*array); + for (size_t i = 0; i < size; ++i) { + const Vec3f P = pHandle->get(Index(i)); + CPPUNIT_ASSERT(P[0] >=-0.2f); + CPPUNIT_ASSERT(P[0] <= 0.2f); + CPPUNIT_ASSERT(P[1] >=-0.2f); + CPPUNIT_ASSERT(P[1] <= 0.2f); + CPPUNIT_ASSERT(P[2] >=-0.2f); + CPPUNIT_ASSERT(P[2] <= 0.2f); + } + + // Test mt11213b + + using mt11213b = std::mersenne_twister_engine; + + points = points::uniformPointScatter(grid, total); + + CPPUNIT_ASSERT_EQUAL(Index32(1), points->tree().leafCount()); + CPPUNIT_ASSERT(Index64(NUM_VALUES) > points->tree().activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(total, pointCount(points->tree())); + + // Test no remainder - grid contains one tile, scatter NUM_VALUES points + + points = points::uniformPointScatter(grid, Index64(NUM_VALUES)); + + CPPUNIT_ASSERT_EQUAL(Index32(1), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(NUM_VALUES), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(NUM_VALUES), pointCount(points->tree())); + + const auto* const leaf = points->tree().probeConstLeaf(math::Coord(0)); + CPPUNIT_ASSERT(leaf); + CPPUNIT_ASSERT(leaf->isDense()); + + const auto* const data = leaf->buffer().data(); + CPPUNIT_ASSERT_EQUAL(Index32(1), Index32(data[1] - data[0])); + + for (size_t i = 1; i < NUM_VALUES; ++i) { + const Index32 offset = data[i] - data[i - 1]; + CPPUNIT_ASSERT_EQUAL(Index32(1), offset); + } +} + +void +TestPointScatter::testDenseUniformPointScatter() +{ + const Index32 pointsPerVoxel = 8; + const math::CoordBBox boxBounds(math::Coord(-1), math::Coord(1)); // 27 voxels across 8 leaves + + // Test the free function for all default grid types + + { + BoolGrid grid; + grid.sparseFill(boxBounds, false, /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + DoubleGrid grid; + grid.sparseFill(boxBounds, 0.0, /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + FloatGrid grid; + grid.sparseFill(boxBounds, 0.0f, /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + Int32Grid grid; + grid.sparseFill(boxBounds, 0, /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + Int64Grid grid; + grid.sparseFill(boxBounds, 0, /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + MaskGrid grid; + grid.sparseFill(boxBounds, /*maskBuffer*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + StringGrid grid; + grid.sparseFill(boxBounds, "", /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + Vec3DGrid grid; + grid.sparseFill(boxBounds, Vec3d(), /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + Vec3IGrid grid; + grid.sparseFill(boxBounds, Vec3i(), /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + Vec3SGrid grid; + grid.sparseFill(boxBounds, Vec3f(), /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + PointDataGrid grid; + grid.sparseFill(boxBounds, 0, /*active*/true); + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + + // Test 0 produces empty grid + + { + BoolGrid grid; + grid.sparseFill(boxBounds, false, /*active*/true); + auto points = points::denseUniformPointScatter(grid, 0.0f); + CPPUNIT_ASSERT(points->empty()); + } + + // Test topology between 0 - 1 + + { + BoolGrid grid; + grid.sparseFill(boxBounds, false, /*active*/true); + auto points = points::denseUniformPointScatter(grid, 0.8f); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + // Note that a value of 22 is precomputed as the number of active + // voxels/points produced by a value of 0.8 + CPPUNIT_ASSERT_EQUAL(Index64(22), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(22), pointCount(points->tree())); + + // Test below 0 throws + + CPPUNIT_ASSERT_THROW(points::denseUniformPointScatter(grid, -0.1f), openvdb::ValueError); + } + + // Test a grid containing tiles scatters correctly + + BoolGrid grid; + grid.tree().addTile(/*level*/1, math::Coord(0), /*value*/true, /*active*/true); + grid.tree().setValueOn(math::Coord(8,0,0)); // add another leaf + + const Index32 NUM_VALUES = BoolGrid::TreeType::LeafNodeType::NUM_VALUES; + + CPPUNIT_ASSERT_EQUAL(Index32(1), grid.tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(NUM_VALUES + 1), grid.activeVoxelCount()); + + auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); + + const Index64 expectedCount = Index64(pointsPerVoxel * (NUM_VALUES + 1)); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + CPPUNIT_ASSERT_EQUAL(Index64(0), points->tree().activeTileCount()); +#endif + CPPUNIT_ASSERT_EQUAL(Index32(2), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(NUM_VALUES + 1), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(expectedCount, pointCount(points->tree())); + + // Explicitly check P attribute + + const auto* attributeSet = &(points->tree().cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(size_t(1), attributeSet->size()); + const auto* array = attributeSet->getConst(0); + CPPUNIT_ASSERT(array); + + using PositionArrayT = TypedAttributeArray; + CPPUNIT_ASSERT(array->isType()); + + size_t size = array->size(); + CPPUNIT_ASSERT_EQUAL(size_t(pointsPerVoxel * NUM_VALUES), size); + + AttributeHandle::Ptr pHandle = + AttributeHandle::create(*array); + for (size_t i = 0; i < size; ++i) { + const Vec3f P = pHandle->get(Index(i)); + CPPUNIT_ASSERT(P[0] >=-0.5f); + CPPUNIT_ASSERT(P[0] <= 0.5f); + CPPUNIT_ASSERT(P[1] >=-0.5f); + CPPUNIT_ASSERT(P[1] <= 0.5f); + CPPUNIT_ASSERT(P[2] >=-0.5f); + CPPUNIT_ASSERT(P[2] <= 0.5f); + } + + // Test the rng seed + + const Vec3f firstPosition = pHandle->get(0); + points = points::denseUniformPointScatter(grid, pointsPerVoxel, /*seed*/1); + + attributeSet = &(points->tree().cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(size_t(1), attributeSet->size()); + + array = attributeSet->getConst(0); + CPPUNIT_ASSERT(array); + CPPUNIT_ASSERT(array->isType()); + + size = array->size(); + CPPUNIT_ASSERT_EQUAL(size_t(pointsPerVoxel * NUM_VALUES), size); + pHandle = AttributeHandle::create(*array); + + const Vec3f secondPosition = pHandle->get(0); + CPPUNIT_ASSERT(!math::isExactlyEqual(firstPosition[0], secondPosition[0])); + CPPUNIT_ASSERT(!math::isExactlyEqual(firstPosition[1], secondPosition[1])); + CPPUNIT_ASSERT(!math::isExactlyEqual(firstPosition[2], secondPosition[2])); + + // Test spread + + points = points::denseUniformPointScatter(grid, pointsPerVoxel, /*seed*/1, /*spread*/0.2f); + + attributeSet = &(points->tree().cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(size_t(1), attributeSet->size()); + array = attributeSet->getConst(0); + CPPUNIT_ASSERT(array); + CPPUNIT_ASSERT(array->isType()); + + size = array->size(); + CPPUNIT_ASSERT_EQUAL(size_t(pointsPerVoxel * NUM_VALUES), size); + + pHandle = AttributeHandle::create(*array); + for (size_t i = 0; i < size; ++i) { + const Vec3f P = pHandle->get(Index(i)); + CPPUNIT_ASSERT(P[0] >=-0.2f); + CPPUNIT_ASSERT(P[0] <= 0.2f); + CPPUNIT_ASSERT(P[1] >=-0.2f); + CPPUNIT_ASSERT(P[1] <= 0.2f); + CPPUNIT_ASSERT(P[2] >=-0.2f); + CPPUNIT_ASSERT(P[2] <= 0.2f); + } + + // Test mt11213b + + using mt11213b = std::mersenne_twister_engine; + + points = points::denseUniformPointScatter(grid, pointsPerVoxel); + + CPPUNIT_ASSERT_EQUAL(Index32(2), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(NUM_VALUES + 1), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(expectedCount, pointCount(points->tree())); +} + +void +TestPointScatter::testNonUniformPointScatter() +{ + const Index32 pointsPerVoxel = 8; + const math::CoordBBox totalBoxBounds(math::Coord(-2), math::Coord(2)); // 125 voxels across 8 leaves + const math::CoordBBox activeBoxBounds(math::Coord(-1), math::Coord(1)); // 27 voxels across 8 leaves + + // Test the free function for all default scalar grid types + + { + BoolGrid grid; + grid.sparseFill(totalBoxBounds, false, /*active*/true); + grid.sparseFill(activeBoxBounds, true, /*active*/true); + auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + DoubleGrid grid; + grid.sparseFill(totalBoxBounds, 0.0, /*active*/true); + grid.sparseFill(activeBoxBounds, 1.0, /*active*/true); + auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + FloatGrid grid; + grid.sparseFill(totalBoxBounds, 0.0f, /*active*/true); + grid.sparseFill(activeBoxBounds, 1.0f, /*active*/true); + auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + Int32Grid grid; + grid.sparseFill(totalBoxBounds, 0, /*active*/true); + grid.sparseFill(activeBoxBounds, 1, /*active*/true); + auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + Int64Grid grid; + grid.sparseFill(totalBoxBounds, 0, /*active*/true); + grid.sparseFill(activeBoxBounds, 1, /*active*/true); + auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + { + MaskGrid grid; + grid.sparseFill(totalBoxBounds, /*maskBuffer*/0); + grid.sparseFill(activeBoxBounds, /*maskBuffer*/1); + auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); + CPPUNIT_ASSERT_EQUAL(Index32(8), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(27), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 27), pointCount(points->tree())); + } + + BoolGrid grid; + + // Test below 0 throws + + CPPUNIT_ASSERT_THROW(points::nonUniformPointScatter(grid, -0.1f), openvdb::ValueError); + + // Test a grid containing tiles scatters correctly + + grid.tree().addTile(/*level*/1, math::Coord(0), /*value*/true, /*active*/true); + grid.tree().setValueOn(math::Coord(8,0,0), true); // add another leaf + + const Index32 NUM_VALUES = BoolGrid::TreeType::LeafNodeType::NUM_VALUES; + + CPPUNIT_ASSERT_EQUAL(Index32(1), grid.tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(NUM_VALUES + 1), grid.activeVoxelCount()); + + auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); + + const Index64 expectedCount = Index64(pointsPerVoxel * (NUM_VALUES + 1)); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + CPPUNIT_ASSERT_EQUAL(Index64(0), points->tree().activeTileCount()); +#endif + CPPUNIT_ASSERT_EQUAL(Index32(2), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(NUM_VALUES + 1), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(expectedCount, pointCount(points->tree())); + + // Explicitly check P attribute + + const auto* attributeSet = &(points->tree().cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(size_t(1), attributeSet->size()); + const auto* array = attributeSet->getConst(0); + CPPUNIT_ASSERT(array); + + using PositionArrayT = TypedAttributeArray; + CPPUNIT_ASSERT(array->isType()); + + size_t size = array->size(); + CPPUNIT_ASSERT_EQUAL(size_t(pointsPerVoxel * NUM_VALUES), size); + + AttributeHandle::Ptr pHandle = + AttributeHandle::create(*array); + for (size_t i = 0; i < size; ++i) { + const Vec3f P = pHandle->get(Index(i)); + CPPUNIT_ASSERT(P[0] >=-0.5f); + CPPUNIT_ASSERT(P[0] <= 0.5f); + CPPUNIT_ASSERT(P[1] >=-0.5f); + CPPUNIT_ASSERT(P[1] <= 0.5f); + CPPUNIT_ASSERT(P[2] >=-0.5f); + CPPUNIT_ASSERT(P[2] <= 0.5f); + } + + // Test the rng seed + + const Vec3f firstPosition = pHandle->get(0); + points = points::nonUniformPointScatter(grid, pointsPerVoxel, /*seed*/1); + + attributeSet = &(points->tree().cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(size_t(1), attributeSet->size()); + + array = attributeSet->getConst(0); + CPPUNIT_ASSERT(array); + CPPUNIT_ASSERT(array->isType()); + + size = array->size(); + CPPUNIT_ASSERT_EQUAL(size_t(pointsPerVoxel * NUM_VALUES), size); + pHandle = AttributeHandle::create(*array); + + const Vec3f secondPosition = pHandle->get(0); + CPPUNIT_ASSERT(!math::isExactlyEqual(firstPosition[0], secondPosition[0])); + CPPUNIT_ASSERT(!math::isExactlyEqual(firstPosition[1], secondPosition[1])); + CPPUNIT_ASSERT(!math::isExactlyEqual(firstPosition[2], secondPosition[2])); + + // Test spread + + points = points::nonUniformPointScatter(grid, pointsPerVoxel, /*seed*/1, /*spread*/0.2f); + + attributeSet = &(points->tree().cbeginLeaf()->attributeSet()); + CPPUNIT_ASSERT_EQUAL(size_t(1), attributeSet->size()); + array = attributeSet->getConst(0); + CPPUNIT_ASSERT(array); + CPPUNIT_ASSERT(array->isType()); + + size = array->size(); + CPPUNIT_ASSERT_EQUAL(size_t(pointsPerVoxel * NUM_VALUES), size); + + pHandle = AttributeHandle::create(*array); + for (size_t i = 0; i < size; ++i) { + const Vec3f P = pHandle->get(Index(i)); + CPPUNIT_ASSERT(P[0] >=-0.2f); + CPPUNIT_ASSERT(P[0] <= 0.2f); + CPPUNIT_ASSERT(P[1] >=-0.2f); + CPPUNIT_ASSERT(P[1] <= 0.2f); + CPPUNIT_ASSERT(P[2] >=-0.2f); + CPPUNIT_ASSERT(P[2] <= 0.2f); + } + + // Test varying counts + + Int32Grid countGrid; + + // tets negative values equate to 0 + countGrid.tree().setValueOn(Coord(0), -1); + for (int i = 1; i < 8; ++i) { + countGrid.tree().setValueOn(Coord(i), i); + } + + points = points::nonUniformPointScatter(countGrid, pointsPerVoxel); + + CPPUNIT_ASSERT_EQUAL(Index32(1), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(7), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(pointsPerVoxel * 28), pointCount(points->tree())); + + for (int i = 1; i < 8; ++i) { + CPPUNIT_ASSERT(points->tree().isValueOn(Coord(i))); + auto& value = points->tree().getValue(Coord(i)); + Index32 expected(0); + for (Index32 j = i; j > 0; --j) expected += j; + CPPUNIT_ASSERT_EQUAL(Index32(expected * pointsPerVoxel), Index32(value)); + } + + // Test mt11213b + + using mt11213b = std::mersenne_twister_engine; + + points = points::nonUniformPointScatter(grid, pointsPerVoxel); + + CPPUNIT_ASSERT_EQUAL(Index32(2), points->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(NUM_VALUES + 1), points->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(expectedCount, pointCount(points->tree())); +} + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointScatter); diff --git a/openvdb/unittest/TestPointsToMask.cc b/openvdb/unittest/TestPointsToMask.cc new file mode 100644 index 00000000..61e6ccf9 --- /dev/null +++ b/openvdb/unittest/TestPointsToMask.cc @@ -0,0 +1,172 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include // for math::Random01 +#include +#include +#include +#include +#include +#include +#include "util.h" // for genPoints + + +struct TestPointsToMask: public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(TestPointsToMask); + CPPUNIT_TEST(testPointsToMask); + CPPUNIT_TEST_SUITE_END(); + + void testPointsToMask(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPointsToMask); + +//////////////////////////////////////// + +namespace { + +class PointList +{ +public: + PointList(const std::vector& points) : mPoints(&points) {} + + size_t size() const { return mPoints->size(); } + + void getPos(size_t n, openvdb::Vec3R& xyz) const { xyz = (*mPoints)[n]; } +protected: + std::vector const * const mPoints; +}; // PointList + +} // namespace + + + +//////////////////////////////////////// + + +void +TestPointsToMask::testPointsToMask() +{ + {// BoolGrid + // generate one point + std::vector points; + points.push_back( openvdb::Vec3R(-19.999, 4.50001, 6.71) ); + //points.push_back( openvdb::Vec3R( 20,-4.5,-5.2) ); + PointList pointList(points); + + // construct an empty mask grid + openvdb::BoolGrid grid( false ); + const float voxelSize = 0.1f; + grid.setTransform( openvdb::math::Transform::createLinearTransform(voxelSize) ); + CPPUNIT_ASSERT( grid.empty() ); + + // generate mask from points + openvdb::tools::PointsToMask mask( grid ); + mask.addPoints( pointList ); + CPPUNIT_ASSERT(!grid.empty() ); + CPPUNIT_ASSERT_EQUAL( 1, int(grid.activeVoxelCount()) ); + openvdb::BoolGrid::ValueOnCIter iter = grid.cbeginValueOn(); + //std::cerr << "Coord = " << iter.getCoord() << std::endl; + const openvdb::Coord p(-200, 45, 67); + CPPUNIT_ASSERT( iter.getCoord() == p ); + CPPUNIT_ASSERT(grid.tree().isValueOn( p ) ); + } + + {// MaskGrid + // generate one point + std::vector points; + points.push_back( openvdb::Vec3R(-19.999, 4.50001, 6.71) ); + //points.push_back( openvdb::Vec3R( 20,-4.5,-5.2) ); + PointList pointList(points); + + // construct an empty mask grid + openvdb::MaskGrid grid( false ); + const float voxelSize = 0.1f; + grid.setTransform( openvdb::math::Transform::createLinearTransform(voxelSize) ); + CPPUNIT_ASSERT( grid.empty() ); + + // generate mask from points + openvdb::tools::PointsToMask<> mask( grid ); + mask.addPoints( pointList ); + CPPUNIT_ASSERT(!grid.empty() ); + CPPUNIT_ASSERT_EQUAL( 1, int(grid.activeVoxelCount()) ); + openvdb::TopologyGrid::ValueOnCIter iter = grid.cbeginValueOn(); + //std::cerr << "Coord = " << iter.getCoord() << std::endl; + const openvdb::Coord p(-200, 45, 67); + CPPUNIT_ASSERT( iter.getCoord() == p ); + CPPUNIT_ASSERT(grid.tree().isValueOn( p ) ); + } + + + // generate shared transformation + openvdb::Index64 voxelCount = 0; + const float voxelSize = 0.001f; + const openvdb::math::Transform::Ptr xform = + openvdb::math::Transform::createLinearTransform(voxelSize); + + // generate lots of points + std::vector points; + unittest_util::genPoints(15000000, points); + PointList pointList(points); + + //openvdb::util::CpuTimer timer; + {// serial BoolGrid + // construct an empty mask grid + openvdb::BoolGrid grid( false ); + grid.setTransform( xform ); + CPPUNIT_ASSERT( grid.empty() ); + + // generate mask from points + openvdb::tools::PointsToMask mask( grid ); + //timer.start("\nSerial BoolGrid"); + mask.addPoints( pointList, 0 ); + //timer.stop(); + + CPPUNIT_ASSERT(!grid.empty() ); + //grid.print(std::cerr, 3); + voxelCount = grid.activeVoxelCount(); + } + {// parallel BoolGrid + // construct an empty mask grid + openvdb::BoolGrid grid( false ); + grid.setTransform( xform ); + CPPUNIT_ASSERT( grid.empty() ); + + // generate mask from points + openvdb::tools::PointsToMask mask( grid ); + //timer.start("\nParallel BoolGrid"); + mask.addPoints( pointList ); + //timer.stop(); + + CPPUNIT_ASSERT(!grid.empty() ); + //grid.print(std::cerr, 3); + CPPUNIT_ASSERT_EQUAL( voxelCount, grid.activeVoxelCount() ); + } + {// parallel MaskGrid + // construct an empty mask grid + openvdb::MaskGrid grid( false ); + grid.setTransform( xform ); + CPPUNIT_ASSERT( grid.empty() ); + + // generate mask from points + openvdb::tools::PointsToMask<> mask( grid ); + //timer.start("\nParallel MaskGrid"); + mask.addPoints( pointList ); + //timer.stop(); + + CPPUNIT_ASSERT(!grid.empty() ); + //grid.print(std::cerr, 3); + CPPUNIT_ASSERT_EQUAL( voxelCount, grid.activeVoxelCount() ); + } + {// parallel create TopologyGrid + //timer.start("\nParallel Create MaskGrid"); + openvdb::MaskGrid::Ptr grid = openvdb::tools::createPointMask(pointList, *xform); + //timer.stop(); + + CPPUNIT_ASSERT(!grid->empty() ); + //grid->print(std::cerr, 3); + CPPUNIT_ASSERT_EQUAL( voxelCount, grid->activeVoxelCount() ); + } +} diff --git a/openvdb/unittest/TestPoissonSolver.cc b/openvdb/unittest/TestPoissonSolver.cc new file mode 100644 index 00000000..201bae9b --- /dev/null +++ b/openvdb/unittest/TestPoissonSolver.cc @@ -0,0 +1,511 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file unittest/TestPoissonSolver.cc +/// @authors D.J. Hill, Peter Cucka + +#include +#include +#include +//#include // for math::isApproxEqual() +#include // for JacobiPreconditioner +#include // for csgDifference/Union/Intersection +#include // for tools::createLevelSetSphere() +#include // for tools::sdfToFogVolume() +#include // for createLevelSetBox() +#include // for tools::erodeVoxels() +#include +#include // for boost::math::constants::pi +#include + + +class TestPoissonSolver: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestPoissonSolver); + CPPUNIT_TEST(testIndexTree); + CPPUNIT_TEST(testTreeToVectorToTree); + CPPUNIT_TEST(testLaplacian); + CPPUNIT_TEST(testSolve); + CPPUNIT_TEST(testSolveWithBoundaryConditions); + CPPUNIT_TEST(testSolveWithSegmentedDomain); + CPPUNIT_TEST_SUITE_END(); + + void testIndexTree(); + void testTreeToVectorToTree(); + void testLaplacian(); + void testSolve(); + void testSolveWithBoundaryConditions(); + void testSolveWithSegmentedDomain(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPoissonSolver); + + +//////////////////////////////////////// + + +void +TestPoissonSolver::testIndexTree() +{ + using namespace openvdb; + using tools::poisson::VIndex; + + using VIdxTree = FloatTree::ValueConverter::Type; + using LeafNodeType = VIdxTree::LeafNodeType; + + VIdxTree tree; + /// @todo populate tree + tree::LeafManager leafManager(tree); + + VIndex testOffset = 0; + for (size_t n = 0, N = leafManager.leafCount(); n < N; ++n) { + const LeafNodeType& leaf = leafManager.leaf(n); + for (LeafNodeType::ValueOnCIter it = leaf.cbeginValueOn(); it; ++it, testOffset++) { + CPPUNIT_ASSERT_EQUAL(testOffset, *it); + } + } + + //if (testOffset != VIndex(tree.activeVoxelCount())) { + // std::cout << "--Testing offsetmap - " + // << testOffset<<" != " + // << tree.activeVoxelCount() + // << " has active tile count " + // << tree.activeTileCount()<::Type; + + FloatGrid::Ptr sphere = tools::createLevelSetSphere( + /*radius=*/10.f, /*center=*/Vec3f(0.f), /*voxelSize=*/0.25f); + tools::sdfToFogVolume(*sphere); + FloatTree& inputTree = sphere->tree(); + + const Index64 numVoxels = inputTree.activeVoxelCount(); + + // Generate an index tree. + VIdxTree::Ptr indexTree = tools::poisson::createIndexTree(inputTree); + CPPUNIT_ASSERT(bool(indexTree)); + + // Copy the values of the active voxels of the tree into a vector. + math::pcg::VectorS::Ptr vec = + tools::poisson::createVectorFromTree(inputTree, *indexTree); + CPPUNIT_ASSERT_EQUAL(math::pcg::SizeType(numVoxels), vec->size()); + + { + // Convert the vector back to a tree. + FloatTree::Ptr inputTreeCopy = tools::poisson::createTreeFromVector( + *vec, *indexTree, /*bg=*/0.f); + + // Check that voxel values were preserved. + FloatGrid::ConstAccessor inputAcc = sphere->getConstAccessor(); + for (FloatTree::ValueOnCIter it = inputTreeCopy->cbeginValueOn(); it; ++it) { + const Coord ijk = it.getCoord(); + //if (!math::isApproxEqual(*it, inputTree.getValue(ijk))) { + // std::cout << " value error " << *it << " " + // << inputTree.getValue(ijk) << std::endl; + //} + CPPUNIT_ASSERT_DOUBLES_EQUAL(inputAcc.getValue(ijk), *it, /*tolerance=*/1.0e-6); + } + } +} + + +void +TestPoissonSolver::testLaplacian() +{ + using namespace openvdb; + using tools::poisson::VIndex; + + using VIdxTree = FloatTree::ValueConverter::Type; + + // For two different problem sizes, N = 8 and N = 20... + for (int N = 8; N <= 20; N += 12) { + // Construct an N x N x N volume in which the value of voxel (i, j, k) + // is sin(i) * sin(j) * sin(k), using a voxel spacing of pi / N. + const double delta = boost::math::constants::pi() / N; + FloatTree inputTree(/*background=*/0.f); + Coord ijk(0); + Int32 &i = ijk[0], &j = ijk[1], &k = ijk[2]; + for (i = 1; i < N; ++i) { + for (j = 1; j < N; ++j) { + for (k = 1; k < N; ++k) { + inputTree.setValue(ijk, static_cast( + std::sin(delta * i) * std::sin(delta * j) * std::sin(delta * k))); + } + } + } + const Index64 numVoxels = inputTree.activeVoxelCount(); + + // Generate an index tree. + VIdxTree::Ptr indexTree = tools::poisson::createIndexTree(inputTree); + CPPUNIT_ASSERT(bool(indexTree)); + + // Copy the values of the active voxels of the tree into a vector. + math::pcg::VectorS::Ptr source = + tools::poisson::createVectorFromTree(inputTree, *indexTree); + CPPUNIT_ASSERT_EQUAL(math::pcg::SizeType(numVoxels), source->size()); + + // Create a mask of the interior voxels of the source tree. + BoolTree interiorMask(/*background=*/false); + interiorMask.fill(CoordBBox(Coord(2), Coord(N-2)), /*value=*/true, /*active=*/true); + + // Compute the Laplacian of the source: + // D^2 sin(i) * sin(j) * sin(k) = -3 sin(i) * sin(j) * sin(k) + tools::poisson::LaplacianMatrix::Ptr laplacian = + tools::poisson::createISLaplacian(*indexTree, interiorMask, /*staggered=*/true); + laplacian->scale(1.0 / (delta * delta)); // account for voxel spacing + CPPUNIT_ASSERT_EQUAL(math::pcg::SizeType(numVoxels), laplacian->size()); + + math::pcg::VectorS result(source->size()); + laplacian->vectorMultiply(*source, result); + + // Dividing the result by the source should produce a vector of uniform value -3. + // Due to finite differencing, the actual ratio will be somewhat different, though. + const math::pcg::VectorS& src = *source; + const float expected = // compute the expected ratio using one of the corner voxels + float((3.0 * src[1] - 6.0 * src[0]) / (delta * delta * src[0])); + for (math::pcg::SizeType n = 0; n < result.size(); ++n) { + result[n] /= src[n]; + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, result[n], /*tolerance=*/1.0e-4); + } + } +} + + +void +TestPoissonSolver::testSolve() +{ + using namespace openvdb; + + FloatGrid::Ptr sphere = tools::createLevelSetSphere( + /*radius=*/10.f, /*center=*/Vec3f(0.f), /*voxelSize=*/0.25f); + tools::sdfToFogVolume(*sphere); + + math::pcg::State result = math::pcg::terminationDefaults(); + result.iterations = 100; + result.relativeError = result.absoluteError = 1.0e-4; + + FloatTree::Ptr outTree = tools::poisson::solve(sphere->tree(), result); + + CPPUNIT_ASSERT(result.success); + CPPUNIT_ASSERT(result.iterations < 60); +} + + +//////////////////////////////////////// + + +namespace { + +struct BoundaryOp { + void operator()(const openvdb::Coord& ijk, const openvdb::Coord& neighbor, + double& source, double& diagonal) const + { + if (neighbor.x() == ijk.x() && neighbor.z() == ijk.z()) { + // Workaround for spurious GCC 4.8 -Wstrict-overflow warning: + const openvdb::Coord::ValueType dy = (ijk.y() - neighbor.y()); + if (dy > 0) source -= 1.0; + else diagonal -= 1.0; + } + } +}; + + +template +void +doTestSolveWithBoundaryConditions() +{ + using namespace openvdb; + + using ValueType = typename TreeType::ValueType; + + // Solve for the pressure in a cubic tank of liquid that is open at the top. + // Boundary conditions are P = 0 at the top, dP/dy = -1 at the bottom + // and dP/dx = 0 at the sides. + // + // P = 0 + // +------+ (N,-1,N) + // /| /| + // (0,-1,0) +------+ | + // | | | | dP/dx = 0 + // dP/dx = 0 | +----|-+ + // |/ |/ + // (0,-N-1,0) +------+ (N,-N-1,0) + // dP/dy = -1 + + const int N = 9; + const ValueType zero = zeroVal(); + const double epsilon = math::Delta::value(); + + TreeType source(/*background=*/zero); + source.fill(CoordBBox(Coord(0, -N-1, 0), Coord(N, -1, N)), /*value=*/zero); + + math::pcg::State state = math::pcg::terminationDefaults(); + state.iterations = 100; + state.relativeError = state.absoluteError = epsilon; + + util::NullInterrupter interrupter; + + typename TreeType::Ptr solution = tools::poisson::solveWithBoundaryConditions( + source, BoundaryOp(), state, interrupter, /*staggered=*/true); + + CPPUNIT_ASSERT(state.success); + CPPUNIT_ASSERT(state.iterations < 60); + + // Verify that P = -y throughout the solution space. + for (typename TreeType::ValueOnCIter it = solution->cbeginValueOn(); it; ++it) { + CPPUNIT_ASSERT_DOUBLES_EQUAL( + double(-it.getCoord().y()), double(*it), /*tolerance=*/10.0 * epsilon); + } +} + +} // unnamed namespace + + +void +TestPoissonSolver::testSolveWithBoundaryConditions() +{ + doTestSolveWithBoundaryConditions(); + doTestSolveWithBoundaryConditions(); +} + + +namespace { + +openvdb::FloatGrid::Ptr +newCubeLS( + const int outerLength, // in voxels + const int innerLength, // in voxels + const openvdb::Vec3I& centerIS, // in index space + const float dx, // grid spacing + bool openTop) +{ + using namespace openvdb; + + using BBox = math::BBox; + + // World space dimensions and center for this box + const float outerWS = dx * float(outerLength); + const float innerWS = dx * float(innerLength); + Vec3f centerWS(centerIS); + centerWS *= dx; + + // Construct world space bounding boxes + BBox outerBBox( + Vec3f(-outerWS / 2, -outerWS / 2, -outerWS / 2), + Vec3f( outerWS / 2, outerWS / 2, outerWS / 2)); + BBox innerBBox; + if (openTop) { + innerBBox = BBox( + Vec3f(-innerWS / 2, -innerWS / 2, -innerWS / 2), + Vec3f( innerWS / 2, innerWS / 2, outerWS)); + } else { + innerBBox = BBox( + Vec3f(-innerWS / 2, -innerWS / 2, -innerWS / 2), + Vec3f( innerWS / 2, innerWS / 2, innerWS / 2)); + } + outerBBox.translate(centerWS); + innerBBox.translate(centerWS); + + math::Transform::Ptr xform = math::Transform::createLinearTransform(dx); + FloatGrid::Ptr cubeLS = tools::createLevelSetBox(outerBBox, *xform); + FloatGrid::Ptr inside = tools::createLevelSetBox(innerBBox, *xform); + tools::csgDifference(*cubeLS, *inside); + + return cubeLS; +} + + +class LSBoundaryOp +{ +public: + LSBoundaryOp(const openvdb::FloatTree& lsTree): mLS(&lsTree) {} + LSBoundaryOp(const LSBoundaryOp& other): mLS(other.mLS) {} + + void operator()(const openvdb::Coord& ijk, const openvdb::Coord& neighbor, + double& source, double& diagonal) const + { + // Doing nothing is equivalent to imposing dP/dn = 0 boundary condition + + if (neighbor.x() == ijk.x() && neighbor.y() == ijk.y()) { // on top or bottom + if (mLS->getValue(neighbor) <= 0.f) { + // closed boundary + source -= 1.0; + } else { + // open boundary + diagonal -= 1.0; + } + } + } + +private: + const openvdb::FloatTree* mLS; +}; + +} // unnamed namespace + + +void +TestPoissonSolver::testSolveWithSegmentedDomain() +{ + // In fluid simulations, incompressibility is enforced by the pressure, which is + // computed as a solution of a Poisson equation. Often, procedural animation + // of objects (e.g., characters) interacting with liquid will result in boundary + // conditions that describe multiple disjoint regions: regions of free surface flow + // and regions of trapped fluid. It is this second type of region for which + // there may be no consistent pressure (e.g., a shrinking watertight region + // filled with incompressible liquid). + // + // This unit test demonstrates how to use a level set and topological tools + // to separate the well-posed problem of a liquid with a free surface + // from the possibly ill-posed problem of fully enclosed liquid regions. + // + // For simplicity's sake, the physical boundaries are idealized as three + // non-overlapping cubes, one with an open top and two that are fully closed. + // All three contain incompressible liquid (x), and one of the closed cubes + // will be partially filled so that two of the liquid regions have a free surface + // (Dirichlet boundary condition on one side) while the totally filled cube + // would have no free surface (Neumann boundary conditions on all sides). + // ________________ ________________ + // __ __ | __________ | | __________ | + // | |x x x x x | | | | | | | |x x x x x | | + // | |x x x x x | | | |x x x x x | | | |x x x x x | | + // | |x x x x x | | | |x x x x x | | | |x x x x x | | + // | —————————— | | —————————— | | —————————— | + // |________________| |________________| |________________| + // + // The first two regions are clearly well-posed, while the third region + // may have no solution (or multiple solutions). + // -D.J.Hill + + using namespace openvdb; + + using PreconditionerType = + math::pcg::IncompleteCholeskyPreconditioner; + + // Grid spacing + const float dx = 0.05f; + + // Construct the solid boundaries in a single grid. + FloatGrid::Ptr solidBoundary; + { + // Create three non-overlapping cubes. + const int outerDim = 41; + const int innerDim = 31; + FloatGrid::Ptr + openDomain = newCubeLS(outerDim, innerDim, /*ctr=*/Vec3I(0, 0, 0), dx, /*open=*/true), + closedDomain0 = newCubeLS(outerDim, innerDim, /*ctr=*/Vec3I(60, 0, 0), dx, false), + closedDomain1 = newCubeLS(outerDim, innerDim, /*ctr=*/Vec3I(120, 0, 0), dx, false); + + // Union all three cubes into one grid. + tools::csgUnion(*openDomain, *closedDomain0); + tools::csgUnion(*openDomain, *closedDomain1); + + // Strictly speaking the solidBoundary level set should be rebuilt + // (with tools::levelSetRebuild()) after the csgUnions to insure a proper + // signed distance field, but we will forgo the rebuild in this example. + solidBoundary = openDomain; + } + + // Generate the source for the Poisson solver. + // For a liquid simulation this will be the divergence of the velocity field + // and will coincide with the liquid location. + // + // We activate by hand cells in distinct solution regions. + + FloatTree source(/*background=*/0.f); + + // The source is active in the union of the following "liquid" regions: + + // Fill the open box. + const int N = 15; + CoordBBox liquidInOpenDomain(Coord(-N, -N, -N), Coord(N, N, N)); + source.fill(liquidInOpenDomain, 0.f); + + // Totally fill closed box 0. + CoordBBox liquidInClosedDomain0(Coord(-N, -N, -N), Coord(N, N, N)); + liquidInClosedDomain0.translate(Coord(60, 0, 0)); + source.fill(liquidInClosedDomain0, 0.f); + + // Half fill closed box 1. + CoordBBox liquidInClosedDomain1(Coord(-N, -N, -N), Coord(N, N, 0)); + liquidInClosedDomain1.translate(Coord(120, 0, 0)); + source.fill(liquidInClosedDomain1, 0.f); + + // Compute the number of voxels in the well-posed region of the source. + const Index64 expectedWellPosedVolume = + liquidInOpenDomain.volume() + liquidInClosedDomain1.volume(); + + // Generate a mask that defines the solution domain. + // Inactive values of the source map to false and active values map to true. + const BoolTree totalSourceDomain(source, /*inactive=*/false, /*active=*/true, TopologyCopy()); + + // Extract the "interior regions" from the solid boundary. + // The result will correspond to the the walls of the boxes unioned with inside of the full box. + const BoolTree::ConstPtr interiorMask = tools::extractEnclosedRegion( + solidBoundary->tree(), /*isovalue=*/float(0), &totalSourceDomain); + + // Identify the well-posed part of the problem. + BoolTree wellPosedDomain(source, /*inactive=*/false, /*active=*/true, TopologyCopy()); + wellPosedDomain.topologyDifference(*interiorMask); + CPPUNIT_ASSERT_EQUAL(expectedWellPosedVolume, wellPosedDomain.activeVoxelCount()); + + // Solve the well-posed Poisson equation. + + const double epsilon = math::Delta::value(); + math::pcg::State state = math::pcg::terminationDefaults(); + state.iterations = 200; + state.relativeError = state.absoluteError = epsilon; + + util::NullInterrupter interrupter; + + // Define boundary conditions that are consistent with solution = 0 + // at the liquid/air boundary and with a linear response with depth. + LSBoundaryOp boundaryOp(solidBoundary->tree()); + + // Compute the solution + FloatTree::Ptr wellPosedSolutionP = + tools::poisson::solveWithBoundaryConditionsAndPreconditioner( + source, wellPosedDomain, boundaryOp, state, interrupter, /*staggered=*/true); + + CPPUNIT_ASSERT_EQUAL(expectedWellPosedVolume, wellPosedSolutionP->activeVoxelCount()); + CPPUNIT_ASSERT(state.success); + CPPUNIT_ASSERT(state.iterations < 68); + + // Verify that the solution is linear with depth. + for (FloatTree::ValueOnCIter it = wellPosedSolutionP->cbeginValueOn(); it; ++it) { + Index32 depth; + if (liquidInOpenDomain.isInside(it.getCoord())) { + depth = 1 + liquidInOpenDomain.max().z() - it.getCoord().z(); + } else { + depth = 1 + liquidInClosedDomain1.max().z() - it.getCoord().z(); + } + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(depth), double(*it), /*tolerance=*/10.0 * epsilon); + } + +#if 0 + // Optionally, one could attempt to compute the solution in the enclosed regions. + { + // Identify the potentially ill-posed part of the problem. + BoolTree illPosedDomain(source, /*inactive=*/false, /*active=*/true, TopologyCopy()); + illPosedDomain.topologyIntersection(source); + + // Solve the Poisson equation in the two unconnected regions. + FloatTree::Ptr illPosedSoln = + tools::poisson::solveWithBoundaryConditionsAndPreconditioner( + source, illPosedDomain, LSBoundaryOp(*solidBoundary->tree()), + state, interrupter, /*staggered=*/true); + } +#endif +} diff --git a/openvdb/unittest/TestPotentialFlow.cc b/openvdb/unittest/TestPotentialFlow.cc new file mode 100644 index 00000000..963532ae --- /dev/null +++ b/openvdb/unittest/TestPotentialFlow.cc @@ -0,0 +1,429 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file unittest/TestPotentialFlow.cc + +#include +#include +#include +#include + + +class TestPotentialFlow: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestPotentialFlow); + CPPUNIT_TEST(testMask); + CPPUNIT_TEST(testNeumannVelocities); + CPPUNIT_TEST(testUniformStream); + CPPUNIT_TEST(testFlowAroundSphere); + CPPUNIT_TEST_SUITE_END(); + void testMask(); + void testNeumannVelocities(); + void testUniformStream(); + void testFlowAroundSphere(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPotentialFlow); + + +void +TestPotentialFlow::testMask() +{ + using namespace openvdb; + + const float radius = 1.5f; + const Vec3f center(0.0f, 0.0f, 0.0f); + const float voxelSize = 0.25f; + const float halfWidth = 3.0f; + + FloatGrid::Ptr sphere = + tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); + + const int dilation = 5; + + MaskGrid::Ptr mask = tools::createPotentialFlowMask(*sphere, dilation); + MaskGrid::Ptr defaultMask = tools::createPotentialFlowMask(*sphere); + CPPUNIT_ASSERT(*mask == *defaultMask); + + auto acc = mask->getAccessor(); + + // the isosurface of this sphere is at y = 6 + // this mask forms a band dilated outwards from the isosurface by 5 voxels + + CPPUNIT_ASSERT(!acc.isValueOn(Coord(0, 5, 0))); + CPPUNIT_ASSERT(acc.isValueOn(Coord(0, 6, 0))); + CPPUNIT_ASSERT(acc.isValueOn(Coord(0, 10, 0))); + CPPUNIT_ASSERT(!acc.isValueOn(Coord(0, 11, 0))); + + { // error on non-uniform voxel size + FloatGrid::Ptr nonUniformSphere = + tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); + math::Transform::Ptr nonUniformTransform(new math::Transform( + math::MapBase::Ptr(new math::ScaleMap(Vec3d(0.1, 0.2, 0.3))))); + nonUniformSphere->setTransform(nonUniformTransform); + + CPPUNIT_ASSERT_THROW(tools::createPotentialFlowMask(*nonUniformSphere, dilation), + openvdb::ValueError); + } + + // this is the minimum mask of one voxel either side of the isosurface + + mask = tools::createPotentialFlowMask(*sphere, 2); + + acc = mask->getAccessor(); + + CPPUNIT_ASSERT(!acc.isValueOn(Coord(0, 5, 0))); + CPPUNIT_ASSERT(acc.isValueOn(Coord(0, 6, 0))); + CPPUNIT_ASSERT(acc.isValueOn(Coord(0, 7, 0))); + CPPUNIT_ASSERT(!acc.isValueOn(Coord(0, 8, 0))); + + // these should all produce the same masks as the dilation value is clamped + + MaskGrid::Ptr negativeMask = tools::createPotentialFlowMask(*sphere, -1); + MaskGrid::Ptr zeroMask = tools::createPotentialFlowMask(*sphere, 0); + MaskGrid::Ptr oneMask = tools::createPotentialFlowMask(*sphere, 1); + + CPPUNIT_ASSERT(*negativeMask == *mask); + CPPUNIT_ASSERT(*zeroMask == *mask); + CPPUNIT_ASSERT(*oneMask == *mask); +} + + +void +TestPotentialFlow::testNeumannVelocities() +{ + using namespace openvdb; + + const float radius = 1.5f; + const Vec3f center(0.0f, 0.0f, 0.0f); + const float voxelSize = 0.25f; + const float halfWidth = 3.0f; + + FloatGrid::Ptr sphere = + tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); + + MaskGrid::Ptr domain = tools::createPotentialFlowMask(*sphere); + + { + // test identical potential from a wind velocity supplied through grid or background value + + Vec3d windVelocityValue(0, 0, 10); + + Vec3dTree::Ptr windTree(new Vec3dTree(sphere->tree(), zeroVal(), TopologyCopy())); + dilateVoxels(*windTree, 2, tools::NN_FACE_EDGE_VERTEX); + windTree->voxelizeActiveTiles(); + + for (auto leaf = windTree->beginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->beginValueOn(); iter; ++iter) { + iter.setValue(windVelocityValue); + } + } + + Vec3dGrid::Ptr windGrid(Vec3dGrid::create(windTree)); + windGrid->setTransform(sphere->transform().copy()); + + auto windPotentialFromGrid = tools::createPotentialFlowNeumannVelocities( + *sphere, *domain, windGrid, Vec3d(0)); + + CPPUNIT_ASSERT_EQUAL(windPotentialFromGrid->transform(), sphere->transform()); + + auto windPotentialFromBackground = tools::createPotentialFlowNeumannVelocities( + *sphere, *domain, Vec3dGrid::Ptr(), windVelocityValue); + + auto accessor = windPotentialFromGrid->getConstAccessor(); + auto accessor2 = windPotentialFromBackground->getConstAccessor(); + + CPPUNIT_ASSERT_EQUAL(windPotentialFromGrid->activeVoxelCount(), + windPotentialFromBackground->activeVoxelCount()); + + for (auto leaf = windPotentialFromGrid->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL(accessor.isValueOn(iter.getCoord()), + accessor2.isValueOn(iter.getCoord())); + CPPUNIT_ASSERT_EQUAL(accessor.getValue(iter.getCoord()), + accessor2.getValue(iter.getCoord())); + } + } + + // test potential from a wind velocity supplied through grid background value + + Vec3dTree::Ptr emptyWindTree( + new Vec3dTree(sphere->tree(), windVelocityValue, TopologyCopy())); + Vec3dGrid::Ptr emptyWindGrid(Vec3dGrid::create(emptyWindTree)); + emptyWindGrid->setTransform(sphere->transform().copy()); + + auto windPotentialFromGridBackground = tools::createPotentialFlowNeumannVelocities( + *sphere, *domain, emptyWindGrid, Vec3d(0)); + + CPPUNIT_ASSERT_EQUAL(windPotentialFromGridBackground->transform(), sphere->transform()); + + accessor = windPotentialFromGridBackground->getConstAccessor(); + accessor2 = windPotentialFromBackground->getConstAccessor(); + + CPPUNIT_ASSERT_EQUAL(windPotentialFromGridBackground->activeVoxelCount(), + windPotentialFromBackground->activeVoxelCount()); + + for (auto leaf = windPotentialFromGridBackground->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL(accessor.isValueOn(iter.getCoord()), + accessor2.isValueOn(iter.getCoord())); + CPPUNIT_ASSERT_EQUAL(accessor.getValue(iter.getCoord()), + accessor2.getValue(iter.getCoord())); + } + } + + // test potential values are double when applying wind velocity + // through grid and background values + + auto windPotentialFromBoth = tools::createPotentialFlowNeumannVelocities( + *sphere, *domain, windGrid, windVelocityValue); + + tools::prune(windPotentialFromBoth->tree(), Vec3d(1e-3)); + tools::prune(windPotentialFromBackground->tree(), Vec3d(1e-3)); + + accessor = windPotentialFromBoth->getConstAccessor(); + accessor2 = windPotentialFromBackground->getConstAccessor(); + + for (auto leaf = windPotentialFromBoth->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL(accessor.isValueOn(iter.getCoord()), + accessor2.isValueOn(iter.getCoord())); + CPPUNIT_ASSERT_EQUAL(accessor.getValue(iter.getCoord()), + accessor2.getValue(iter.getCoord()) * 2); + } + } + + CPPUNIT_ASSERT(*windPotentialFromBoth == *windPotentialFromBackground); + } + + Vec3dGrid::Ptr zeroVelocity = Vec3dGrid::create(Vec3d(0)); + + { // error if grid is not a levelset + FloatGrid::Ptr nonLevelSetSphere = + tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); + nonLevelSetSphere->setGridClass(GRID_FOG_VOLUME); + + CPPUNIT_ASSERT_THROW(tools::createPotentialFlowNeumannVelocities( + *nonLevelSetSphere, *domain, zeroVelocity, Vec3d(5)), openvdb::TypeError); + } + + { // accept double level set grid + DoubleGrid::Ptr doubleSphere = + tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); + + CPPUNIT_ASSERT_NO_THROW(tools::createPotentialFlowNeumannVelocities( + *doubleSphere, *domain, zeroVelocity, Vec3d(5))); + } + + { // zero boundary velocities and background velocity + Vec3d zeroVelocityValue(zeroVal()); + auto neumannVelocities = tools::createPotentialFlowNeumannVelocities( + *sphere, *domain, zeroVelocity, zeroVelocityValue); + CPPUNIT_ASSERT_EQUAL(neumannVelocities->activeVoxelCount(), Index64(0)); + } +} + + +void +TestPotentialFlow::testUniformStream() +{ + // this unit test checks the scalar potential and velocity flow field + // for a uniform stream which consists of a 100x100x100 cube of + // neumann voxels with constant velocity (0, 0, 1) + + using namespace openvdb; + + auto transform = math::Transform::createLinearTransform(1.0); + + auto mask = MaskGrid::create(false); + mask->setTransform(transform); + auto maskAccessor = mask->getAccessor(); + + auto neumann = Vec3dGrid::create(Vec3d(0)); + auto neumannAccessor = neumann->getAccessor(); + + for (int i = -50; i < 50; i++) { + for (int j = -50; j < 50; j++) { + for (int k = -50; k < 50; k++) { + Coord ijk(i, j, k); + maskAccessor.setValueOn(ijk, true); + neumannAccessor.setValueOn(ijk, Vec3d(0, 0, 1)); + } + } + } + + openvdb::math::pcg::State state = math::pcg::terminationDefaults(); + + state.iterations = 2000; + state.absoluteError = 1e-8; + + auto potential = tools::computeScalarPotential(*mask, *neumann, state); + + // check convergence + + CPPUNIT_ASSERT(state.success); + CPPUNIT_ASSERT(state.iterations > 0 && state.iterations < 1000); + CPPUNIT_ASSERT(state.absoluteError < 1e-6); + + CPPUNIT_ASSERT_EQUAL(potential->activeVoxelCount(), mask->activeVoxelCount()); + + // for uniform flow along the z-axis, the scalar potential should be equal to the z co-ordinate + + for (auto leaf = potential->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + const double staggeredZ = iter.getCoord().z() + 0.5; + CPPUNIT_ASSERT(math::isApproxEqual(iter.getValue(), staggeredZ, /*tolerance*/0.1)); + } + } + + auto flow = tools::computePotentialFlow(*potential, *neumann); + + CPPUNIT_ASSERT_EQUAL(flow->activeVoxelCount(), mask->activeVoxelCount()); + + // flow velocity should be equal to the input velocity (0, 0, 1) + + for (auto leaf = flow->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT(math::isApproxEqual(iter.getValue().x(), 0.0, /*tolerance*/1e-6)); + CPPUNIT_ASSERT(math::isApproxEqual(iter.getValue().y(), 0.0, /*tolerance*/1e-6)); + CPPUNIT_ASSERT(math::isApproxEqual(iter.getValue().z(), 1.0, /*tolerance*/1e-6)); + } + } +} + + +void +TestPotentialFlow::testFlowAroundSphere() +{ + using namespace openvdb; + + const float radius = 1.5f; + const Vec3f center(0.0f, 0.0f, 0.0f); + const float voxelSize = 0.25f; + const float halfWidth = 3.0f; + + const int dilation = 50; + + FloatGrid::Ptr sphere = + tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); + + MaskGrid::Ptr domain = tools::createPotentialFlowMask(*sphere, dilation); + + { // compute potential flow for a global wind velocity around a sphere + + Vec3f windVelocity(0, 0, 1); + Vec3fGrid::Ptr neumann = tools::createPotentialFlowNeumannVelocities(*sphere, + *domain, Vec3fGrid::Ptr(), windVelocity); + + openvdb::math::pcg::State state = math::pcg::terminationDefaults(); + + state.iterations = 2000; + state.absoluteError = 1e-8; + + FloatGrid::Ptr potential = tools::computeScalarPotential(*domain, *neumann, state); + + // compute a laplacian of the potential within the domain (excluding neumann voxels) + // and ensure it evaluates to zero + + auto mask = BoolGrid::create(/*background=*/false); + mask->setTransform(potential->transform().copy()); + mask->topologyUnion(*potential); + + auto dilatedSphereMask = tools::interiorMask(*sphere); + tools::dilateActiveValues(dilatedSphereMask->tree(), 1); + mask->topologyDifference(*dilatedSphereMask); + + FloatGrid::Ptr laplacian = tools::laplacian(*potential, *mask); + + for (auto leaf = laplacian->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT(math::isApproxEqual(iter.getValue(), 0.0f, /*tolerance*/1e-3f)); + } + } + + Vec3fGrid::Ptr flowVel = tools::computePotentialFlow(*potential, *neumann); + + // compute the divergence of the flow velocity within the domain + // (excluding neumann voxels and exterior voxels) + // and ensure it evaluates to zero + + tools::erodeVoxels(mask->tree(), 2, tools::NN_FACE); + + FloatGrid::Ptr divergence = tools::divergence(*flowVel, *mask); + + for (auto leaf = divergence->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT(math::isApproxEqual(iter.getValue(), 0.0f, /*tolerance*/0.1f)); + } + } + + // check the background velocity has been applied correctly + + Vec3fGrid::Ptr flowVelBackground = + tools::computePotentialFlow(*potential, *neumann, windVelocity); + + CPPUNIT_ASSERT_EQUAL(flowVelBackground->activeVoxelCount(), + flowVelBackground->activeVoxelCount()); + + auto maskAccessor = mask->getConstAccessor(); + + auto accessor = flowVel->getConstAccessor(); + auto accessor2 = flowVelBackground->getConstAccessor(); + + for (auto leaf = flowVelBackground->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + // ignore values near the neumann boundary + if (!maskAccessor.isValueOn(iter.getCoord())) continue; + + const Vec3f value1 = accessor.getValue(iter.getCoord()); + const Vec3f value2 = accessor2.getValue(iter.getCoord()) + windVelocity; + + CPPUNIT_ASSERT(math::isApproxEqual(value1.x(), value2.x(), /*tolerance=*/1e-3f)); + CPPUNIT_ASSERT(math::isApproxEqual(value1.y(), value2.y(), /*tolerance=*/1e-3f)); + CPPUNIT_ASSERT(math::isApproxEqual(value1.z(), value2.z(), /*tolerance=*/1e-3f)); + } + } + } + + { // check double-precision solve + DoubleGrid::Ptr sphereDouble = + tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); + + Vec3d windVelocity(0, 0, 1); + Vec3dGrid::Ptr neumann = tools::createPotentialFlowNeumannVelocities(*sphereDouble, + *domain, Vec3dGrid::Ptr(), windVelocity); + + openvdb::math::pcg::State state = math::pcg::terminationDefaults(); + + state.iterations = 2000; + state.absoluteError = 1e-8; + + DoubleGrid::Ptr potential = tools::computeScalarPotential(*domain, *neumann, state); + + CPPUNIT_ASSERT(potential); + + // compute a laplacian of the potential within the domain (excluding neumann voxels) + // and ensure it evaluates to zero + + auto mask = BoolGrid::create(/*background=*/false); + mask->setTransform(potential->transform().copy()); + mask->topologyUnion(*potential); + + auto dilatedSphereMask = tools::interiorMask(*sphereDouble); + tools::dilateActiveValues(dilatedSphereMask->tree(), 1); + mask->topologyDifference(*dilatedSphereMask); + + DoubleGrid::Ptr laplacian = tools::laplacian(*potential, *mask); + + for (auto leaf = laplacian->tree().cbeginLeaf(); leaf; ++leaf) { + for (auto iter = leaf->cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT(math::isApproxEqual(iter.getValue(), 0.0, /*tolerance*/1e-5)); + } + } + + Vec3dGrid::Ptr flowVel = tools::computePotentialFlow(*potential, *neumann); + + CPPUNIT_ASSERT(flowVel); + } +} diff --git a/openvdb/unittest/TestPrePostAPI.cc b/openvdb/unittest/TestPrePostAPI.cc new file mode 100644 index 00000000..113b8d0d --- /dev/null +++ b/openvdb/unittest/TestPrePostAPI.cc @@ -0,0 +1,686 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include + + +class TestPrePostAPI: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestPrePostAPI); + + CPPUNIT_TEST(testMat4); + CPPUNIT_TEST(testMat4Rotate); + CPPUNIT_TEST(testMat4Scale); + CPPUNIT_TEST(testMat4Shear); + CPPUNIT_TEST(testMaps); + CPPUNIT_TEST(testLinearTransform); + CPPUNIT_TEST(testFrustumTransform); + + CPPUNIT_TEST_SUITE_END(); + + void testMat4(); + void testMat4Rotate(); + void testMat4Scale(); + void testMat4Shear(); + void testMaps(); + void testLinearTransform(); + void testFrustumTransform(); + //void testIsType(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPrePostAPI); + + +void +TestPrePostAPI::testMat4() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + + Mat4d m = Mat4d::identity(); + Mat4d minv = Mat4d::identity(); + + // create matrix with pre-API + // Translate Shear Rotate Translate Scale matrix + m.preScale(Vec3d(1, 2, 3)); + m.preTranslate(Vec3d(2, 3, 4)); + m.preRotate(X_AXIS, 20); + m.preShear(X_AXIS, Y_AXIS, 2); + m.preTranslate(Vec3d(2, 2, 2)); + + // create inverse using the post-API + minv.postScale(Vec3d(1.f, 1.f/2.f, 1.f/3.f)); + minv.postTranslate(-Vec3d(2, 3, 4)); + minv.postRotate(X_AXIS,-20); + minv.postShear(X_AXIS, Y_AXIS, -2); + minv.postTranslate(-Vec3d(2, 2, 2)); + + Mat4d mtest = minv * m; + + // verify that the results is an identity + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][0], 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][1], 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][2], 1, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][3], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][2], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][3], 1, TOL); +} + + +void +TestPrePostAPI::testMat4Rotate() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + Mat4d rx, ry, rz; + const double angle1 = 20. * M_PI / 180.; + const double angle2 = 64. * M_PI / 180.; + const double angle3 = 125. *M_PI / 180.; + rx.setToRotation(Vec3d(1,0,0), angle1); + ry.setToRotation(Vec3d(0,1,0), angle2); + rz.setToRotation(Vec3d(0,0,1), angle3); + + Mat4d shear = Mat4d::identity(); + shear.setToShear(X_AXIS, Z_AXIS, 2.0); + shear.preShear(Y_AXIS, X_AXIS, 3.0); + shear.preTranslate(Vec3d(2,4,1)); + + const Mat4d preResult = rz*ry*rx*shear; + Mat4d mpre = shear; + mpre.preRotate(X_AXIS, angle1); + mpre.preRotate(Y_AXIS, angle2); + mpre.preRotate(Z_AXIS, angle3); + + CPPUNIT_ASSERT( mpre.eq(preResult, TOL) ); + + const Mat4d postResult = shear*rx*ry*rz; + Mat4d mpost = shear; + mpost.postRotate(X_AXIS, angle1); + mpost.postRotate(Y_AXIS, angle2); + mpost.postRotate(Z_AXIS, angle3); + + CPPUNIT_ASSERT( mpost.eq(postResult, TOL) ); + + CPPUNIT_ASSERT( !mpost.eq(mpre, TOL)); + +} + + +void +TestPrePostAPI::testMat4Scale() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + Mat4d mpre, mpost; + double* pre = mpre.asPointer(); + double* post = mpost.asPointer(); + for (int i = 0; i < 16; ++i) { + pre[i] = double(i); + post[i] = double(i); + } + + Mat4d scale = Mat4d::identity(); + scale.setToScale(Vec3d(2, 3, 5.5)); + Mat4d preResult = scale * mpre; + Mat4d postResult = mpost * scale; + + mpre.preScale(Vec3d(2, 3, 5.5)); + mpost.postScale(Vec3d(2, 3, 5.5)); + + CPPUNIT_ASSERT( mpre.eq(preResult, TOL) ); + CPPUNIT_ASSERT( mpost.eq(postResult, TOL) ); +} + + +void +TestPrePostAPI::testMat4Shear() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + Mat4d mpre, mpost; + double* pre = mpre.asPointer(); + double* post = mpost.asPointer(); + for (int i = 0; i < 16; ++i) { + pre[i] = double(i); + post[i] = double(i); + } + + Mat4d shear = Mat4d::identity(); + shear.setToShear(X_AXIS, Z_AXIS, 13.); + Mat4d preResult = shear * mpre; + Mat4d postResult = mpost * shear; + + mpre.preShear(X_AXIS, Z_AXIS, 13.); + mpost.postShear(X_AXIS, Z_AXIS, 13.); + + CPPUNIT_ASSERT( mpre.eq(preResult, TOL) ); + CPPUNIT_ASSERT( mpost.eq(postResult, TOL) ); +} + + +void +TestPrePostAPI::testMaps() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + + { // pre translate + UniformScaleMap usm; + UniformScaleTranslateMap ustm; + ScaleMap sm; + ScaleTranslateMap stm; + AffineMap am; + + const Vec3d trans(1,2,3); + Mat4d correct = Mat4d::identity(); + correct.preTranslate(trans); + { + MapBase::Ptr base = usm.preTranslate(trans); + Mat4d result = (base->getAffineMap())->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = ustm.preTranslate(trans)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = sm.preTranslate(trans)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = stm.preTranslate(trans)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = am.preTranslate(trans)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + } + { // post translate + UniformScaleMap usm; + UniformScaleTranslateMap ustm; + ScaleMap sm; + ScaleTranslateMap stm; + AffineMap am; + + const Vec3d trans(1,2,3); + Mat4d correct = Mat4d::identity(); + correct.postTranslate(trans); + { + const Mat4d result = usm.postTranslate(trans)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = ustm.postTranslate(trans)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = sm.postTranslate(trans)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = stm.postTranslate(trans)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = am.postTranslate(trans)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + } + { // pre scale + UniformScaleMap usm; + UniformScaleTranslateMap ustm; + ScaleMap sm; + ScaleTranslateMap stm; + AffineMap am; + + const Vec3d scale(1,2,3); + Mat4d correct = Mat4d::identity(); + correct.preScale(scale); + { + const Mat4d result = usm.preScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = ustm.preScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = sm.preScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = stm.preScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = am.preScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + } + { // post scale + UniformScaleMap usm; + UniformScaleTranslateMap ustm; + ScaleMap sm; + ScaleTranslateMap stm; + AffineMap am; + + const Vec3d scale(1,2,3); + Mat4d correct = Mat4d::identity(); + correct.postScale(scale); + { + const Mat4d result = usm.postScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = ustm.postScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = sm.postScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = stm.postScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = am.postScale(scale)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + } + { // pre shear + UniformScaleMap usm; + UniformScaleTranslateMap ustm; + ScaleMap sm; + ScaleTranslateMap stm; + AffineMap am; + + Mat4d correct = Mat4d::identity(); + correct.preShear(X_AXIS, Z_AXIS, 13.); + { + const Mat4d result = usm.preShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = ustm.preShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = sm.preShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = stm.preShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = am.preShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + } + { // post shear + UniformScaleMap usm; + UniformScaleTranslateMap ustm; + ScaleMap sm; + ScaleTranslateMap stm; + AffineMap am; + + Mat4d correct = Mat4d::identity(); + correct.postShear(X_AXIS, Z_AXIS, 13.); + { + const Mat4d result = usm.postShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = + ustm.postShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = sm.postShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = stm.postShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = am.postShear(13., X_AXIS, Z_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + } + { // pre rotate + const double angle1 = 20. * M_PI / 180.; + UniformScaleMap usm; + UniformScaleTranslateMap ustm; + ScaleMap sm; + ScaleTranslateMap stm; + AffineMap am; + + Mat4d correct = Mat4d::identity(); + correct.preRotate(X_AXIS, angle1); + { + const Mat4d result = usm.preRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = ustm.preRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = sm.preRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = stm.preRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = am.preRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + } + { // post rotate + const double angle1 = 20. * M_PI / 180.; + UniformScaleMap usm; + UniformScaleTranslateMap ustm; + ScaleMap sm; + ScaleTranslateMap stm; + AffineMap am; + + Mat4d correct = Mat4d::identity(); + correct.postRotate(X_AXIS, angle1); + { + const Mat4d result = usm.postRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = ustm.postRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = sm.postRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = stm.postRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + { + const Mat4d result = am.postRotate(angle1, X_AXIS)->getAffineMap()->getConstMat4(); + CPPUNIT_ASSERT( correct.eq(result, TOL)); + } + } +} + + +void +TestPrePostAPI::testLinearTransform() +{ + using namespace openvdb::math; + + double TOL = 1e-7; + { + Transform::Ptr t = Transform::createLinearTransform(1.f); + Transform::Ptr tinv = Transform::createLinearTransform(1.f); + + // create matrix with pre-API + // Translate Shear Rotate Translate Scale matrix + t->preScale(Vec3d(1, 2, 3)); + t->preTranslate(Vec3d(2, 3, 4)); + t->preRotate(20); + t->preShear(2, X_AXIS, Y_AXIS); + t->preTranslate(Vec3d(2, 2, 2)); + + // create inverse using the post-API + tinv->postScale(Vec3d(1.f, 1.f/2.f, 1.f/3.f)); + tinv->postTranslate(-Vec3d(2, 3, 4)); + tinv->postRotate(-20); + tinv->postShear(-2, X_AXIS, Y_AXIS); + tinv->postTranslate(-Vec3d(2, 2, 2)); + + + // test this by verifying that equvilent interal matrix + // represenations are inverses + Mat4d m = t->baseMap()->getAffineMap()->getMat4(); + Mat4d minv = tinv->baseMap()->getAffineMap()->getMat4(); + + Mat4d mtest = minv * m; + + // verify that the results is an identity + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][0], 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][1], 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][2], 1, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][3], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][2], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][3], 1, TOL); + } + + { + Transform::Ptr t = Transform::createLinearTransform(1.f); + + Mat4d m = Mat4d::identity(); + + // create matrix with pre-API + // Translate Shear Rotate Translate Scale matrix + m.preScale(Vec3d(1, 2, 3)); + m.preTranslate(Vec3d(2, 3, 4)); + m.preRotate(X_AXIS, 20); + m.preShear(X_AXIS, Y_AXIS, 2); + m.preTranslate(Vec3d(2, 2, 2)); + + t->preScale(Vec3d(1,2,3)); + t->preMult(m); + t->postMult(m); + + Mat4d minv = Mat4d::identity(); + + // create inverse using the post-API + minv.postScale(Vec3d(1.f, 1.f/2.f, 1.f/3.f)); + minv.postTranslate(-Vec3d(2, 3, 4)); + minv.postRotate(X_AXIS,-20); + minv.postShear(X_AXIS, Y_AXIS, -2); + minv.postTranslate(-Vec3d(2, 2, 2)); + + t->preMult(minv); + t->postMult(minv); + + Mat4d mtest = t->baseMap()->getAffineMap()->getMat4(); + + + // verify that the results is the scale + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][0], 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][1], 2, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][2], 3, 1e-6); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][3], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][0], 0, 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][1], 0, 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][2], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][3], 1, TOL); + } + + +} + + +void +TestPrePostAPI::testFrustumTransform() +{ + using namespace openvdb::math; + + using BBoxd = BBox; + + double TOL = 1e-7; + { + + BBoxd bbox(Vec3d(-5,-5,0), Vec3d(5,5,10)); + Transform::Ptr t = Transform::createFrustumTransform( + bbox, /* taper*/ 1, /*depth*/10, /* voxel size */1.f); + Transform::Ptr tinv = Transform::createFrustumTransform( + bbox, /* taper*/ 1, /*depth*/10, /* voxel size */1.f); + + + // create matrix with pre-API + // Translate Shear Rotate Translate Scale matrix + t->preScale(Vec3d(1, 2, 3)); + t->preTranslate(Vec3d(2, 3, 4)); + t->preRotate(20); + t->preShear(2, X_AXIS, Y_AXIS); + t->preTranslate(Vec3d(2, 2, 2)); + + // create inverse using the post-API + tinv->postScale(Vec3d(1.f, 1.f/2.f, 1.f/3.f)); + tinv->postTranslate(-Vec3d(2, 3, 4)); + tinv->postRotate(-20); + tinv->postShear(-2, X_AXIS, Y_AXIS); + tinv->postTranslate(-Vec3d(2, 2, 2)); + + + // test this by verifying that equvilent interal matrix + // represenations are inverses + NonlinearFrustumMap::Ptr frustum = + openvdb::StaticPtrCast(t->baseMap()); + NonlinearFrustumMap::Ptr frustuminv = + openvdb::StaticPtrCast(tinv->baseMap()); + + Mat4d m = frustum->secondMap().getMat4(); + Mat4d minv = frustuminv->secondMap().getMat4(); + + Mat4d mtest = minv * m; + + // verify that the results is an identity + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][0], 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][1], 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][2], 1, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][3], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][2], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][3], 1, TOL); + } + + { + + BBoxd bbox(Vec3d(-5,-5,0), Vec3d(5,5,10)); + Transform::Ptr t = Transform::createFrustumTransform( + bbox, /* taper*/ 1, /*depth*/10, /* voxel size */1.f); + + + Mat4d m = Mat4d::identity(); + + // create matrix with pre-API + // Translate Shear Rotate Translate Scale matrix + m.preScale(Vec3d(1, 2, 3)); + m.preTranslate(Vec3d(2, 3, 4)); + m.preRotate(X_AXIS, 20); + m.preShear(X_AXIS, Y_AXIS, 2); + m.preTranslate(Vec3d(2, 2, 2)); + + t->preScale(Vec3d(1,2,3)); + t->preMult(m); + t->postMult(m); + + Mat4d minv = Mat4d::identity(); + + // create inverse using the post-API + minv.postScale(Vec3d(1.f, 1.f/2.f, 1.f/3.f)); + minv.postTranslate(-Vec3d(2, 3, 4)); + minv.postRotate(X_AXIS,-20); + minv.postShear(X_AXIS, Y_AXIS, -2); + minv.postTranslate(-Vec3d(2, 2, 2)); + + t->preMult(minv); + t->postMult(minv); + + NonlinearFrustumMap::Ptr frustum = + openvdb::StaticPtrCast(t->baseMap()); + Mat4d mtest = frustum->secondMap().getMat4(); + + // verify that the results is the scale + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][0], 1, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][1], 2, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][2], 3, 1e-6); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[0][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][2], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[1][3], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][0], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][1], 0, TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[2][3], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][0], 0, 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][1], 0, 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][2], 0, TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(mtest[3][3], 1, TOL); + } + + +} diff --git a/openvdb/unittest/TestQuadraticInterp.cc b/openvdb/unittest/TestQuadraticInterp.cc new file mode 100644 index 00000000..87c2b623 --- /dev/null +++ b/openvdb/unittest/TestQuadraticInterp.cc @@ -0,0 +1,330 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file TestQuadraticInterp.cc + +#include +#include +#include +#include + +// CPPUNIT_TEST_SUITE() invokes CPPUNIT_TESTNAMER_DECL() to generate a suite name +// from the FixtureType. But if FixtureType is a templated type, the generated name +// can become long and messy. This macro overrides the normal naming logic, +// instead invoking FixtureType::testSuiteName(), which should be a static member +// function that returns a std::string containing the suite name for the specific +// template instantiation. +#undef CPPUNIT_TESTNAMER_DECL +#define CPPUNIT_TESTNAMER_DECL( variableName, FixtureType ) \ + CPPUNIT_NS::TestNamer variableName( FixtureType::testSuiteName() ) + + +namespace { +// Absolute tolerance for floating-point equality comparisons +const double TOLERANCE = 1.0e-5; +} + + +//////////////////////////////////////// + + +template +class TestQuadraticInterp: public CppUnit::TestCase +{ +public: + typedef typename GridType::ValueType ValueT; + typedef typename GridType::Ptr GridPtr; + struct TestVal { float x, y, z; ValueT expected; }; + + static std::string testSuiteName() + { + std::string name = openvdb::typeNameAsString(); + if (!name.empty()) name[0] = static_cast(::toupper(name[0])); + return "TestQuadraticInterp" + name; + } + + CPPUNIT_TEST_SUITE(TestQuadraticInterp); + CPPUNIT_TEST(test); + CPPUNIT_TEST(testConstantValues); + CPPUNIT_TEST(testFillValues); + CPPUNIT_TEST(testNegativeIndices); + CPPUNIT_TEST_SUITE_END(); + + void test(); + void testConstantValues(); + void testFillValues(); + void testNegativeIndices(); + +private: + void executeTest(const GridPtr&, const TestVal*, size_t numVals) const; + + /// Initialize an arbitrary ValueType from a scalar. + static inline ValueT constValue(double d) { return ValueT(d); } + + /// Compare two numeric values for equality within an absolute tolerance. + static inline bool relEq(const ValueT& v1, const ValueT& v2) + { return fabs(v1 - v2) <= TOLERANCE; } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestQuadraticInterp); +CPPUNIT_TEST_SUITE_REGISTRATION(TestQuadraticInterp); +CPPUNIT_TEST_SUITE_REGISTRATION(TestQuadraticInterp); + + +//////////////////////////////////////// + + +/// Specialization for Vec3s grids +template<> +inline openvdb::Vec3s +TestQuadraticInterp::constValue(double d) +{ + return openvdb::Vec3s(float(d), float(d), float(d)); +} + +/// Specialization for Vec3s grids +template<> +inline bool +TestQuadraticInterp::relEq( + const openvdb::Vec3s& v1, const openvdb::Vec3s& v2) +{ + return v1.eq(v2, float(TOLERANCE)); +} + + +/// Sample the given tree at various locations and assert if +/// any of the sampled values don't match the expected values. +template +void +TestQuadraticInterp::executeTest(const GridPtr& grid, + const TestVal* testVals, size_t numVals) const +{ + openvdb::tools::GridSampler interpolator(*grid); + //openvdb::tools::QuadraticInterp interpolator(*tree); + + for (size_t i = 0; i < numVals; ++i) { + const TestVal& val = testVals[i]; + const ValueT actual = interpolator.sampleVoxel(val.x, val.y, val.z); + if (!relEq(val.expected, actual)) { + std::ostringstream ostr; + ostr << std::setprecision(10) + << "sampleVoxel(" << val.x << ", " << val.y << ", " << val.z + << "): expected " << val.expected << ", got " << actual; + CPPUNIT_FAIL(ostr.str()); + } + } +} + + +template +void +TestQuadraticInterp::test() +{ + const ValueT + one = constValue(1), + two = constValue(2), + three = constValue(3), + four = constValue(4), + fillValue = constValue(256); + + GridPtr grid(new GridType(fillValue)); + typename GridType::TreeType& tree = grid->tree(); + + tree.setValue(openvdb::Coord(10, 10, 10), one); + + tree.setValue(openvdb::Coord(11, 10, 10), two); + tree.setValue(openvdb::Coord(11, 11, 10), two); + tree.setValue(openvdb::Coord(10, 11, 10), two); + tree.setValue(openvdb::Coord( 9, 11, 10), two); + tree.setValue(openvdb::Coord( 9, 10, 10), two); + tree.setValue(openvdb::Coord( 9, 9, 10), two); + tree.setValue(openvdb::Coord(10, 9, 10), two); + tree.setValue(openvdb::Coord(11, 9, 10), two); + + tree.setValue(openvdb::Coord(10, 10, 11), three); + tree.setValue(openvdb::Coord(11, 10, 11), three); + tree.setValue(openvdb::Coord(11, 11, 11), three); + tree.setValue(openvdb::Coord(10, 11, 11), three); + tree.setValue(openvdb::Coord( 9, 11, 11), three); + tree.setValue(openvdb::Coord( 9, 10, 11), three); + tree.setValue(openvdb::Coord( 9, 9, 11), three); + tree.setValue(openvdb::Coord(10, 9, 11), three); + tree.setValue(openvdb::Coord(11, 9, 11), three); + + tree.setValue(openvdb::Coord(10, 10, 9), four); + tree.setValue(openvdb::Coord(11, 10, 9), four); + tree.setValue(openvdb::Coord(11, 11, 9), four); + tree.setValue(openvdb::Coord(10, 11, 9), four); + tree.setValue(openvdb::Coord( 9, 11, 9), four); + tree.setValue(openvdb::Coord( 9, 10, 9), four); + tree.setValue(openvdb::Coord( 9, 9, 9), four); + tree.setValue(openvdb::Coord(10, 9, 9), four); + tree.setValue(openvdb::Coord(11, 9, 9), four); + + const TestVal testVals[] = { + { 10.5f, 10.5f, 10.5f, constValue(1.703125) }, + { 10.0f, 10.0f, 10.0f, one }, + { 11.0f, 10.0f, 10.0f, two }, + { 11.0f, 11.0f, 10.0f, two }, + { 11.0f, 11.0f, 11.0f, three }, + { 9.0f, 11.0f, 9.0f, four }, + { 9.0f, 10.0f, 9.0f, four }, + { 10.1f, 10.0f, 10.0f, constValue(1.01) }, + { 10.8f, 10.8f, 10.8f, constValue(2.513344) }, + { 10.1f, 10.8f, 10.5f, constValue(1.8577) }, + { 10.8f, 10.1f, 10.5f, constValue(1.8577) }, + { 10.5f, 10.1f, 10.8f, constValue(2.2927) }, + { 10.5f, 10.8f, 10.1f, constValue(1.6977) }, + }; + const size_t numVals = sizeof(testVals) / sizeof(TestVal); + + executeTest(grid, testVals, numVals); +} + + +template +void +TestQuadraticInterp::testConstantValues() +{ + const ValueT + two = constValue(2), + fillValue = constValue(256); + + GridPtr grid(new GridType(fillValue)); + typename GridType::TreeType& tree = grid->tree(); + + tree.setValue(openvdb::Coord(10, 10, 10), two); + + tree.setValue(openvdb::Coord(11, 10, 10), two); + tree.setValue(openvdb::Coord(11, 11, 10), two); + tree.setValue(openvdb::Coord(10, 11, 10), two); + tree.setValue(openvdb::Coord( 9, 11, 10), two); + tree.setValue(openvdb::Coord( 9, 10, 10), two); + tree.setValue(openvdb::Coord( 9, 9, 10), two); + tree.setValue(openvdb::Coord(10, 9, 10), two); + tree.setValue(openvdb::Coord(11, 9, 10), two); + + tree.setValue(openvdb::Coord(10, 10, 11), two); + tree.setValue(openvdb::Coord(11, 10, 11), two); + tree.setValue(openvdb::Coord(11, 11, 11), two); + tree.setValue(openvdb::Coord(10, 11, 11), two); + tree.setValue(openvdb::Coord( 9, 11, 11), two); + tree.setValue(openvdb::Coord( 9, 10, 11), two); + tree.setValue(openvdb::Coord( 9, 9, 11), two); + tree.setValue(openvdb::Coord(10, 9, 11), two); + tree.setValue(openvdb::Coord(11, 9, 11), two); + + tree.setValue(openvdb::Coord(10, 10, 9), two); + tree.setValue(openvdb::Coord(11, 10, 9), two); + tree.setValue(openvdb::Coord(11, 11, 9), two); + tree.setValue(openvdb::Coord(10, 11, 9), two); + tree.setValue(openvdb::Coord( 9, 11, 9), two); + tree.setValue(openvdb::Coord( 9, 10, 9), two); + tree.setValue(openvdb::Coord( 9, 9, 9), two); + tree.setValue(openvdb::Coord(10, 9, 9), two); + tree.setValue(openvdb::Coord(11, 9, 9), two); + + const TestVal testVals[] = { + { 10.5f, 10.5f, 10.5f, two }, + { 10.0f, 10.0f, 10.0f, two }, + { 10.1f, 10.0f, 10.0f, two }, + { 10.8f, 10.8f, 10.8f, two }, + { 10.1f, 10.8f, 10.5f, two }, + { 10.8f, 10.1f, 10.5f, two }, + { 10.5f, 10.1f, 10.8f, two }, + { 10.5f, 10.8f, 10.1f, two } + }; + const size_t numVals = sizeof(testVals) / sizeof(TestVal); + + executeTest(grid, testVals, numVals); +} + + +template +void +TestQuadraticInterp::testFillValues() +{ + const ValueT fillValue = constValue(256); + + GridPtr grid(new GridType(fillValue)); + + const TestVal testVals[] = { + { 10.5f, 10.5f, 10.5f, fillValue }, + { 10.0f, 10.0f, 10.0f, fillValue }, + { 10.1f, 10.0f, 10.0f, fillValue }, + { 10.8f, 10.8f, 10.8f, fillValue }, + { 10.1f, 10.8f, 10.5f, fillValue }, + { 10.8f, 10.1f, 10.5f, fillValue }, + { 10.5f, 10.1f, 10.8f, fillValue }, + { 10.5f, 10.8f, 10.1f, fillValue } + }; + const size_t numVals = sizeof(testVals) / sizeof(TestVal); + + executeTest(grid, testVals, numVals); +} + + +template +void +TestQuadraticInterp::testNegativeIndices() +{ + const ValueT + one = constValue(1), + two = constValue(2), + three = constValue(3), + four = constValue(4), + fillValue = constValue(256); + + GridPtr grid(new GridType(fillValue)); + typename GridType::TreeType& tree = grid->tree(); + + tree.setValue(openvdb::Coord(-10, -10, -10), one); + + tree.setValue(openvdb::Coord(-11, -10, -10), two); + tree.setValue(openvdb::Coord(-11, -11, -10), two); + tree.setValue(openvdb::Coord(-10, -11, -10), two); + tree.setValue(openvdb::Coord( -9, -11, -10), two); + tree.setValue(openvdb::Coord( -9, -10, -10), two); + tree.setValue(openvdb::Coord( -9, -9, -10), two); + tree.setValue(openvdb::Coord(-10, -9, -10), two); + tree.setValue(openvdb::Coord(-11, -9, -10), two); + + tree.setValue(openvdb::Coord(-10, -10, -11), three); + tree.setValue(openvdb::Coord(-11, -10, -11), three); + tree.setValue(openvdb::Coord(-11, -11, -11), three); + tree.setValue(openvdb::Coord(-10, -11, -11), three); + tree.setValue(openvdb::Coord( -9, -11, -11), three); + tree.setValue(openvdb::Coord( -9, -10, -11), three); + tree.setValue(openvdb::Coord( -9, -9, -11), three); + tree.setValue(openvdb::Coord(-10, -9, -11), three); + tree.setValue(openvdb::Coord(-11, -9, -11), three); + + tree.setValue(openvdb::Coord(-10, -10, -9), four); + tree.setValue(openvdb::Coord(-11, -10, -9), four); + tree.setValue(openvdb::Coord(-11, -11, -9), four); + tree.setValue(openvdb::Coord(-10, -11, -9), four); + tree.setValue(openvdb::Coord( -9, -11, -9), four); + tree.setValue(openvdb::Coord( -9, -10, -9), four); + tree.setValue(openvdb::Coord( -9, -9, -9), four); + tree.setValue(openvdb::Coord(-10, -9, -9), four); + tree.setValue(openvdb::Coord(-11, -9, -9), four); + + const TestVal testVals[] = { + { -10.5f, -10.5f, -10.5f, constValue(-104.75586) }, + { -10.0f, -10.0f, -10.0f, one }, + { -11.0f, -10.0f, -10.0f, two }, + { -11.0f, -11.0f, -10.0f, two }, + { -11.0f, -11.0f, -11.0f, three }, + { -9.0f, -11.0f, -9.0f, four }, + { -9.0f, -10.0f, -9.0f, four }, + { -10.1f, -10.0f, -10.0f, constValue(-10.28504) }, + { -10.8f, -10.8f, -10.8f, constValue(-62.84878) }, + { -10.1f, -10.8f, -10.5f, constValue(-65.68951) }, + { -10.8f, -10.1f, -10.5f, constValue(-65.68951) }, + { -10.5f, -10.1f, -10.8f, constValue(-65.40736) }, + { -10.5f, -10.8f, -10.1f, constValue(-66.30510) }, + }; + const size_t numVals = sizeof(testVals) / sizeof(TestVal); + + executeTest(grid, testVals, numVals); +} diff --git a/openvdb/unittest/TestQuantizedUnitVec.cc b/openvdb/unittest/TestQuantizedUnitVec.cc new file mode 100644 index 00000000..5547ba6d --- /dev/null +++ b/openvdb/unittest/TestQuantizedUnitVec.cc @@ -0,0 +1,146 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class TestQuantizedUnitVec: public CppUnit::TestFixture +{ +public: + CPPUNIT_TEST_SUITE(TestQuantizedUnitVec); + CPPUNIT_TEST(testQuantization); + CPPUNIT_TEST_SUITE_END(); + + void testQuantization(); + +private: + // Generate a random number in the range [0, 1]. + double randNumber() { return double(rand()) / (double(RAND_MAX) + 1.0); } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestQuantizedUnitVec); + + +//////////////////////////////////////// + + +namespace { +const uint16_t + MASK_XSIGN = 0x8000, // 1000000000000000 + MASK_YSIGN = 0x4000, // 0100000000000000 + MASK_ZSIGN = 0x2000; // 0010000000000000 +} + + +//////////////////////////////////////// + + +void +TestQuantizedUnitVec::testQuantization() +{ + using namespace openvdb; + using namespace openvdb::math; + + // + // Check sign bits + // + Vec3s unitVec = Vec3s(-1.0, -1.0, -1.0); + unitVec.normalize(); + + uint16_t quantizedVec = QuantizedUnitVec::pack(unitVec); + + CPPUNIT_ASSERT((quantizedVec & MASK_XSIGN)); + CPPUNIT_ASSERT((quantizedVec & MASK_YSIGN)); + CPPUNIT_ASSERT((quantizedVec & MASK_ZSIGN)); + + unitVec[0] = -unitVec[0]; + unitVec[2] = -unitVec[2]; + quantizedVec = QuantizedUnitVec::pack(unitVec); + + CPPUNIT_ASSERT(!(quantizedVec & MASK_XSIGN)); + CPPUNIT_ASSERT((quantizedVec & MASK_YSIGN)); + CPPUNIT_ASSERT(!(quantizedVec & MASK_ZSIGN)); + + unitVec[1] = -unitVec[1]; + quantizedVec = QuantizedUnitVec::pack(unitVec); + + CPPUNIT_ASSERT(!(quantizedVec & MASK_XSIGN)); + CPPUNIT_ASSERT(!(quantizedVec & MASK_YSIGN)); + CPPUNIT_ASSERT(!(quantizedVec & MASK_ZSIGN)); + + QuantizedUnitVec::flipSignBits(quantizedVec); + + CPPUNIT_ASSERT((quantizedVec & MASK_XSIGN)); + CPPUNIT_ASSERT((quantizedVec & MASK_YSIGN)); + CPPUNIT_ASSERT((quantizedVec & MASK_ZSIGN)); + + unitVec[2] = -unitVec[2]; + quantizedVec = QuantizedUnitVec::pack(unitVec); + QuantizedUnitVec::flipSignBits(quantizedVec); + + CPPUNIT_ASSERT((quantizedVec & MASK_XSIGN)); + CPPUNIT_ASSERT((quantizedVec & MASK_YSIGN)); + CPPUNIT_ASSERT(!(quantizedVec & MASK_ZSIGN)); + + // + // Check conversion error + // + const double tol = 0.05; // component error tolerance + + const int numNormals = 40000; + + + // init + srand(0); + const int n = int(std::sqrt(double(numNormals))); + const double xScale = (2.0 * M_PI) / double(n); + const double yScale = M_PI / double(n); + + double x, y, theta, phi; + Vec3s n0, n1; + + // generate random normals, by uniformly distributing points on a unit-sphere. + + // loop over a [0 to n) x [0 to n) grid. + for (int a = 0; a < n; ++a) { + for (int b = 0; b < n; ++b) { + + // jitter, move to random pos. inside the current cell + x = double(a) + randNumber(); + y = double(b) + randNumber(); + + // remap to a lat/long map + theta = y * yScale; // [0 to PI] + phi = x * xScale; // [0 to 2PI] + + // convert to cartesian coordinates on a unit sphere. + // spherical coordinate triplet (r=1, theta, phi) + n0[0] = float(std::sin(theta)*std::cos(phi)); + n0[1] = float(std::sin(theta)*std::sin(phi)); + n0[2] = float(std::cos(theta)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, n0.length(), 1e-6); + + n1 = QuantizedUnitVec::unpack(QuantizedUnitVec::pack(n0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, n1.length(), 1e-6); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(n0[0], n1[0], tol); + CPPUNIT_ASSERT_DOUBLES_EQUAL(n0[1], n1[1], tol); + CPPUNIT_ASSERT_DOUBLES_EQUAL(n0[2], n1[2], tol); + + float sumDiff = std::abs(n0[0] - n1[0]) + std::abs(n0[1] - n1[1]) + + std::abs(n0[2] - n1[2]); + + CPPUNIT_ASSERT(sumDiff < (2.0 * tol)); + } + } +} diff --git a/openvdb/unittest/TestQuat.cc b/openvdb/unittest/TestQuat.cc new file mode 100644 index 00000000..12d39586 --- /dev/null +++ b/openvdb/unittest/TestQuat.cc @@ -0,0 +1,282 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +using namespace openvdb::math; + +class TestQuat: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE( TestQuat ); + CPPUNIT_TEST( testConstructor ); + CPPUNIT_TEST( testAxisAngle ); + CPPUNIT_TEST( testOpPlus ); + CPPUNIT_TEST( testOpMinus ); + CPPUNIT_TEST( testOpMultiply ); + CPPUNIT_TEST( testInvert ); + CPPUNIT_TEST( testEulerAngles ); + CPPUNIT_TEST_SUITE_END(); + + void testConstructor(); + void testAxisAngle(); + void testOpPlus(); + void testOpMinus(); + void testOpMultiply(); + void testInvert(); + void testEulerAngles(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestQuat); + + +void +TestQuat::testConstructor() +{ + { + Quat qq(1.23f, 2.34f, 3.45f, 4.56f); + CPPUNIT_ASSERT( isExactlyEqual(qq.x(), 1.23f) ); + CPPUNIT_ASSERT( isExactlyEqual(qq.y(), 2.34f) ); + CPPUNIT_ASSERT( isExactlyEqual(qq.z(), 3.45f) ); + CPPUNIT_ASSERT( isExactlyEqual(qq.w(), 4.56f) ); + } + + { + float a[] = { 1.23f, 2.34f, 3.45f, 4.56f }; + Quat qq(a); + CPPUNIT_ASSERT( isExactlyEqual(qq.x(), 1.23f) ); + CPPUNIT_ASSERT( isExactlyEqual(qq.y(), 2.34f) ); + CPPUNIT_ASSERT( isExactlyEqual(qq.z(), 3.45f) ); + CPPUNIT_ASSERT( isExactlyEqual(qq.w(), 4.56f) ); + } +} + + +void +TestQuat::testAxisAngle() +{ + float TOL = 1e-6f; + + Quat q1(1.0f, 2.0f, 3.0f, 4.0f); + Quat q2(1.2f, 2.3f, 3.4f, 4.5f); + + Vec3s v(1, 2, 3); + v.normalize(); + float a = float(M_PI / 4.f); + + Quat q(v,a); + float b = q.angle(); + Vec3s vv = q.axis(); + + CPPUNIT_ASSERT( isApproxEqual(a, b, TOL) ); + CPPUNIT_ASSERT( v.eq(vv, TOL) ); + + q1.setAxisAngle(v,a); + b = q1.angle(); + vv = q1.axis(); + CPPUNIT_ASSERT( isApproxEqual(a, b, TOL) ); + CPPUNIT_ASSERT( v.eq(vv, TOL) ); +} + + +void +TestQuat::testOpPlus() +{ + Quat q1(1.0f, 2.0f, 3.0f, 4.0f); + Quat q2(1.2f, 2.3f, 3.4f, 4.5f); + + Quat q = q1 + q2; + + float + x=q1.x()+q2.x(), y=q1.y()+q2.y(), z=q1.z()+q2.z(), w=q1.w()+q2.w(); + CPPUNIT_ASSERT( isExactlyEqual(q.x(), x) ); + CPPUNIT_ASSERT( isExactlyEqual(q.y(), y) ); + CPPUNIT_ASSERT( isExactlyEqual(q.z(), z) ); + CPPUNIT_ASSERT( isExactlyEqual(q.w(), w) ); + + q = q1; + q += q2; + CPPUNIT_ASSERT( isExactlyEqual(q.x(), x) ); + CPPUNIT_ASSERT( isExactlyEqual(q.y(), y) ); + CPPUNIT_ASSERT( isExactlyEqual(q.z(), z) ); + CPPUNIT_ASSERT( isExactlyEqual(q.w(), w) ); + + q.add(q1,q2); + CPPUNIT_ASSERT( isExactlyEqual(q.x(), x) ); + CPPUNIT_ASSERT( isExactlyEqual(q.y(), y) ); + CPPUNIT_ASSERT( isExactlyEqual(q.z(), z) ); + CPPUNIT_ASSERT( isExactlyEqual(q.w(), w) ); +} + + +void +TestQuat::testOpMinus() +{ + Quat q1(1.0f, 2.0f, 3.0f, 4.0f); + Quat q2(1.2f, 2.3f, 3.4f, 4.5f); + + Quat q = q1 - q2; + + float + x=q1.x()-q2.x(), y=q1.y()-q2.y(), z=q1.z()-q2.z(), w=q1.w()-q2.w(); + CPPUNIT_ASSERT( isExactlyEqual(q.x(), x) ); + CPPUNIT_ASSERT( isExactlyEqual(q.y(), y) ); + CPPUNIT_ASSERT( isExactlyEqual(q.z(), z) ); + CPPUNIT_ASSERT( isExactlyEqual(q.w(), w) ); + + q = q1; + q -= q2; + CPPUNIT_ASSERT( isExactlyEqual(q.x(), x) ); + CPPUNIT_ASSERT( isExactlyEqual(q.y(), y) ); + CPPUNIT_ASSERT( isExactlyEqual(q.z(), z) ); + CPPUNIT_ASSERT( isExactlyEqual(q.w(), w) ); + + q.sub(q1,q2); + CPPUNIT_ASSERT( isExactlyEqual(q.x(), x) ); + CPPUNIT_ASSERT( isExactlyEqual(q.y(), y) ); + CPPUNIT_ASSERT( isExactlyEqual(q.z(), z) ); + CPPUNIT_ASSERT( isExactlyEqual(q.w(), w) ); +} + + +void +TestQuat::testOpMultiply() +{ + Quat q1(1.0f, 2.0f, 3.0f, 4.0f); + Quat q2(1.2f, 2.3f, 3.4f, 4.5f); + + Quat q = q1 * 1.5f; + + CPPUNIT_ASSERT( isExactlyEqual(q.x(), float(1.5f)*q1.x()) ); + CPPUNIT_ASSERT( isExactlyEqual(q.y(), float(1.5f)*q1.y()) ); + CPPUNIT_ASSERT( isExactlyEqual(q.z(), float(1.5f)*q1.z()) ); + CPPUNIT_ASSERT( isExactlyEqual(q.w(), float(1.5f)*q1.w()) ); + + q = q1; + q *= 1.5f; + CPPUNIT_ASSERT( isExactlyEqual(q.x(), float(1.5f)*q1.x()) ); + CPPUNIT_ASSERT( isExactlyEqual(q.y(), float(1.5f)*q1.y()) ); + CPPUNIT_ASSERT( isExactlyEqual(q.z(), float(1.5f)*q1.z()) ); + CPPUNIT_ASSERT( isExactlyEqual(q.w(), float(1.5f)*q1.w()) ); + + q.scale(1.5f, q1); + CPPUNIT_ASSERT( isExactlyEqual(q.x(), float(1.5f)*q1.x()) ); + CPPUNIT_ASSERT( isExactlyEqual(q.y(), float(1.5f)*q1.y()) ); + CPPUNIT_ASSERT( isExactlyEqual(q.z(), float(1.5f)*q1.z()) ); + CPPUNIT_ASSERT( isExactlyEqual(q.w(), float(1.5f)*q1.w()) ); +} + + +void +TestQuat::testInvert() +{ + float TOL = 1e-6f; + + Quat q1(1.0f, 2.0f, 3.0f, 4.0f); + Quat q2(1.2f, 2.3f, 3.4f, 4.5f); + + + q1 = q2; + q2 = q2.inverse(); + + Quat q = q1*q2; + + CPPUNIT_ASSERT( q.eq( Quat(0,0,0,1), TOL ) ); + + q1.normalize(); + q2 = q1.conjugate(); + q = q1*q2; + CPPUNIT_ASSERT( q.eq( Quat(0,0,0,1), TOL ) ); +} + + +void +TestQuat::testEulerAngles() +{ + + { + double TOL = 1e-7; + + Mat4d rx, ry, rz; + const double angle1 = 20. * M_PI / 180.; + const double angle2 = 64. * M_PI / 180.; + const double angle3 = 125. *M_PI / 180.; + rx.setToRotation(Vec3d(1,0,0), angle1); + ry.setToRotation(Vec3d(0,1,0), angle2); + rz.setToRotation(Vec3d(0,0,1), angle3); + + Mat4d r = rx * ry * rz; + + const Quat rot(r.getMat3()); + Vec3d result = rot.eulerAngles(ZYX_ROTATION); + + rx.setToRotation(Vec3d(1,0,0), result[0]); + ry.setToRotation(Vec3d(0,1,0), result[1]); + rz.setToRotation(Vec3d(0,0,1), result[2]); + + Mat4d rtest = rx * ry * rz; + + CPPUNIT_ASSERT(r.eq(rtest, TOL)); + } + + { + double TOL = 1e-7; + + Mat4d rx, ry, rz; + const double angle1 = 20. * M_PI / 180.; + const double angle2 = 64. * M_PI / 180.; + const double angle3 = 125. *M_PI / 180.; + rx.setToRotation(Vec3d(1,0,0), angle1); + ry.setToRotation(Vec3d(0,1,0), angle2); + rz.setToRotation(Vec3d(0,0,1), angle3); + + Mat4d r = rz * ry * rx; + + const Quat rot(r.getMat3()); + Vec3d result = rot.eulerAngles(XYZ_ROTATION); + + rx.setToRotation(Vec3d(1,0,0), result[0]); + ry.setToRotation(Vec3d(0,1,0), result[1]); + rz.setToRotation(Vec3d(0,0,1), result[2]); + + Mat4d rtest = rz * ry * rx; + + CPPUNIT_ASSERT(r.eq(rtest, TOL)); + } + + { + double TOL = 1e-7; + + Mat4d rx, ry, rz; + const double angle1 = 20. * M_PI / 180.; + const double angle2 = 64. * M_PI / 180.; + const double angle3 = 125. *M_PI / 180.; + rx.setToRotation(Vec3d(1,0,0), angle1); + ry.setToRotation(Vec3d(0,1,0), angle2); + rz.setToRotation(Vec3d(0,0,1), angle3); + + Mat4d r = rz * rx * ry; + + const Quat rot(r.getMat3()); + Vec3d result = rot.eulerAngles(YXZ_ROTATION); + + rx.setToRotation(Vec3d(1,0,0), result[0]); + ry.setToRotation(Vec3d(0,1,0), result[1]); + rz.setToRotation(Vec3d(0,0,1), result[2]); + + Mat4d rtest = rz * rx * ry; + + CPPUNIT_ASSERT(r.eq(rtest, TOL)); + } + + { + const Quat rot(X_AXIS, 1.0); + Vec3s result = rot.eulerAngles(XZY_ROTATION); + CPPUNIT_ASSERT_EQUAL(result, Vec3s(1,0,0)); + } + +} diff --git a/openvdb/unittest/TestRay.cc b/openvdb/unittest/TestRay.cc new file mode 100644 index 00000000..330c17b4 --- /dev/null +++ b/openvdb/unittest/TestRay.cc @@ -0,0 +1,463 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + +#define ASSERT_DOUBLES_APPROX_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/1.e-6); + +class TestRay : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestRay); + CPPUNIT_TEST(testInfinity); + CPPUNIT_TEST(testRay); + CPPUNIT_TEST(testTimeSpan); + CPPUNIT_TEST(testDDA); + CPPUNIT_TEST_SUITE_END(); + + void testInfinity(); + void testRay(); + void testTimeSpan(); + void testDDA(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestRay); + +// the Ray class makes use of infinity=1/0 so we test for it +void +TestRay::testInfinity() +{ + // This code generates compiler warnings which is why it's not + // enabled by default. + /* + const double one=1, zero = 0, infinity = one / zero; + CPPUNIT_ASSERT_DOUBLES_EQUAL( infinity , infinity,0);//not a NAN + CPPUNIT_ASSERT_DOUBLES_EQUAL( infinity , infinity+1,0);//not a NAN + CPPUNIT_ASSERT_DOUBLES_EQUAL( infinity , infinity*10,0);//not a NAN + CPPUNIT_ASSERT( zero < infinity); + CPPUNIT_ASSERT( zero > -infinity); + CPPUNIT_ASSERT_DOUBLES_EQUAL( zero , one/infinity,0); + CPPUNIT_ASSERT_DOUBLES_EQUAL( zero , -one/infinity,0); + CPPUNIT_ASSERT_DOUBLES_EQUAL( infinity , one/zero,0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-infinity , -one/zero,0); + + std::cerr << "inf: " << infinity << "\n"; + std::cerr << "1 / inf: " << one / infinity << "\n"; + std::cerr << "1 / (-inf): " << one / (-infinity) << "\n"; + std::cerr << " inf * 0: " << infinity * 0 << "\n"; + std::cerr << "-inf * 0: " << (-infinity) * 0 << "\n"; + std::cerr << "(inf): " << (bool)(infinity) << "\n"; + std::cerr << "inf == inf: " << (infinity == infinity) << "\n"; + std::cerr << "inf > 0: " << (infinity > 0) << "\n"; + std::cerr << "-inf > 0: " << ((-infinity) > 0) << "\n"; + */ +} + +void TestRay::testRay() +{ + using namespace openvdb; + typedef double RealT; + typedef math::Ray RayT; + typedef RayT::Vec3T Vec3T; + typedef math::BBox BBoxT; + + {//default constructor + RayT ray; + CPPUNIT_ASSERT(ray.eye() == Vec3T(0,0,0)); + CPPUNIT_ASSERT(ray.dir() == Vec3T(1,0,0)); + ASSERT_DOUBLES_APPROX_EQUAL( math::Delta::value(), ray.t0()); + ASSERT_DOUBLES_APPROX_EQUAL( std::numeric_limits::max(), ray.t1()); + } + + {// simple construction + + Vec3T eye(1.5,1.5,1.5), dir(1.5,1.5,1.5); dir.normalize(); + RealT t0=0.1, t1=12589.0; + + RayT ray(eye, dir, t0, t1); + CPPUNIT_ASSERT(ray.eye()==eye); + CPPUNIT_ASSERT(ray.dir()==dir); + ASSERT_DOUBLES_APPROX_EQUAL( t0, ray.t0()); + ASSERT_DOUBLES_APPROX_EQUAL( t1, ray.t1()); + } + + {// test transformation + math::Transform::Ptr xform = math::Transform::createLinearTransform(); + + xform->preRotate(M_PI, math::Y_AXIS ); + xform->postTranslate(math::Vec3d(1, 2, 3)); + xform->preScale(Vec3R(0.1, 0.2, 0.4)); + + Vec3T eye(9,1,1), dir(1,2,0); + dir.normalize(); + RealT t0=0.1, t1=12589.0; + + RayT ray0(eye, dir, t0, t1); + CPPUNIT_ASSERT( ray0.test(t0)); + CPPUNIT_ASSERT( ray0.test(t1)); + CPPUNIT_ASSERT( ray0.test(0.5*(t0+t1))); + CPPUNIT_ASSERT(!ray0.test(t0-1)); + CPPUNIT_ASSERT(!ray0.test(t1+1)); + //std::cerr << "Ray0: " << ray0 << std::endl; + RayT ray1 = ray0.applyMap( *(xform->baseMap()) ); + //std::cerr << "Ray1: " << ray1 << std::endl; + RayT ray2 = ray1.applyInverseMap( *(xform->baseMap()) ); + //std::cerr << "Ray2: " << ray2 << std::endl; + + ASSERT_DOUBLES_APPROX_EQUAL( eye[0], ray2.eye()[0]); + ASSERT_DOUBLES_APPROX_EQUAL( eye[1], ray2.eye()[1]); + ASSERT_DOUBLES_APPROX_EQUAL( eye[2], ray2.eye()[2]); + + ASSERT_DOUBLES_APPROX_EQUAL( dir[0], ray2.dir()[0]); + ASSERT_DOUBLES_APPROX_EQUAL( dir[1], ray2.dir()[1]); + ASSERT_DOUBLES_APPROX_EQUAL( dir[2], ray2.dir()[2]); + + ASSERT_DOUBLES_APPROX_EQUAL( dir[0], 1.0/ray2.invDir()[0]); + ASSERT_DOUBLES_APPROX_EQUAL( dir[1], 1.0/ray2.invDir()[1]); + ASSERT_DOUBLES_APPROX_EQUAL( dir[2], 1.0/ray2.invDir()[2]); + + ASSERT_DOUBLES_APPROX_EQUAL( t0, ray2.t0()); + ASSERT_DOUBLES_APPROX_EQUAL( t1, ray2.t1()); + } + + {// test transformation + + // This is the index to world transform + math::Transform::Ptr xform = math::Transform::createLinearTransform(); + xform->postRotate(M_PI, math::Y_AXIS ); + xform->postTranslate(math::Vec3d(1, 2, 3)); + xform->postScale(Vec3R(0.1, 0.1, 0.1));//voxel size + + // Define a ray in world space + Vec3T eye(9,1,1), dir(1,2,0); + dir.normalize(); + RealT t0=0.1, t1=12589.0; + RayT ray0(eye, dir, t0, t1); + //std::cerr << "\nWorld Ray0: " << ray0 << std::endl; + CPPUNIT_ASSERT( ray0.test(t0)); + CPPUNIT_ASSERT( ray0.test(t1)); + CPPUNIT_ASSERT( ray0.test(0.5*(t0+t1))); + CPPUNIT_ASSERT(!ray0.test(t0-1)); + CPPUNIT_ASSERT(!ray0.test(t1+1)); + Vec3T xyz0[3] = {ray0.start(), ray0.mid(), ray0.end()}; + + // Transform the ray to index space + RayT ray1 = ray0.applyInverseMap( *(xform->baseMap()) ); + //std::cerr << "\nIndex Ray1: " << ray1 << std::endl; + Vec3T xyz1[3] = {ray1.start(), ray1.mid(), ray1.end()}; + + for (int i=0; i<3; ++i) { + Vec3T pos = xform->baseMap()->applyMap(xyz1[i]); + //std::cerr << "world0 ="< DDAType; + const RayType::Vec3T eye( 0, 0, 0); + for (int s = -1; s<=1; s+=2) { + for (int a = 0; a<3; ++a) { + const int d[3]={s*(a==0), s*(a==1), s*(a==2)}; + const RayType::Vec3T dir(d[0], d[1], d[2]); + RayType ray(eye, dir); + DDAType dda(ray); + //std::cerr << "\nray: "<= h.min(j) && data[i] < h.max(j)) bin[j]++; + } + for (int i=0; i<5; ++i) CPPUNIT_ASSERT_EQUAL(bin[i],int(h.count(i))); + //h.print("test"); + } + {//Test print of Histogram + openvdb::math::Stats s; + const int N=500000; + for (int i=0; i + void operator()(const GridT::ValueOnCIter& it, StatsT& stats) const + { + typedef openvdb::math::ISGradient GradT; + if (it.isVoxelValue()) { + stats.add(GradT::result(acc, it.getCoord()).length()); + } else { + openvdb::CoordBBox bbox = it.getBoundingBox(); + openvdb::Coord xyz; + int &x = xyz[0], &y = xyz[1], &z = xyz[2]; + for (x = bbox.min()[0]; x <= bbox.max()[0]; ++x) { + for (y = bbox.min()[1]; y <= bbox.max()[1]; ++y) { + for (z = bbox.min()[2]; z <= bbox.max()[2]; ++z) { + stats.add(GradT::result(acc, xyz).length()); + } + } + } + } + } +}; + +} // unnamed namespace + +void +TestStats::testGridExtrema() +{ + using namespace openvdb; + + const int DIM = 109; + { + const float background = 0.0; + FloatGrid grid(background); + { + // Compute active value statistics for a grid with a single active voxel. + grid.tree().setValue(Coord(0), /*value=*/42.0); + math::Extrema ex = tools::extrema(grid.cbeginValueOn()); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(42.0, ex.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(42.0, ex.max(), /*tolerance=*/0.0); + + // Compute inactive value statistics for a grid with only background voxels. + grid.tree().setValueOff(Coord(0), background); + ex = tools::extrema(grid.cbeginValueOff()); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, ex.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, ex.max(), /*tolerance=*/0.0); + } + + // Compute active value statistics for a grid with two active voxel populations + // of the same size but two different values. + grid.fill(CoordBBox::createCube(Coord(0), DIM), /*value=*/1.0); + grid.fill(CoordBBox::createCube(Coord(-300), DIM), /*value=*/-3.0); + + CPPUNIT_ASSERT_EQUAL(Index64(2 * DIM * DIM * DIM), grid.activeVoxelCount()); + + for (int threaded = 0; threaded <= 1; ++threaded) { + math::Extrema ex = tools::extrema(grid.cbeginValueOn(), threaded); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(-3.0), ex.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), ex.max(), /*tolerance=*/0.0); + } + + // Compute active value statistics for just the positive values. + for (int threaded = 0; threaded <= 1; ++threaded) { + struct Local { + static void addIfPositive(const FloatGrid::ValueOnCIter& it, math::Extrema& ex) + { + const float f = *it; + if (f > 0.0) { + if (it.isVoxelValue()) ex.add(f); + else ex.add(f, it.getVoxelCount()); + } + } + }; + math::Extrema ex = + tools::extrema(grid.cbeginValueOn(), &Local::addIfPositive, threaded); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), ex.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), ex.max(), /*tolerance=*/0.0); + } + + // Compute active value statistics for the first-order gradient. + for (int threaded = 0; threaded <= 1; ++threaded) { + // First, using a custom ValueOp... + math::Extrema ex = tools::extrema(grid.cbeginValueOn(), GradOp(grid), threaded); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.0), ex.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL( + double(9.0 + 9.0 + 9.0), ex.max() * ex.max(), /*tol=*/1.0e-3); + // max gradient is (dx, dy, dz) = (-3 - 0, -3 - 0, -3 - 0) + + // ...then using tools::opStatistics(). + typedef math::ISOpMagnitude > MathOp; + ex = tools::opExtrema(grid.cbeginValueOn(), MathOp(), threaded); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.0), ex.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL( + double(9.0 + 9.0 + 9.0), ex.max() * ex.max(), /*tolerance=*/1.0e-3); + // max gradient is (dx, dy, dz) = (-3 - 0, -3 - 0, -3 - 0) + } + } + { + const Vec3s background(0.0); + Vec3SGrid grid(background); + + // Compute active vector magnitude statistics for a vector-valued grid + // with two active voxel populations of the same size but two different values. + grid.fill(CoordBBox::createCube(Coord(0), DIM), Vec3s(3.0, 0.0, 4.0)); // length = 5 + grid.fill(CoordBBox::createCube(Coord(-300), DIM), Vec3s(1.0, 2.0, 2.0)); // length = 3 + + CPPUNIT_ASSERT_EQUAL(Index64(2 * DIM * DIM * DIM), grid.activeVoxelCount()); + + for (int threaded = 0; threaded <= 1; ++threaded) { + math::Extrema ex = tools::extrema(grid.cbeginValueOn(), threaded); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(3.0), ex.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(5.0), ex.max(), /*tolerance=*/0.0); + } + } +} + +void +TestStats::testGridStats() +{ + using namespace openvdb; + + const int DIM = 109; + { + const float background = 0.0; + FloatGrid grid(background); + { + // Compute active value statistics for a grid with a single active voxel. + grid.tree().setValue(Coord(0), /*value=*/42.0); + math::Stats stats = tools::statistics(grid.cbeginValueOn()); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(42.0, stats.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(42.0, stats.max(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(42.0, stats.mean(), /*tolerance=*/1.0e-8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, stats.variance(), /*tolerance=*/1.0e-8); + + // Compute inactive value statistics for a grid with only background voxels. + grid.tree().setValueOff(Coord(0), background); + stats = tools::statistics(grid.cbeginValueOff()); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, stats.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, stats.max(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, stats.mean(), /*tolerance=*/1.0e-8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, stats.variance(), /*tolerance=*/1.0e-8); + } + + // Compute active value statistics for a grid with two active voxel populations + // of the same size but two different values. + grid.fill(CoordBBox::createCube(Coord(0), DIM), /*value=*/1.0); + grid.fill(CoordBBox::createCube(Coord(-300), DIM), /*value=*/-3.0); + + CPPUNIT_ASSERT_EQUAL(Index64(2 * DIM * DIM * DIM), grid.activeVoxelCount()); + + for (int threaded = 0; threaded <= 1; ++threaded) { + math::Stats stats = tools::statistics(grid.cbeginValueOn(), threaded); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(-3.0), stats.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), stats.max(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(-1.0), stats.mean(), /*tolerance=*/1.0e-8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(4.0), stats.variance(), /*tolerance=*/1.0e-8); + } + + // Compute active value statistics for just the positive values. + for (int threaded = 0; threaded <= 1; ++threaded) { + struct Local { + static void addIfPositive(const FloatGrid::ValueOnCIter& it, math::Stats& stats) + { + const float f = *it; + if (f > 0.0) { + if (it.isVoxelValue()) stats.add(f); + else stats.add(f, it.getVoxelCount()); + } + } + }; + math::Stats stats = + tools::statistics(grid.cbeginValueOn(), &Local::addIfPositive, threaded); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), stats.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), stats.max(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), stats.mean(), /*tolerance=*/1.0e-8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.0), stats.variance(), /*tolerance=*/1.0e-8); + } + + // Compute active value statistics for the first-order gradient. + for (int threaded = 0; threaded <= 1; ++threaded) { + // First, using a custom ValueOp... + math::Stats stats = tools::statistics(grid.cbeginValueOn(), GradOp(grid), threaded); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.0), stats.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL( + double(9.0 + 9.0 + 9.0), stats.max() * stats.max(), /*tol=*/1.0e-3); + // max gradient is (dx, dy, dz) = (-3 - 0, -3 - 0, -3 - 0) + + // ...then using tools::opStatistics(). + typedef math::ISOpMagnitude > MathOp; + stats = tools::opStatistics(grid.cbeginValueOn(), MathOp(), threaded); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0.0), stats.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL( + double(9.0 + 9.0 + 9.0), stats.max() * stats.max(), /*tolerance=*/1.0e-3); + // max gradient is (dx, dy, dz) = (-3 - 0, -3 - 0, -3 - 0) + } + } + { + const Vec3s background(0.0); + Vec3SGrid grid(background); + + // Compute active vector magnitude statistics for a vector-valued grid + // with two active voxel populations of the same size but two different values. + grid.fill(CoordBBox::createCube(Coord(0), DIM), Vec3s(3.0, 0.0, 4.0)); // length = 5 + grid.fill(CoordBBox::createCube(Coord(-300), DIM), Vec3s(1.0, 2.0, 2.0)); // length = 3 + + CPPUNIT_ASSERT_EQUAL(Index64(2 * DIM * DIM * DIM), grid.activeVoxelCount()); + + for (int threaded = 0; threaded <= 1; ++threaded) { + math::Stats stats = tools::statistics(grid.cbeginValueOn(), threaded); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(3.0), stats.min(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(5.0), stats.max(), /*tolerance=*/0.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(4.0), stats.mean(), /*tolerance=*/1.0e-8); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(1.0), stats.variance(), /*tolerance=*/1.0e-8); + } + } +} + + +namespace { + +template +inline void +doTestGridOperatorStats(const GridT& grid, const OpT& op) +{ + openvdb::math::Stats serialStats = + openvdb::tools::opStatistics(grid.cbeginValueOn(), op, /*threaded=*/false); + + openvdb::math::Stats parallelStats = + openvdb::tools::opStatistics(grid.cbeginValueOn(), op, /*threaded=*/true); + + // Verify that the results from threaded and serial runs are equivalent. + CPPUNIT_ASSERT_EQUAL(serialStats.size(), parallelStats.size()); + ASSERT_DOUBLES_EXACTLY_EQUAL(serialStats.min(), parallelStats.min()); + ASSERT_DOUBLES_EXACTLY_EQUAL(serialStats.max(), parallelStats.max()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(serialStats.mean(), parallelStats.mean(), /*tolerance=*/1.0e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(serialStats.variance(), parallelStats.variance(), 1.0e-6); +} + +} + +void +TestStats::testGridOperatorStats() +{ + using namespace openvdb; + + typedef math::UniformScaleMap MapType; + MapType map; + + const int DIM = 109; + { + // Test operations on a scalar grid. + const float background = 0.0; + FloatGrid grid(background); + grid.fill(CoordBBox::createCube(Coord(0), DIM), /*value=*/1.0); + grid.fill(CoordBBox::createCube(Coord(-300), DIM), /*value=*/-3.0); + + { // Magnitude of gradient computed via first-order differencing + typedef math::MapAdapter, MapType>, double> OpT; + doTestGridOperatorStats(grid, OpT(map)); + } + { // Magnitude of index-space gradient computed via first-order differencing + typedef math::ISOpMagnitude > OpT; + doTestGridOperatorStats(grid, OpT()); + } + { // Laplacian of index-space gradient computed via second-order central differencing + typedef math::ISLaplacian OpT; + doTestGridOperatorStats(grid, OpT()); + } + } + { + // Test operations on a vector grid. + const Vec3s background(0.0); + Vec3SGrid grid(background); + grid.fill(CoordBBox::createCube(Coord(0), DIM), Vec3s(3.0, 0.0, 4.0)); // length = 5 + grid.fill(CoordBBox::createCube(Coord(-300), DIM), Vec3s(1.0, 2.0, 2.0)); // length = 3 + + { // Divergence computed via first-order differencing + typedef math::MapAdapter, double> OpT; + doTestGridOperatorStats(grid, OpT(map)); + } + { // Magnitude of curl computed via first-order differencing + typedef math::MapAdapter, MapType>, double> OpT; + doTestGridOperatorStats(grid, OpT(map)); + } + { // Magnitude of index-space curl computed via first-order differencing + typedef math::ISOpMagnitude > OpT; + doTestGridOperatorStats(grid, OpT()); + } + } +} + + +void +TestStats::testGridHistogram() +{ + using namespace openvdb; + + const int DIM = 109; + { + const float background = 0.0; + FloatGrid grid(background); + { + const double value = 42.0; + + // Compute a histogram of the active values of a grid with a single active voxel. + grid.tree().setValue(Coord(0), value); + math::Histogram hist = tools::histogram(grid.cbeginValueOn(), + /*min=*/0.0, /*max=*/100.0); + + for (int i = 0, N = int(hist.numBins()); i < N; ++i) { + uint64_t expected = ((hist.min(i) <= value && value <= hist.max(i)) ? 1 : 0); + CPPUNIT_ASSERT_EQUAL(expected, hist.count(i)); + } + } + + // Compute a histogram of the active values of a grid with two + // active voxel populations of the same size but two different values. + grid.fill(CoordBBox::createCube(Coord(0), DIM), /*value=*/1.0); + grid.fill(CoordBBox::createCube(Coord(-300), DIM), /*value=*/3.0); + + CPPUNIT_ASSERT_EQUAL(uint64_t(2 * DIM * DIM * DIM), grid.activeVoxelCount()); + + for (int threaded = 0; threaded <= 1; ++threaded) { + math::Histogram hist = tools::histogram(grid.cbeginValueOn(), + /*min=*/0.0, /*max=*/10.0, /*numBins=*/9, threaded); + + CPPUNIT_ASSERT_EQUAL(Index64(2 * DIM * DIM * DIM), hist.size()); + for (int i = 0, N = int(hist.numBins()); i < N; ++i) { + if (i == 0 || i == 2) { + CPPUNIT_ASSERT_EQUAL(uint64_t(DIM * DIM * DIM), hist.count(i)); + } else { + CPPUNIT_ASSERT_EQUAL(uint64_t(0), hist.count(i)); + } + } + } + } + { + const Vec3s background(0.0); + Vec3SGrid grid(background); + + // Compute a histogram of vector magnitudes of the active values of a + // vector-valued grid with two active voxel populations of the same size + // but two different values. + grid.fill(CoordBBox::createCube(Coord(0), DIM), Vec3s(3.0, 0.0, 4.0)); // length = 5 + grid.fill(CoordBBox::createCube(Coord(-300), DIM), Vec3s(1.0, 2.0, 2.0)); // length = 3 + + CPPUNIT_ASSERT_EQUAL(Index64(2 * DIM * DIM * DIM), grid.activeVoxelCount()); + + for (int threaded = 0; threaded <= 1; ++threaded) { + math::Histogram hist = tools::histogram(grid.cbeginValueOn(), + /*min=*/0.0, /*max=*/10.0, /*numBins=*/9, threaded); + + CPPUNIT_ASSERT_EQUAL(Index64(2 * DIM * DIM * DIM), hist.size()); + for (int i = 0, N = int(hist.numBins()); i < N; ++i) { + if (i == 2 || i == 4) { + CPPUNIT_ASSERT_EQUAL(uint64_t(DIM * DIM * DIM), hist.count(i)); + } else { + CPPUNIT_ASSERT_EQUAL(uint64_t(0), hist.count(i)); + } + } + } + } +} diff --git a/openvdb/unittest/TestStream.cc b/openvdb/unittest/TestStream.cc new file mode 100644 index 00000000..0efc5c66 --- /dev/null +++ b/openvdb/unittest/TestStream.cc @@ -0,0 +1,248 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include // for remove() +#include + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(a, b) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((a), (b), /*tolerance=*/0.0); + + +class TestStream: public CppUnit::TestCase +{ +public: + void setUp() override; + void tearDown() override; + + CPPUNIT_TEST_SUITE(TestStream); + CPPUNIT_TEST(testWrite); + CPPUNIT_TEST(testRead); + CPPUNIT_TEST(testFileReadFromStream); + CPPUNIT_TEST_SUITE_END(); + + void testWrite(); + void testRead(); + void testFileReadFromStream(); + +private: + static openvdb::GridPtrVecPtr createTestGrids(openvdb::MetaMap::Ptr&); + static void verifyTestGrids(openvdb::GridPtrVecPtr, openvdb::MetaMap::Ptr); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestStream); + + +//////////////////////////////////////// + + +void +TestStream::setUp() +{ + openvdb::uninitialize(); + + openvdb::Int32Grid::registerGrid(); + openvdb::FloatGrid::registerGrid(); + + openvdb::StringMetadata::registerType(); + openvdb::Int32Metadata::registerType(); + openvdb::Int64Metadata::registerType(); + openvdb::Vec3IMetadata::registerType(); + openvdb::io::DelayedLoadMetadata::registerType(); + + // Register maps + openvdb::math::MapRegistry::clear(); + openvdb::math::AffineMap::registerMap(); + openvdb::math::ScaleMap::registerMap(); + openvdb::math::UniformScaleMap::registerMap(); + openvdb::math::TranslationMap::registerMap(); + openvdb::math::ScaleTranslateMap::registerMap(); + openvdb::math::UniformScaleTranslateMap::registerMap(); + openvdb::math::NonlinearFrustumMap::registerMap(); +} + + +void +TestStream::tearDown() +{ + openvdb::uninitialize(); +} + + +//////////////////////////////////////// + + +openvdb::GridPtrVecPtr +TestStream::createTestGrids(openvdb::MetaMap::Ptr& metadata) +{ + using namespace openvdb; + + // Create trees + Int32Tree::Ptr tree1(new Int32Tree(1)); + FloatTree::Ptr tree2(new FloatTree(2.0)); + + // Set some values + tree1->setValue(Coord(0, 0, 0), 5); + tree1->setValue(Coord(100, 0, 0), 6); + tree2->setValue(Coord(0, 0, 0), 10); + tree2->setValue(Coord(0, 100, 0), 11); + + // Create grids + GridBase::Ptr + grid1 = createGrid(tree1), + grid2 = createGrid(tree1), // instance of grid1 + grid3 = createGrid(tree2); + grid1->setName("density"); + grid2->setName("density_copy"); + grid3->setName("temperature"); + + // Create transforms + math::Transform::Ptr trans1 = math::Transform::createLinearTransform(0.1); + math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1); + grid1->setTransform(trans1); + grid2->setTransform(trans2); + grid3->setTransform(trans2); + + metadata.reset(new MetaMap); + metadata->insertMeta("author", StringMetadata("Einstein")); + metadata->insertMeta("year", Int32Metadata(2009)); + + GridPtrVecPtr grids(new GridPtrVec); + grids->push_back(grid1); + grids->push_back(grid2); + grids->push_back(grid3); + + return grids; +} + + +void +TestStream::verifyTestGrids(openvdb::GridPtrVecPtr grids, openvdb::MetaMap::Ptr meta) +{ + using namespace openvdb; + + CPPUNIT_ASSERT(grids.get() != nullptr); + CPPUNIT_ASSERT(meta.get() != nullptr); + + // Verify the metadata. + CPPUNIT_ASSERT_EQUAL(2, int(meta->metaCount())); + CPPUNIT_ASSERT_EQUAL(std::string("Einstein"), meta->metaValue("author")); + CPPUNIT_ASSERT_EQUAL(2009, meta->metaValue("year")); + + // Verify the grids. + CPPUNIT_ASSERT_EQUAL(3, int(grids->size())); + + GridBase::Ptr grid = findGridByName(*grids, "density"); + CPPUNIT_ASSERT(grid.get() != nullptr); + Int32Tree::Ptr density = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(density.get() != nullptr); + + grid.reset(); + grid = findGridByName(*grids, "density_copy"); + CPPUNIT_ASSERT(grid.get() != nullptr); + CPPUNIT_ASSERT(gridPtrCast(grid)->treePtr().get() != nullptr); + // Verify that "density_copy" is an instance of (i.e., shares a tree with) "density". + CPPUNIT_ASSERT_EQUAL(density, gridPtrCast(grid)->treePtr()); + + grid.reset(); + grid = findGridByName(*grids, "temperature"); + CPPUNIT_ASSERT(grid.get() != nullptr); + FloatTree::Ptr temperature = gridPtrCast(grid)->treePtr(); + CPPUNIT_ASSERT(temperature.get() != nullptr); + + ASSERT_DOUBLES_EXACTLY_EQUAL(5, density->getValue(Coord(0, 0, 0))); + ASSERT_DOUBLES_EXACTLY_EQUAL(6, density->getValue(Coord(100, 0, 0))); + ASSERT_DOUBLES_EXACTLY_EQUAL(10, temperature->getValue(Coord(0, 0, 0))); + ASSERT_DOUBLES_EXACTLY_EQUAL(11, temperature->getValue(Coord(0, 100, 0))); +} + + +//////////////////////////////////////// + + +void +TestStream::testWrite() +{ + using namespace openvdb; + + // Create test grids and stream them to a string. + MetaMap::Ptr meta; + GridPtrVecPtr grids = createTestGrids(meta); + std::ostringstream ostr(std::ios_base::binary); + io::Stream(ostr).write(*grids, *meta); + //std::ofstream file("debug.vdb2", std::ios_base::binary); + //file << ostr.str(); + + // Stream the grids back in. + std::istringstream is(ostr.str(), std::ios_base::binary); + io::Stream strm(is); + meta = strm.getMetadata(); + grids = strm.getGrids(); + + verifyTestGrids(grids, meta); +} + + +void +TestStream::testRead() +{ + using namespace openvdb; + + // Create test grids and write them to a file. + MetaMap::Ptr meta; + GridPtrVecPtr grids = createTestGrids(meta); + const char* filename = "something.vdb2"; + io::File(filename).write(*grids, *meta); + SharedPtr scopedFile(filename, ::remove); + + // Stream the grids back in. + std::ifstream is(filename, std::ios_base::binary); + io::Stream strm(is); + meta = strm.getMetadata(); + grids = strm.getGrids(); + + verifyTestGrids(grids, meta); +} + + +/// Stream grids to a file using io::Stream, then read the file back using io::File. +void +TestStream::testFileReadFromStream() +{ + using namespace openvdb; + + MetaMap::Ptr meta; + GridPtrVecPtr grids; + + // Create test grids and stream them to a file (and then close the file). + const char* filename = "something.vdb2"; + SharedPtr scopedFile(filename, ::remove); + { + std::ofstream os(filename, std::ios_base::binary); + grids = createTestGrids(meta); + io::Stream(os).write(*grids, *meta); + } + + // Read the grids back in. + io::File file(filename); + CPPUNIT_ASSERT(file.inputHasGridOffsets()); + CPPUNIT_ASSERT_THROW(file.getGrids(), IoError); + + file.open(); + meta = file.getMetadata(); + grids = file.getGrids(); + + CPPUNIT_ASSERT(!file.inputHasGridOffsets()); + CPPUNIT_ASSERT(meta.get() != nullptr); + CPPUNIT_ASSERT(grids.get() != nullptr); + CPPUNIT_ASSERT(!grids->empty()); + + verifyTestGrids(grids, meta); +} diff --git a/openvdb/unittest/TestStreamCompression.cc b/openvdb/unittest/TestStreamCompression.cc new file mode 100644 index 00000000..470a92c2 --- /dev/null +++ b/openvdb/unittest/TestStreamCompression.cc @@ -0,0 +1,660 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + +#include // io::COMPRESS_BLOSC + +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-macros" +#endif +// Boost.Interprocess uses a header-only portion of Boost.DateTime +#define BOOST_DATE_TIME_NO_LIB +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif +#include +#include +#include +#include +#include +#include +#include +#include // for BOOST_VERSION + +#include + +#ifdef _MSC_VER +#include // open_existing_file(), close_file() +// boost::interprocess::detail was renamed to boost::interprocess::ipcdetail in Boost 1.48. +// Ensure that both namespaces exist. +namespace boost { namespace interprocess { namespace detail {} namespace ipcdetail {} } } +#include +#else +#include // for struct stat +#include // for stat() +#include // for unlink() +#endif + +#include +#include // for std::iota() + +#ifdef OPENVDB_USE_BLOSC +#include +// A Blosc optimization introduced in 1.11.0 uses a slightly smaller block size for +// HCR codecs (LZ4, ZLIB, ZSTD), which otherwise fails a few regression test cases +#if BLOSC_VERSION_MAJOR > 1 || (BLOSC_VERSION_MAJOR == 1 && BLOSC_VERSION_MINOR > 10) +#define BLOSC_HCR_BLOCKSIZE_OPTIMIZATION +#endif +// Blosc 1.14+ writes backwards-compatible data by default. +// http://blosc.org/posts/new-forward-compat-policy/ +#if BLOSC_VERSION_MAJOR > 1 || (BLOSC_VERSION_MAJOR == 1 && BLOSC_VERSION_MINOR >= 14) +#define BLOSC_BACKWARDS_COMPATIBLE +#endif +#endif + +/// @brief io::MappedFile has a private constructor, so this unit tests uses a matching proxy +class ProxyMappedFile +{ +public: + explicit ProxyMappedFile(const std::string& filename) + : mImpl(new Impl(filename)) { } + +private: + class Impl + { + public: + Impl(const std::string& filename) + : mMap(filename.c_str(), boost::interprocess::read_only) + , mRegion(mMap, boost::interprocess::read_only) + { + mLastWriteTime = 0; + const char* regionFilename = mMap.get_name(); +#ifdef _MSC_VER + using namespace boost::interprocess::detail; + using namespace boost::interprocess::ipcdetail; + using openvdb::Index64; + + if (void* fh = open_existing_file(regionFilename, boost::interprocess::read_only)) { + FILETIME mtime; + if (GetFileTime(fh, nullptr, nullptr, &mtime)) { + mLastWriteTime = (Index64(mtime.dwHighDateTime) << 32) | mtime.dwLowDateTime; + } + close_file(fh); + } +#else + struct stat info; + if (0 == ::stat(regionFilename, &info)) { + mLastWriteTime = openvdb::Index64(info.st_mtime); + } +#endif + } + + using Notifier = std::function; + boost::interprocess::file_mapping mMap; + boost::interprocess::mapped_region mRegion; + bool mAutoDelete = false; + Notifier mNotifier; + mutable tbb::atomic mLastWriteTime; + }; // class Impl + std::unique_ptr mImpl; +}; // class ProxyMappedFile + +using namespace openvdb; +using namespace openvdb::compression; + +class TestStreamCompression: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestStreamCompression); + CPPUNIT_TEST(testBlosc); + CPPUNIT_TEST(testPagedStreams); + + CPPUNIT_TEST_SUITE_END(); + + void testBlosc(); + void testPagedStreams(); +}; // class TestStreamCompression + +CPPUNIT_TEST_SUITE_REGISTRATION(TestStreamCompression); + + +//////////////////////////////////////// + + +void +TestStreamCompression::testBlosc() +{ + // ensure that the library and unit tests are both built with or without Blosc enabled +#ifdef OPENVDB_USE_BLOSC + CPPUNIT_ASSERT(bloscCanCompress()); +#else + CPPUNIT_ASSERT(!bloscCanCompress()); +#endif + + const int count = 256; + + { // valid buffer + // compress + + std::unique_ptr uncompressedBuffer(new int[count]); + + for (int i = 0; i < count; i++) { + uncompressedBuffer.get()[i] = i / 2; + } + + size_t uncompressedBytes = count * sizeof(int); + size_t compressedBytes; + + size_t testCompressedBytes = bloscCompressedSize( + reinterpret_cast(uncompressedBuffer.get()), uncompressedBytes); + + std::unique_ptr compressedBuffer = bloscCompress( + reinterpret_cast(uncompressedBuffer.get()), uncompressedBytes, compressedBytes); + +#ifdef OPENVDB_USE_BLOSC + CPPUNIT_ASSERT(compressedBytes < uncompressedBytes); + CPPUNIT_ASSERT(compressedBuffer); + CPPUNIT_ASSERT_EQUAL(testCompressedBytes, compressedBytes); + + // uncompressedSize + + CPPUNIT_ASSERT_EQUAL(uncompressedBytes, bloscUncompressedSize(compressedBuffer.get())); + + // decompress + + std::unique_ptr newUncompressedBuffer = + bloscDecompress(compressedBuffer.get(), uncompressedBytes); + + // incorrect number of expected bytes + CPPUNIT_ASSERT_THROW(newUncompressedBuffer = + bloscDecompress(compressedBuffer.get(), 1), openvdb::RuntimeError); + + CPPUNIT_ASSERT(newUncompressedBuffer); +#else + CPPUNIT_ASSERT(!compressedBuffer); + CPPUNIT_ASSERT_EQUAL(testCompressedBytes, size_t(0)); + + // uncompressedSize + + CPPUNIT_ASSERT_THROW(bloscUncompressedSize(compressedBuffer.get()), openvdb::RuntimeError); + + // decompress + + std::unique_ptr newUncompressedBuffer; + CPPUNIT_ASSERT_THROW( + newUncompressedBuffer = bloscDecompress(compressedBuffer.get(), uncompressedBytes), + openvdb::RuntimeError); + + CPPUNIT_ASSERT(!newUncompressedBuffer); +#endif + } + + { // one value (below minimum bytes) + std::unique_ptr uncompressedBuffer(new int[1]); + uncompressedBuffer.get()[0] = 10; + + size_t compressedBytes; + + std::unique_ptr compressedBuffer = bloscCompress( + reinterpret_cast(uncompressedBuffer.get()), sizeof(int), compressedBytes); + + CPPUNIT_ASSERT(!compressedBuffer); + CPPUNIT_ASSERT_EQUAL(compressedBytes, size_t(0)); + } + + { // padded buffer + std::unique_ptr largeBuffer(new char[2048]); + + for (int paddedCount = 1; paddedCount < 256; paddedCount++) { + + std::unique_ptr newTest(new char[paddedCount]); + for (int i = 0; i < paddedCount; i++) newTest.get()[i] = char(0); + +#ifdef OPENVDB_USE_BLOSC + size_t compressedBytes; + std::unique_ptr compressedBuffer = bloscCompress( + newTest.get(), paddedCount, compressedBytes); + + // compress into a large buffer to check for any padding issues + size_t compressedSizeBytes; + bloscCompress(largeBuffer.get(), compressedSizeBytes, size_t(2048), + newTest.get(), paddedCount); + + // regardless of compression, these numbers should always match + CPPUNIT_ASSERT_EQUAL(compressedSizeBytes, compressedBytes); + + // no compression performed due to buffer being too small + if (paddedCount <= BLOSC_MINIMUM_BYTES) { + CPPUNIT_ASSERT(!compressedBuffer); + } + else { + CPPUNIT_ASSERT(compressedBuffer); + CPPUNIT_ASSERT(compressedBytes > 0); + CPPUNIT_ASSERT(int(compressedBytes) < paddedCount); + + std::unique_ptr uncompressedBuffer = bloscDecompress( + compressedBuffer.get(), paddedCount); + + CPPUNIT_ASSERT(uncompressedBuffer); + + for (int i = 0; i < paddedCount; i++) { + CPPUNIT_ASSERT_EQUAL((uncompressedBuffer.get())[i], newTest[i]); + } + } +#endif + } + } + + { // invalid buffer (out of range) + + // compress + + std::vector smallBuffer; + smallBuffer.reserve(count); + + for (int i = 0; i < count; i++) smallBuffer[i] = i; + + size_t invalidBytes = INT_MAX - 1; + + size_t testCompressedBytes = bloscCompressedSize( + reinterpret_cast(&smallBuffer[0]), invalidBytes); + + CPPUNIT_ASSERT_EQUAL(testCompressedBytes, size_t(0)); + + std::unique_ptr buffer = bloscCompress( + reinterpret_cast(&smallBuffer[0]), invalidBytes, testCompressedBytes); + + CPPUNIT_ASSERT(!buffer); + CPPUNIT_ASSERT_EQUAL(testCompressedBytes, size_t(0)); + + // decompress + +#ifdef OPENVDB_USE_BLOSC + std::unique_ptr compressedBuffer = bloscCompress( + reinterpret_cast(&smallBuffer[0]), count * sizeof(int), testCompressedBytes); + + CPPUNIT_ASSERT_THROW(buffer = bloscDecompress( + reinterpret_cast(compressedBuffer.get()), invalidBytes - 16), + openvdb::RuntimeError); + + CPPUNIT_ASSERT(!buffer); + + CPPUNIT_ASSERT_THROW(bloscDecompress( + reinterpret_cast(compressedBuffer.get()), count * sizeof(int) + 1), + openvdb::RuntimeError); +#endif + } + + { // uncompressible buffer + const int uncompressedCount = 32; + + std::vector values; + values.reserve(uncompressedCount); // 128 bytes + + for (int i = 0; i < uncompressedCount; i++) values.push_back(i*10000); + + std::random_shuffle(values.begin(), values.end()); + + std::unique_ptr uncompressedBuffer(new int[values.size()]); + + for (size_t i = 0; i < values.size(); i++) uncompressedBuffer.get()[i] = values[i]; + + size_t uncompressedBytes = values.size() * sizeof(int); + size_t compressedBytes; + + std::unique_ptr compressedBuffer = bloscCompress( + reinterpret_cast(uncompressedBuffer.get()), uncompressedBytes, compressedBytes); + + CPPUNIT_ASSERT(!compressedBuffer); + CPPUNIT_ASSERT_EQUAL(compressedBytes, size_t(0)); + } +} + + +void +TestStreamCompression::testPagedStreams() +{ + { // one small value + std::ostringstream ostr(std::ios_base::binary); + PagedOutputStream ostream(ostr); + + int foo = 5; + ostream.write(reinterpret_cast(&foo), sizeof(int)); + CPPUNIT_ASSERT_EQUAL(ostr.tellp(), std::streampos(0)); + + ostream.flush(); + CPPUNIT_ASSERT_EQUAL(ostr.tellp(), std::streampos(sizeof(int))); + } + + { // small values up to page threshold + std::ostringstream ostr(std::ios_base::binary); + PagedOutputStream ostream(ostr); + + for (int i = 0; i < PageSize; i++) { + uint8_t oneByte = 255; + ostream.write(reinterpret_cast(&oneByte), sizeof(uint8_t)); + } + CPPUNIT_ASSERT_EQUAL(ostr.tellp(), std::streampos(0)); + + std::vector values; + values.assign(PageSize, uint8_t(255)); + size_t compressedSize = compression::bloscCompressedSize( + reinterpret_cast(&values[0]), PageSize); + + uint8_t oneMoreByte(255); + ostream.write(reinterpret_cast(&oneMoreByte), sizeof(char)); + + if (compressedSize == 0) { + CPPUNIT_ASSERT_EQUAL(ostr.tellp(), std::streampos(PageSize)); + } + else { + CPPUNIT_ASSERT_EQUAL(ostr.tellp(), std::streampos(compressedSize)); + } + } + + { // one large block at exactly page threshold + std::ostringstream ostr(std::ios_base::binary); + PagedOutputStream ostream(ostr); + + std::vector values; + values.assign(PageSize, uint8_t(255)); + ostream.write(reinterpret_cast(&values[0]), values.size()); + + CPPUNIT_ASSERT_EQUAL(ostr.tellp(), std::streampos(0)); + } + + { // two large blocks at page threshold + 1 byte + std::ostringstream ostr(std::ios_base::binary); + PagedOutputStream ostream(ostr); + + std::vector values; + values.assign(PageSize + 1, uint8_t(255)); + ostream.write(reinterpret_cast(&values[0]), values.size()); + + size_t compressedSize = compression::bloscCompressedSize( + reinterpret_cast(&values[0]), values.size()); + +#ifndef OPENVDB_USE_BLOSC + compressedSize = values.size(); +#endif + + CPPUNIT_ASSERT_EQUAL(ostr.tellp(), std::streampos(compressedSize)); + + ostream.write(reinterpret_cast(&values[0]), values.size()); + + CPPUNIT_ASSERT_EQUAL(ostr.tellp(), std::streampos(compressedSize * 2)); + + uint8_t oneMoreByte(255); + ostream.write(reinterpret_cast(&oneMoreByte), sizeof(uint8_t)); + + ostream.flush(); + + CPPUNIT_ASSERT_EQUAL(ostr.tellp(), std::streampos(compressedSize * 2 + 1)); + } + + { // one full page + std::stringstream ss(std::ios_base::out | std::ios_base::in | std::ios_base::binary); + + // write + + PagedOutputStream ostreamSizeOnly(ss); + ostreamSizeOnly.setSizeOnly(true); + + CPPUNIT_ASSERT_EQUAL(ss.tellp(), std::streampos(0)); + + std::vector values; + values.resize(PageSize); + std::iota(values.begin(), values.end(), 0); // ascending integer values + ostreamSizeOnly.write(reinterpret_cast(&values[0]), values.size()); + ostreamSizeOnly.flush(); + +#ifdef OPENVDB_USE_BLOSC + // two integers - compressed size and uncompressed size + CPPUNIT_ASSERT_EQUAL(ss.tellp(), std::streampos(sizeof(int)*2)); +#else + // one integer - uncompressed size + CPPUNIT_ASSERT_EQUAL(ss.tellp(), std::streampos(sizeof(int))); +#endif + + PagedOutputStream ostream(ss); + ostream.write(reinterpret_cast(&values[0]), values.size()); + ostream.flush(); + +#ifdef OPENVDB_USE_BLOSC +#ifdef BLOSC_BACKWARDS_COMPATIBLE + CPPUNIT_ASSERT_EQUAL(ss.tellp(), std::streampos(5400)); +#else +#ifdef BLOSC_HCR_BLOCKSIZE_OPTIMIZATION + CPPUNIT_ASSERT_EQUAL(ss.tellp(), std::streampos(4422)); +#else + CPPUNIT_ASSERT_EQUAL(ss.tellp(), std::streampos(4452)); +#endif +#endif +#else + CPPUNIT_ASSERT_EQUAL(ss.tellp(), std::streampos(PageSize+sizeof(int))); +#endif + + // read + + CPPUNIT_ASSERT_EQUAL(ss.tellg(), std::streampos(0)); + + PagedInputStream istream(ss); + istream.setSizeOnly(true); + + PageHandle::Ptr handle = istream.createHandle(values.size()); + +#ifdef OPENVDB_USE_BLOSC + // two integers - compressed size and uncompressed size + CPPUNIT_ASSERT_EQUAL(ss.tellg(), std::streampos(sizeof(int)*2)); +#else + // one integer - uncompressed size + CPPUNIT_ASSERT_EQUAL(ss.tellg(), std::streampos(sizeof(int))); +#endif + + istream.read(handle, values.size(), false); + +#ifdef OPENVDB_USE_BLOSC +#ifdef BLOSC_BACKWARDS_COMPATIBLE + CPPUNIT_ASSERT_EQUAL(ss.tellg(), std::streampos(5400)); +#else +#ifdef BLOSC_HCR_BLOCKSIZE_OPTIMIZATION + CPPUNIT_ASSERT_EQUAL(ss.tellg(), std::streampos(4422)); +#else + CPPUNIT_ASSERT_EQUAL(ss.tellg(), std::streampos(4452)); +#endif +#endif +#else + CPPUNIT_ASSERT_EQUAL(ss.tellg(), std::streampos(PageSize+sizeof(int))); +#endif + + std::unique_ptr newValues(reinterpret_cast(handle->read().release())); + + CPPUNIT_ASSERT(newValues); + + for (size_t i = 0; i < values.size(); i++) { + CPPUNIT_ASSERT_EQUAL(values[i], newValues.get()[i]); + } + } + + std::string tempDir; + if (const char* dir = std::getenv("TMPDIR")) tempDir = dir; +#ifdef _MSC_VER + if (tempDir.empty()) { + char tempDirBuffer[MAX_PATH+1]; + int tempDirLen = GetTempPath(MAX_PATH+1, tempDirBuffer); + CPPUNIT_ASSERT(tempDirLen > 0 && tempDirLen <= MAX_PATH); + tempDir = tempDirBuffer; + } +#else + if (tempDir.empty()) tempDir = P_tmpdir; +#endif + + { + std::string filename = tempDir + "/openvdb_page1"; + io::StreamMetadata::Ptr streamMetadata(new io::StreamMetadata); + + { // ascending values up to 10 million written in blocks of PageSize/3 + std::ofstream fileout(filename.c_str(), std::ios_base::binary); + + io::setStreamMetadataPtr(fileout, streamMetadata); + io::setDataCompression(fileout, openvdb::io::COMPRESS_BLOSC); + + std::vector values; + values.resize(10*1000*1000); + std::iota(values.begin(), values.end(), 0); // ascending integer values + + // write page sizes + + PagedOutputStream ostreamSizeOnly(fileout); + ostreamSizeOnly.setSizeOnly(true); + + CPPUNIT_ASSERT_EQUAL(fileout.tellp(), std::streampos(0)); + + int increment = PageSize/3; + + for (size_t i = 0; i < values.size(); i += increment) { + if (size_t(i+increment) > values.size()) { + ostreamSizeOnly.write( + reinterpret_cast(&values[0]+i), values.size() - i); + } + else { + ostreamSizeOnly.write(reinterpret_cast(&values[0]+i), increment); + } + } + ostreamSizeOnly.flush(); + +#ifdef OPENVDB_USE_BLOSC + int pages = static_cast(fileout.tellp() / (sizeof(int)*2)); +#else + int pages = static_cast(fileout.tellp() / (sizeof(int))); +#endif + + CPPUNIT_ASSERT_EQUAL(pages, 10); + + // write + + PagedOutputStream ostream(fileout); + + for (size_t i = 0; i < values.size(); i += increment) { + if (size_t(i+increment) > values.size()) { + ostream.write(reinterpret_cast(&values[0]+i), values.size() - i); + } + else { + ostream.write(reinterpret_cast(&values[0]+i), increment); + } + } + + ostream.flush(); + +#ifdef OPENVDB_USE_BLOSC +#ifdef BLOSC_BACKWARDS_COMPATIBLE + CPPUNIT_ASSERT_EQUAL(fileout.tellp(), std::streampos(51480)); +#else +#ifdef BLOSC_HCR_BLOCKSIZE_OPTIMIZATION + CPPUNIT_ASSERT_EQUAL(fileout.tellp(), std::streampos(42424)); +#else + CPPUNIT_ASSERT_EQUAL(fileout.tellp(), std::streampos(42724)); +#endif +#endif +#else + CPPUNIT_ASSERT_EQUAL(fileout.tellp(), std::streampos(values.size()+sizeof(int)*pages)); +#endif + + // abuse File being a friend of MappedFile to get around the private constructor + ProxyMappedFile* proxy = new ProxyMappedFile(filename); + SharedPtr mappedFile(reinterpret_cast(proxy)); + + // read + + std::ifstream filein(filename.c_str(), std::ios_base::in | std::ios_base::binary); + io::setStreamMetadataPtr(filein, streamMetadata); + io::setMappedFilePtr(filein, mappedFile); + + CPPUNIT_ASSERT_EQUAL(filein.tellg(), std::streampos(0)); + + PagedInputStream istreamSizeOnly(filein); + istreamSizeOnly.setSizeOnly(true); + + std::vector handles; + + for (size_t i = 0; i < values.size(); i += increment) { + if (size_t(i+increment) > values.size()) { + handles.push_back(istreamSizeOnly.createHandle(values.size() - i)); + } + else { + handles.push_back(istreamSizeOnly.createHandle(increment)); + } + } + +#ifdef OPENVDB_USE_BLOSC + // two integers - compressed size and uncompressed size + CPPUNIT_ASSERT_EQUAL(filein.tellg(), std::streampos(pages*sizeof(int)*2)); +#else + // one integer - uncompressed size + CPPUNIT_ASSERT_EQUAL(filein.tellg(), std::streampos(pages*sizeof(int))); +#endif + + PagedInputStream istream(filein); + + int pageHandle = 0; + + for (size_t i = 0; i < values.size(); i += increment) { + if (size_t(i+increment) > values.size()) { + istream.read(handles[pageHandle++], values.size() - i); + } + else { + istream.read(handles[pageHandle++], increment); + } + } + + // first three handles live in the same page + + Page& page0 = handles[0]->page(); + Page& page1 = handles[1]->page(); + Page& page2 = handles[2]->page(); + Page& page3 = handles[3]->page(); + + CPPUNIT_ASSERT(page0.isOutOfCore()); + CPPUNIT_ASSERT(page1.isOutOfCore()); + CPPUNIT_ASSERT(page2.isOutOfCore()); + CPPUNIT_ASSERT(page3.isOutOfCore()); + + handles[0]->read(); + + // store the Page shared_ptr + + Page::Ptr page = handles[0]->mPage; + + // verify use count is four (one plus three handles) + + CPPUNIT_ASSERT_EQUAL(page.use_count(), long(4)); + + // on reading from the first handle, all pages referenced + // in the first three handles are in-core + + CPPUNIT_ASSERT(!page0.isOutOfCore()); + CPPUNIT_ASSERT(!page1.isOutOfCore()); + CPPUNIT_ASSERT(!page2.isOutOfCore()); + CPPUNIT_ASSERT(page3.isOutOfCore()); + + handles[1]->read(); + + CPPUNIT_ASSERT(handles[0]->mPage); + + handles[2]->read(); + + handles.erase(handles.begin()); + handles.erase(handles.begin()); + handles.erase(handles.begin()); + + // after all three handles have been read, + // page should have just one use count (itself) + + CPPUNIT_ASSERT_EQUAL(page.use_count(), long(1)); + } + std::remove(filename.c_str()); + } +} diff --git a/openvdb/unittest/TestStringMetadata.cc b/openvdb/unittest/TestStringMetadata.cc new file mode 100644 index 00000000..6d319405 --- /dev/null +++ b/openvdb/unittest/TestStringMetadata.cc @@ -0,0 +1,43 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + +class TestStringMetadata : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestStringMetadata); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); + + void test(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestStringMetadata); + +void +TestStringMetadata::test() +{ + using namespace openvdb; + + Metadata::Ptr m(new StringMetadata("testing")); + Metadata::Ptr m2 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0); + + CPPUNIT_ASSERT(m->typeName().compare("string") == 0); + CPPUNIT_ASSERT(m2->typeName().compare("string") == 0); + + StringMetadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value().compare("testing") == 0); + s->value() = "testing2"; + CPPUNIT_ASSERT(s->value().compare("testing2") == 0); + + m2->copy(*s); + + s = dynamic_cast(m2.get()); + CPPUNIT_ASSERT(s->value().compare("testing2") == 0); +} diff --git a/openvdb/unittest/TestTools.cc b/openvdb/unittest/TestTools.cc new file mode 100644 index 00000000..b0858c09 --- /dev/null +++ b/openvdb/unittest/TestTools.cc @@ -0,0 +1,2936 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include // for csunion() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() +#include +#include +#include // for std::sort +#include +#include + + +// Uncomment to test on models from our web-site +//#define TestTools_DATA_PATH "/home/kmu/src/openvdb/data/" +//#define TestTools_DATA_PATH "/usr/pic1/Data/OpenVDB/LevelSetModels/" + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + +class TestTools: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestTools); + CPPUNIT_TEST(testDilateVoxels); + CPPUNIT_TEST(testDilateActiveValues); + CPPUNIT_TEST(testErodeVoxels); + CPPUNIT_TEST(testActivate); + CPPUNIT_TEST(testFilter); + CPPUNIT_TEST(testFloatApply); + CPPUNIT_TEST(testInteriorMask); + CPPUNIT_TEST(testLevelSetSphere); + CPPUNIT_TEST(testLevelSetPlatonic); + CPPUNIT_TEST(testLevelSetAdvect); + CPPUNIT_TEST(testLevelSetMeasure); + CPPUNIT_TEST(testLevelSetMorph); + CPPUNIT_TEST(testMagnitude); + CPPUNIT_TEST(testMaskedMagnitude); + CPPUNIT_TEST(testNormalize); + CPPUNIT_TEST(testMaskedNormalize); + CPPUNIT_TEST(testPointAdvect); + CPPUNIT_TEST(testPointScatter); + CPPUNIT_TEST(testPrune); + CPPUNIT_TEST(testVolumeAdvect); + CPPUNIT_TEST(testTransformValues); + CPPUNIT_TEST(testVectorApply); + CPPUNIT_TEST(testAccumulate); + CPPUNIT_TEST(testUtil); + CPPUNIT_TEST(testVectorTransformer); + + CPPUNIT_TEST_SUITE_END(); + + void testDilateVoxels(); + void testDilateActiveValues(); + void testErodeVoxels(); + void testActivate(); + void testFilter(); + void testFloatApply(); + void testInteriorMask(); + void testLevelSetSphere(); + void testLevelSetPlatonic(); + void testLevelSetAdvect(); + void testLevelSetMeasure(); + void testLevelSetMorph(); + void testMagnitude(); + void testMaskedMagnitude(); + void testNormalize(); + void testMaskedNormalize(); + void testPointAdvect(); + void testPointScatter(); + void testPrune(); + void testVolumeAdvect(); + void testTransformValues(); + void testVectorApply(); + void testAccumulate(); + void testUtil(); + void testVectorTransformer(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestTools); + + +#if 0 +namespace { + +// Simple helper class to write out numbered vdbs +template +class FrameWriter +{ +public: + FrameWriter(int version, typename GridT::Ptr grid): + mFrame(0), mVersion(version), mGrid(grid) + {} + + void operator()(const std::string& name, float time, size_t n) + { + std::ostringstream ostr; + ostr << name << "_" << mVersion << "_" << mFrame << ".vdb"; + openvdb::io::File file(ostr.str()); + openvdb::GridPtrVec grids; + grids.push_back(mGrid); + file.write(grids); + std::cerr << "\nWrote \"" << ostr.str() << "\" with time = " + << time << " after CFL-iterations = " << n << std::endl; + ++mFrame; + } + +private: + int mFrame, mVersion; + typename GridT::Ptr mGrid; +}; + +} // unnamed namespace +#endif + + +void +TestTools::testDilateVoxels() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + using openvdb::Index32; + using openvdb::Index64; + + using Tree543f = openvdb::tree::Tree4::Type; + + Tree543f::Ptr tree(new Tree543f); + openvdb::tools::changeBackground(*tree, /*background=*/5.0); + CPPUNIT_ASSERT(tree->empty()); + + const openvdb::Index leafDim = Tree543f::LeafNodeType::DIM; + CPPUNIT_ASSERT_EQUAL(1 << 3, int(leafDim)); + + { + // Set and dilate a single voxel at the center of a leaf node. + tree->clear(); + tree->setValue(Coord(leafDim >> 1), 1.0); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeVoxelCount()); + openvdb::tools::dilateVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(Index64(7), tree->activeVoxelCount()); + } + { + // Create an active, leaf node-sized tile. + tree->clear(); + tree->fill(CoordBBox(Coord(0), Coord(leafDim - 1)), 1.0); + CPPUNIT_ASSERT_EQUAL(Index32(0), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(leafDim * leafDim * leafDim), tree->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeTileCount()); + + tree->setValue(Coord(leafDim, leafDim - 1, leafDim - 1), 1.0); + + CPPUNIT_ASSERT_EQUAL(Index64(leafDim * leafDim * leafDim + 1), + tree->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeTileCount()); + + openvdb::tools::dilateVoxels(*tree); + + CPPUNIT_ASSERT_EQUAL(Index64(leafDim * leafDim * leafDim + 1 + 5), + tree->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeTileCount()); + } + { + // Set and dilate a single voxel at each of the eight corners of a leaf node. + for (int i = 0; i < 8; ++i) { + tree->clear(); + + openvdb::Coord xyz( + i & 1 ? leafDim - 1 : 0, + i & 2 ? leafDim - 1 : 0, + i & 4 ? leafDim - 1 : 0); + tree->setValue(xyz, 1.0); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeVoxelCount()); + + openvdb::tools::dilateVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(Index64(7), tree->activeVoxelCount()); + } + } + { + tree->clear(); + tree->setValue(Coord(0), 1.0); + tree->setValue(Coord( 1, 0, 0), 1.0); + tree->setValue(Coord(-1, 0, 0), 1.0); + CPPUNIT_ASSERT_EQUAL(Index64(3), tree->activeVoxelCount()); + openvdb::tools::dilateVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(Index64(17), tree->activeVoxelCount()); + } + { + struct Info { int activeVoxelCount, leafCount, nonLeafCount; }; + Info iterInfo[11] = { + { 1, 1, 3 }, + { 7, 1, 3 }, + { 25, 1, 3 }, + { 63, 1, 3 }, + { 129, 4, 3 }, + { 231, 7, 9 }, + { 377, 7, 9 }, + { 575, 7, 9 }, + { 833, 10, 9 }, + { 1159, 16, 9 }, + { 1561, 19, 15 }, + }; + + // Perform repeated dilations, starting with a single voxel. + tree->clear(); + tree->setValue(Coord(leafDim >> 1), 1.0); + for (int i = 0; i < 11; ++i) { + CPPUNIT_ASSERT_EQUAL(iterInfo[i].activeVoxelCount, int(tree->activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(iterInfo[i].leafCount, int(tree->leafCount())); + CPPUNIT_ASSERT_EQUAL(iterInfo[i].nonLeafCount, int(tree->nonLeafCount())); + + openvdb::tools::dilateVoxels(*tree); + } + } + + {// dialte a narrow band of a sphere + using GridType = openvdb::Grid; + GridType grid(tree->background()); + unittest_util::makeSphere(/*dim=*/openvdb::Coord(64, 64, 64), + /*center=*/openvdb::Vec3f(0, 0, 0), + /*radius=*/20, grid, /*dx=*/1.0f, + unittest_util::SPHERE_DENSE_NARROW_BAND); + const openvdb::Index64 count = grid.tree().activeVoxelCount(); + openvdb::tools::dilateVoxels(grid.tree()); + CPPUNIT_ASSERT(grid.tree().activeVoxelCount() > count); + } + + {// dilate a fog volume of a sphere + using GridType = openvdb::Grid; + GridType grid(tree->background()); + unittest_util::makeSphere(/*dim=*/openvdb::Coord(64, 64, 64), + /*center=*/openvdb::Vec3f(0, 0, 0), + /*radius=*/20, grid, /*dx=*/1.0f, + unittest_util::SPHERE_DENSE_NARROW_BAND); + openvdb::tools::sdfToFogVolume(grid); + const openvdb::Index64 count = grid.tree().activeVoxelCount(); + //std::cerr << "\nBefore: active voxel count = " << count << std::endl; + //grid.print(std::cerr,5); + openvdb::tools::dilateVoxels(grid.tree()); + CPPUNIT_ASSERT(grid.tree().activeVoxelCount() > count); + //std::cerr << "\nAfter: active voxel count = " + // << grid.tree().activeVoxelCount() << std::endl; + } +// {// Test a grid from a file that has proven to be challenging +// openvdb::initialize(); +// openvdb::io::File file("/usr/home/kmuseth/Data/vdb/dilation.vdb"); +// file.open(); +// openvdb::GridBase::Ptr baseGrid = file.readGrid(file.beginName().gridName()); +// file.close(); +// openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast(baseGrid); +// const openvdb::Index64 count = grid->tree().activeVoxelCount(); +// //std::cerr << "\nBefore: active voxel count = " << count << std::endl; +// //grid->print(std::cerr,5); +// openvdb::tools::dilateVoxels(grid->tree()); +// CPPUNIT_ASSERT(grid->tree().activeVoxelCount() > count); +// //std::cerr << "\nAfter: active voxel count = " +// // << grid->tree().activeVoxelCount() << std::endl; +// } + + {// test dilateVoxels6 + for (int x=0; x<8; ++x) { + for (int y=0; y<8; ++y) { + for (int z=0; z<8; ++z) { + const openvdb::Coord ijk(x,y,z); + Tree543f tree1(0.0f); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree1.activeVoxelCount()); + tree1.setValue(ijk, 1.0f); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree1.isValueOn(ijk)); + openvdb::tools::Morphology m(tree1); + m.dilateVoxels6(); + for (int i=-1; i<=1; ++i) { + for (int j=-1; j<=1; ++j) { + for (int k=-1; k<=1; ++k) { + const openvdb::Coord xyz = ijk.offsetBy(i,j,k), d=ijk-xyz; + const int n= openvdb::math::Abs(d[0]) + + openvdb::math::Abs(d[1]) + + openvdb::math::Abs(d[2]); + if (n<=1) { + CPPUNIT_ASSERT( tree1.isValueOn(xyz)); + } else { + CPPUNIT_ASSERT(!tree1.isValueOn(xyz)); + } + } + } + } + CPPUNIT_ASSERT_EQUAL(Index64(1 + 6), tree1.activeVoxelCount()); + } + } + } + } + {// test dilateVoxels18 + for (int x=0; x<8; ++x) { + for (int y=0; y<8; ++y) { + for (int z=0; z<8; ++z) { + const openvdb::Coord ijk(x,y,z); + Tree543f tree1(0.0f); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree1.activeVoxelCount()); + tree1.setValue(ijk, 1.0f); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree1.isValueOn(ijk)); + openvdb::tools::Morphology m(tree1); + m.dilateVoxels18(); + for (int i=-1; i<=1; ++i) { + for (int j=-1; j<=1; ++j) { + for (int k=-1; k<=1; ++k) { + const openvdb::Coord xyz = ijk.offsetBy(i,j,k), d=ijk-xyz; + const int n= openvdb::math::Abs(d[0]) + + openvdb::math::Abs(d[1]) + + openvdb::math::Abs(d[2]); + if (n<=2) { + CPPUNIT_ASSERT( tree1.isValueOn(xyz)); + } else { + CPPUNIT_ASSERT(!tree1.isValueOn(xyz)); + } + } + } + } + CPPUNIT_ASSERT_EQUAL(Index64(1 + 6 + 12), tree1.activeVoxelCount()); + } + } + } + } + {// test dilateVoxels26 + for (int x=0; x<8; ++x) { + for (int y=0; y<8; ++y) { + for (int z=0; z<8; ++z) { + const openvdb::Coord ijk(x,y,z); + Tree543f tree1(0.0f); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree1.activeVoxelCount()); + tree1.setValue(ijk, 1.0f); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree1.isValueOn(ijk)); + openvdb::tools::Morphology m(tree1); + m.dilateVoxels26(); + for (int i=-1; i<=1; ++i) { + for (int j=-1; j<=1; ++j) { + for (int k=-1; k<=1; ++k) { + const openvdb::Coord xyz = ijk.offsetBy(i,j,k), d=ijk-xyz; + const int n = openvdb::math::Abs(d[0]) + + openvdb::math::Abs(d[1]) + + openvdb::math::Abs(d[2]); + if (n<=3) { + CPPUNIT_ASSERT( tree1.isValueOn(xyz)); + } else { + CPPUNIT_ASSERT(!tree1.isValueOn(xyz)); + } + } + } + } + CPPUNIT_ASSERT_EQUAL(Index64(1 + 6 + 12 + 8), tree1.activeVoxelCount()); + } + } + } + } + /* + // Performance test + {// dialte a narrow band of a sphere + const float radius = 335.3f; + const openvdb::Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.5f, width = 3.25f; + openvdb::FloatGrid::Ptr grid = + openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, width); + //grid->print(std::cerr, 3); + const openvdb::Index64 count = grid->tree().activeVoxelCount(); + openvdb::util::CpuTimer t; + t.start("sphere dilateVoxels6"); + openvdb::tools::dilateVoxels(grid->tree()); + t.stop(); + CPPUNIT_ASSERT(grid->tree().activeVoxelCount() > count); + // grid->print(std::cerr, 3); + } + {// dialte a narrow band of a sphere + const float radius = 335.3f; + const openvdb::Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.5f, width = 3.25f; + openvdb::FloatGrid::Ptr grid = + openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, width); + //grid->print(std::cerr, 3); + const openvdb::Index64 count = grid->tree().activeVoxelCount(); + openvdb::util::CpuTimer t; + t.start("sphere dilateVoxels18"); + openvdb::tools::dilateVoxels(grid->tree(), 1, openvdb::tools::NN_FACE_EDGE); + t.stop(); + CPPUNIT_ASSERT(grid->tree().activeVoxelCount() > count); + //grid->print(std::cerr, 3); + } + {// dialte a narrow band of a sphere + const float radius = 335.3f; + const openvdb::Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.5f, width = 3.25f; + openvdb::FloatGrid::Ptr grid = + openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, width); + //grid->print(std::cerr, 3); + const openvdb::Index64 count = grid->tree().activeVoxelCount(); + openvdb::util::CpuTimer t; + t.start("sphere dilateVoxels26"); + openvdb::tools::dilateVoxels(grid->tree(), 1, openvdb::tools::NN_FACE_EDGE_VERTEX); + t.stop(); + CPPUNIT_ASSERT(grid->tree().activeVoxelCount() > count); + //grid->print(std::cerr, 3); + } + */ + +#ifdef TestTools_DATA_PATH + { + openvdb::initialize();//required whenever I/O of OpenVDB files is performed! + const std::string path(TestTools_DATA_PATH); + std::vector filenames; + filenames.push_back("armadillo.vdb"); + filenames.push_back("buddha.vdb"); + filenames.push_back("bunny.vdb"); + filenames.push_back("crawler.vdb"); + filenames.push_back("dragon.vdb"); + filenames.push_back("iss.vdb"); + filenames.push_back("utahteapot.vdb"); + openvdb::util::CpuTimer timer; + for ( size_t i=0; i\"" << filenames[i] << "\" =====================" << std::endl; + std::cerr << "Reading \"" << filenames[i] << "\" ...\n"; + openvdb::io::File file( path + filenames[i] ); + file.open(false);//disable delayed loading + openvdb::FloatGrid::Ptr model = openvdb::gridPtrCast(file.getGrids()->at(0)); + openvdb::MaskTree mask(model->tree(), false, true, openvdb::TopologyCopy() ); + timer.start("Calling dilateVoxels on grid"); + openvdb::tools::dilateVoxels(model->tree(), 1, openvdb::tools::NN_FACE); + timer.stop(); + //model->tree().print(std::cout, 3); + timer.start("Calling dilateVoxels on mask"); + openvdb::tools::dilateVoxels(mask, 1, openvdb::tools::NN_FACE); + timer.stop(); + //mask.print(std::cout, 3); + CPPUNIT_ASSERT_EQUAL(model->activeVoxelCount(), mask.activeVoxelCount()); + } + } +#endif +} + +void +TestTools::testDilateActiveValues() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + using openvdb::Index32; + using openvdb::Index64; + + using Tree543f = openvdb::tree::Tree4::Type; + + Tree543f::Ptr tree(new Tree543f); + openvdb::tools::changeBackground(*tree, /*background=*/5.0); + CPPUNIT_ASSERT(tree->empty()); + + const openvdb::Index leafDim = Tree543f::LeafNodeType::DIM; + CPPUNIT_ASSERT_EQUAL(1 << 3, int(leafDim)); + + { + // Set and dilate a single voxel at the center of a leaf node. + tree->clear(); + tree->setValue(Coord(leafDim >> 1), 1.0); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeVoxelCount()); + openvdb::tools::dilateActiveValues(*tree); + CPPUNIT_ASSERT_EQUAL(Index64(7), tree->activeVoxelCount()); + } + { + // Create an active, leaf node-sized tile. + tree->clear(); + tree->fill(CoordBBox(Coord(0), Coord(leafDim - 1)), 1.0); + CPPUNIT_ASSERT_EQUAL(Index32(0), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(leafDim * leafDim * leafDim), tree->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeTileCount()); + + // This has no effect + openvdb::tools::dilateActiveValues(*tree, 1, openvdb::tools::NN_FACE, openvdb::tools::IGNORE_TILES); + + CPPUNIT_ASSERT_EQUAL(Index32(0), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(leafDim * leafDim * leafDim), tree->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeTileCount()); + } + { + // Create an active, leaf node-sized tile. + tree->clear(); + tree->fill(CoordBBox(Coord(0), Coord(leafDim - 1)), 1.0); + CPPUNIT_ASSERT_EQUAL(Index32(0), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(leafDim * leafDim * leafDim), tree->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeTileCount()); + + // Adds 6 faces of voxels, each of size leafDim^2 + openvdb::tools::dilateActiveValues(*tree, 1, openvdb::tools::NN_FACE, openvdb::tools::EXPAND_TILES); + + CPPUNIT_ASSERT_EQUAL(Index32(1+6), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64((leafDim + 6) * leafDim * leafDim), tree->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree->activeTileCount()); + + } + { + // Create an active, leaf node-sized tile. + tree->clear(); + tree->fill(CoordBBox(Coord(0), Coord(leafDim - 1)), 1.0); + CPPUNIT_ASSERT_EQUAL(Index32(0), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64(leafDim * leafDim * leafDim), tree->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeTileCount()); + + // Adds 6 faces of voxels, each of size leafDim^2 + openvdb::tools::dilateActiveValues(*tree, 1, openvdb::tools::NN_FACE, openvdb::tools::PRESERVE_TILES); + + CPPUNIT_ASSERT_EQUAL(Index32(6), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(Index64((leafDim + 6) * leafDim * leafDim), tree->activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeTileCount()); + + } + { + // Set and dilate a single voxel at each of the eight corners of a leaf node. + for (int i = 0; i < 8; ++i) { + tree->clear(); + + openvdb::Coord xyz( + i & 1 ? leafDim - 1 : 0, + i & 2 ? leafDim - 1 : 0, + i & 4 ? leafDim - 1 : 0); + tree->setValue(xyz, 1.0); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeVoxelCount()); + + openvdb::tools::dilateActiveValues(*tree); + CPPUNIT_ASSERT_EQUAL(Index64(7), tree->activeVoxelCount()); + } + } + { + tree->clear(); + tree->setValue(Coord(0), 1.0); + tree->setValue(Coord( 1, 0, 0), 1.0); + tree->setValue(Coord(-1, 0, 0), 1.0); + CPPUNIT_ASSERT_EQUAL(Index64(3), tree->activeVoxelCount()); + openvdb::tools::dilateActiveValues(*tree); + CPPUNIT_ASSERT_EQUAL(Index64(17), tree->activeVoxelCount()); + } + { + struct Info { int activeVoxelCount, leafCount, nonLeafCount; }; + Info iterInfo[11] = { + { 1, 1, 3 }, + { 7, 1, 3 }, + { 25, 1, 3 }, + { 63, 1, 3 }, + { 129, 4, 3 }, + { 231, 7, 9 }, + { 377, 7, 9 }, + { 575, 7, 9 }, + { 833, 10, 9 }, + { 1159, 16, 9 }, + { 1561, 19, 15 }, + }; + + // Perform repeated dilations, starting with a single voxel. + tree->clear(); + tree->setValue(Coord(leafDim >> 1), 1.0); + for (int i = 0; i < 11; ++i) { + CPPUNIT_ASSERT_EQUAL(iterInfo[i].activeVoxelCount, int(tree->activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(iterInfo[i].leafCount, int(tree->leafCount())); + CPPUNIT_ASSERT_EQUAL(iterInfo[i].nonLeafCount, int(tree->nonLeafCount())); + + openvdb::tools::dilateActiveValues(*tree); + } + } + + {// dialte a narrow band of a sphere + using GridType = openvdb::Grid; + GridType grid(tree->background()); + unittest_util::makeSphere(/*dim=*/openvdb::Coord(64, 64, 64), + /*center=*/openvdb::Vec3f(0, 0, 0), + /*radius=*/20, grid, /*dx=*/1.0f, + unittest_util::SPHERE_DENSE_NARROW_BAND); + const openvdb::Index64 count = grid.tree().activeVoxelCount(); + openvdb::tools::dilateActiveValues(grid.tree()); + CPPUNIT_ASSERT(grid.tree().activeVoxelCount() > count); + } + + {// dilate a fog volume of a sphere + using GridType = openvdb::Grid; + GridType grid(tree->background()); + unittest_util::makeSphere(/*dim=*/openvdb::Coord(64, 64, 64), + /*center=*/openvdb::Vec3f(0, 0, 0), + /*radius=*/20, grid, /*dx=*/1.0f, + unittest_util::SPHERE_DENSE_NARROW_BAND); + openvdb::tools::sdfToFogVolume(grid); + const openvdb::Index64 count = grid.tree().activeVoxelCount(); + //std::cerr << "\nBefore: active voxel count = " << count << std::endl; + //grid.print(std::cerr,5); + openvdb::tools::dilateActiveValues(grid.tree()); + CPPUNIT_ASSERT(grid.tree().activeVoxelCount() > count); + //std::cerr << "\nAfter: active voxel count = " + // << grid.tree().activeVoxelCount() << std::endl; + } +// {// Test a grid from a file that has proven to be challenging +// openvdb::initialize(); +// openvdb::io::File file("/usr/home/kmuseth/Data/vdb/dilation.vdb"); +// file.open(); +// openvdb::GridBase::Ptr baseGrid = file.readGrid(file.beginName().gridName()); +// file.close(); +// openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast(baseGrid); +// const openvdb::Index64 count = grid->tree().activeVoxelCount(); +// //std::cerr << "\nBefore: active voxel count = " << count << std::endl; +// //grid->print(std::cerr,5); +// openvdb::tools::dilateActiveValues(grid->tree()); +// CPPUNIT_ASSERT(grid->tree().activeVoxelCount() > count); +// //std::cerr << "\nAfter: active voxel count = " +// // << grid->tree().activeVoxelCount() << std::endl; +// } + + {// test dilateVoxels6 + for (int x=0; x<8; ++x) { + for (int y=0; y<8; ++y) { + for (int z=0; z<8; ++z) { + const openvdb::Coord ijk(x,y,z); + Tree543f tree1(0.0f); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree1.activeVoxelCount()); + tree1.setValue(ijk, 1.0f); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree1.isValueOn(ijk)); + openvdb::tools::dilateActiveValues(tree1, 1, openvdb::tools::NN_FACE); + //openvdb::tools::Morphology m(tree1); + //m.dilateVoxels6(); + for (int i=-1; i<=1; ++i) { + for (int j=-1; j<=1; ++j) { + for (int k=-1; k<=1; ++k) { + const openvdb::Coord xyz = ijk.offsetBy(i,j,k), d=ijk-xyz; + const int n= openvdb::math::Abs(d[0]) + + openvdb::math::Abs(d[1]) + + openvdb::math::Abs(d[2]); + if (n<=1) { + CPPUNIT_ASSERT( tree1.isValueOn(xyz)); + } else { + CPPUNIT_ASSERT(!tree1.isValueOn(xyz)); + } + } + } + } + CPPUNIT_ASSERT_EQUAL(Index64(1 + 6), tree1.activeVoxelCount()); + } + } + } + } + {// test dilateVoxels18 + for (int x=0; x<8; ++x) { + for (int y=0; y<8; ++y) { + for (int z=0; z<8; ++z) { + const openvdb::Coord ijk(x,y,z); + Tree543f tree1(0.0f); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree1.activeVoxelCount()); + tree1.setValue(ijk, 1.0f); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree1.isValueOn(ijk)); + openvdb::tools::dilateActiveValues(tree1, 1, openvdb::tools::NN_FACE_EDGE); + //openvdb::tools::Morphology m(tree1); + //m.dilateVoxels18(); + for (int i=-1; i<=1; ++i) { + for (int j=-1; j<=1; ++j) { + for (int k=-1; k<=1; ++k) { + const openvdb::Coord xyz = ijk.offsetBy(i,j,k), d=ijk-xyz; + const int n= openvdb::math::Abs(d[0]) + + openvdb::math::Abs(d[1]) + + openvdb::math::Abs(d[2]); + if (n<=2) { + CPPUNIT_ASSERT( tree1.isValueOn(xyz)); + } else { + CPPUNIT_ASSERT(!tree1.isValueOn(xyz)); + } + } + } + } + CPPUNIT_ASSERT_EQUAL(Index64(1 + 6 + 12), tree1.activeVoxelCount()); + } + } + } + } + {// test dilateVoxels26 + for (int x=0; x<8; ++x) { + for (int y=0; y<8; ++y) { + for (int z=0; z<8; ++z) { + const openvdb::Coord ijk(x,y,z); + Tree543f tree1(0.0f); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree1.activeVoxelCount()); + tree1.setValue(ijk, 1.0f); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree1.isValueOn(ijk)); + openvdb::tools::dilateActiveValues(tree1, 1, openvdb::tools::NN_FACE_EDGE_VERTEX); + //openvdb::tools::Morphology m(tree1); + //m.dilateVoxels26(); + for (int i=-1; i<=1; ++i) { + for (int j=-1; j<=1; ++j) { + for (int k=-1; k<=1; ++k) { + const openvdb::Coord xyz = ijk.offsetBy(i,j,k), d=ijk-xyz; + const int n = openvdb::math::Abs(d[0]) + + openvdb::math::Abs(d[1]) + + openvdb::math::Abs(d[2]); + if (n<=3) { + CPPUNIT_ASSERT( tree1.isValueOn(xyz)); + } else { + CPPUNIT_ASSERT(!tree1.isValueOn(xyz)); + } + } + } + } + CPPUNIT_ASSERT_EQUAL(Index64(1 + 6 + 12 + 8), tree1.activeVoxelCount()); + } + } + } + } + /* + // Performance test + {// dialte a narrow band of a sphere + const float radius = 335.3f; + const openvdb::Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.5f, width = 3.25f; + openvdb::FloatGrid::Ptr grid = + openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, width); + //grid->print(std::cerr, 3); + const openvdb::Index64 count = grid->tree().activeVoxelCount(); + openvdb::util::CpuTimer t; + t.start("sphere dilateActiveValues6"); + openvdb::tools::dilateActiveValues(grid->tree(), 1, openvdb::tools::NN_FACE); + //openvdb::tools::dilateVoxels(grid->tree()); + t.stop(); + CPPUNIT_ASSERT(grid->tree().activeVoxelCount() > count); + // grid->print(std::cerr, 3); + } + {// dialte a narrow band of a sphere + const float radius = 335.3f; + const openvdb::Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.5f, width = 3.25f; + openvdb::FloatGrid::Ptr grid = + openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, width); + //grid->print(std::cerr, 3); + const openvdb::Index64 count = grid->tree().activeVoxelCount(); + openvdb::util::CpuTimer t; + t.start("sphere dilateActiveValues18"); + openvdb::tools::dilateActiveValues(grid->tree(), 1, openvdb::tools::NN_FACE_EDGE); + //openvdb::tools::dilateVoxels(grid->tree(), 1, openvdb::tools::NN_FACE_EDGE); + t.stop(); + CPPUNIT_ASSERT(grid->tree().activeVoxelCount() > count); + //grid->print(std::cerr, 3); + } + {// dialte a narrow band of a sphere + const float radius = 335.3f; + const openvdb::Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 0.5f, width = 3.25f; + openvdb::FloatGrid::Ptr grid = + openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, width); + //grid->print(std::cerr, 3); + const openvdb::Index64 count = grid->tree().activeVoxelCount(); + openvdb::util::CpuTimer t; + t.start("sphere dilateActiveValues26"); + openvdb::tools::dilateActiveValues(grid->tree(), 1, openvdb::tools::NN_FACE_EDGE_VERTEX); + //openvdb::tools::dilateVoxels(grid->tree(), 1, openvdb::tools::NN_FACE_EDGE_VERTEX); + t.stop(); + CPPUNIT_ASSERT(grid->tree().activeVoxelCount() > count); + //grid->print(std::cerr, 3); + } + */ + #ifdef TestTools_DATA_PATH + { + openvdb::initialize();//required whenever I/O of OpenVDB files is performed! + const std::string path(TestTools_DATA_PATH); + std::vector filenames; + filenames.push_back("armadillo.vdb"); + filenames.push_back("buddha.vdb"); + filenames.push_back("bunny.vdb"); + filenames.push_back("crawler.vdb"); + filenames.push_back("dragon.vdb"); + filenames.push_back("iss.vdb"); + filenames.push_back("utahteapot.vdb"); + openvdb::util::CpuTimer timer; + for ( size_t i=0; i\"" << filenames[i] << "\" =====================" << std::endl; + std::cerr << "Reading \"" << filenames[i] << "\" ...\n"; + openvdb::io::File file( path + filenames[i] ); + file.open(false);//disable delayed loading + openvdb::FloatGrid::Ptr model = openvdb::gridPtrCast(file.getGrids()->at(0)); + openvdb::MaskTree mask(model->tree(), false, true, openvdb::TopologyCopy() ); + timer.start("Calling dilateActiveValues on grid"); + openvdb::tools::dilateActiveValues(model->tree(), 1, openvdb::tools::NN_FACE); + timer.stop(); + //model->tree().print(std::cout, 3); + timer.start("Calling dilateActiveValues on mask"); + openvdb::tools::dilateActiveValues(mask, 1, openvdb::tools::NN_FACE); + timer.stop(); + //mask.print(std::cout, 3); + CPPUNIT_ASSERT_EQUAL(model->activeVoxelCount(), mask.activeVoxelCount()); + } + } +#endif + +} + +void +TestTools::testErodeVoxels() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + using openvdb::Index32; + using openvdb::Index64; + + using TreeType = openvdb::tree::Tree4::Type; + + TreeType::Ptr tree(new TreeType); + openvdb::tools::changeBackground(*tree, /*background=*/5.0); + CPPUNIT_ASSERT(tree->empty()); + + const int leafDim = TreeType::LeafNodeType::DIM; + CPPUNIT_ASSERT_EQUAL(1 << 3, leafDim); + + { + // Set, dilate and erode a single voxel at the center of a leaf node. + tree->clear(); + CPPUNIT_ASSERT_EQUAL(0, int(tree->activeVoxelCount())); + + tree->setValue(Coord(leafDim >> 1), 1.0); + CPPUNIT_ASSERT_EQUAL(1, int(tree->activeVoxelCount())); + + openvdb::tools::dilateVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(7, int(tree->activeVoxelCount())); + + openvdb::tools::erodeVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(1, int(tree->activeVoxelCount())); + + openvdb::tools::erodeVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(0, int(tree->activeVoxelCount())); + } + { + // Create an active, leaf node-sized tile. + tree->clear(); + tree->fill(CoordBBox(Coord(0), Coord(leafDim - 1)), 1.0); + CPPUNIT_ASSERT_EQUAL(0, int(tree->leafCount())); + CPPUNIT_ASSERT_EQUAL(leafDim * leafDim * leafDim, int(tree->activeVoxelCount())); + + tree->setValue(Coord(leafDim, leafDim - 1, leafDim - 1), 1.0); + CPPUNIT_ASSERT_EQUAL(1, int(tree->leafCount())); + CPPUNIT_ASSERT_EQUAL(leafDim * leafDim * leafDim + 1,int(tree->activeVoxelCount())); + + openvdb::tools::dilateVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(3, int(tree->leafCount())); + CPPUNIT_ASSERT_EQUAL(leafDim * leafDim * leafDim + 1 + 5,int(tree->activeVoxelCount())); + + openvdb::tools::erodeVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(1, int(tree->leafCount())); + CPPUNIT_ASSERT_EQUAL(leafDim * leafDim * leafDim + 1, int(tree->activeVoxelCount())); + } + { + // Set and dilate a single voxel at each of the eight corners of a leaf node. + for (int i = 0; i < 8; ++i) { + tree->clear(); + + openvdb::Coord xyz( + i & 1 ? leafDim - 1 : 0, + i & 2 ? leafDim - 1 : 0, + i & 4 ? leafDim - 1 : 0); + tree->setValue(xyz, 1.0); + CPPUNIT_ASSERT_EQUAL(1, int(tree->activeVoxelCount())); + + openvdb::tools::dilateVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(7, int(tree->activeVoxelCount())); + + openvdb::tools::erodeVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(1, int(tree->activeVoxelCount())); + } + } + { + // Set three active voxels and dilate and erode + tree->clear(); + tree->setValue(Coord(0), 1.0); + tree->setValue(Coord( 1, 0, 0), 1.0); + tree->setValue(Coord(-1, 0, 0), 1.0); + CPPUNIT_ASSERT_EQUAL(3, int(tree->activeVoxelCount())); + + openvdb::tools::dilateVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(17, int(tree->activeVoxelCount())); + + openvdb::tools::erodeVoxels(*tree); + CPPUNIT_ASSERT_EQUAL(3, int(tree->activeVoxelCount())); + } + { + struct Info { + void test(TreeType::Ptr aTree) { + CPPUNIT_ASSERT_EQUAL(activeVoxelCount, int(aTree->activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(leafCount, int(aTree->leafCount())); + CPPUNIT_ASSERT_EQUAL(nonLeafCount, int(aTree->nonLeafCount())); + } + int activeVoxelCount, leafCount, nonLeafCount; + }; + Info iterInfo[12] = { + { 0, 0, 1 },//an empty tree only contains a root node + { 1, 1, 3 }, + { 7, 1, 3 }, + { 25, 1, 3 }, + { 63, 1, 3 }, + { 129, 4, 3 }, + { 231, 7, 9 }, + { 377, 7, 9 }, + { 575, 7, 9 }, + { 833, 10, 9 }, + { 1159, 16, 9 }, + { 1561, 19, 15 }, + }; + + // Perform repeated dilations, starting with a single voxel. + tree->clear(); + iterInfo[0].test(tree); + + tree->setValue(Coord(leafDim >> 1), 1.0); + iterInfo[1].test(tree); + + for (int i = 2; i < 12; ++i) { + openvdb::tools::dilateVoxels(*tree); + iterInfo[i].test(tree); + } + for (int i = 10; i >= 0; --i) { + openvdb::tools::erodeVoxels(*tree); + iterInfo[i].test(tree); + } + + // Now try it using the resursive calls + for (int i = 2; i < 12; ++i) { + tree->clear(); + tree->setValue(Coord(leafDim >> 1), 1.0); + openvdb::tools::dilateVoxels(*tree, i-1); + iterInfo[i].test(tree); + } + for (int i = 10; i >= 0; --i) { + tree->clear(); + tree->setValue(Coord(leafDim >> 1), 1.0); + openvdb::tools::dilateVoxels(*tree, 10); + openvdb::tools::erodeVoxels(*tree, 11-i); + iterInfo[i].test(tree); + } + } + + {// erode a narrow band of a sphere + using GridType = openvdb::Grid; + GridType grid(tree->background()); + unittest_util::makeSphere(/*dim=*/openvdb::Coord(64, 64, 64), + /*center=*/openvdb::Vec3f(0, 0, 0), + /*radius=*/20, grid, /*dx=*/1.0f, + unittest_util::SPHERE_DENSE_NARROW_BAND); + const openvdb::Index64 count = grid.tree().activeVoxelCount(); + openvdb::tools::erodeVoxels(grid.tree()); + CPPUNIT_ASSERT(grid.tree().activeVoxelCount() < count); + } + + {// erode a fog volume of a sphere + using GridType = openvdb::Grid; + GridType grid(tree->background()); + unittest_util::makeSphere(/*dim=*/openvdb::Coord(64, 64, 64), + /*center=*/openvdb::Vec3f(0, 0, 0), + /*radius=*/20, grid, /*dx=*/1.0f, + unittest_util::SPHERE_DENSE_NARROW_BAND); + openvdb::tools::sdfToFogVolume(grid); + const openvdb::Index64 count = grid.tree().activeVoxelCount(); + openvdb::tools::erodeVoxels(grid.tree()); + CPPUNIT_ASSERT(grid.tree().activeVoxelCount() < count); + } + + {//erode6 + for (int x=0; x<8; ++x) { + for (int y=0; y<8; ++y) { + for (int z=0; z<8; ++z) { + tree->clear(); + const openvdb::Coord ijk(x,y,z); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree->activeVoxelCount()); + tree->setValue(ijk, 1.0f); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeVoxelCount()); + CPPUNIT_ASSERT(tree->isValueOn(ijk)); + openvdb::tools::dilateVoxels(*tree, 1, openvdb::tools::NN_FACE); + CPPUNIT_ASSERT_EQUAL(Index64(1 + 6), tree->activeVoxelCount()); + openvdb::tools::erodeVoxels( *tree, 1, openvdb::tools::NN_FACE); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeVoxelCount()); + CPPUNIT_ASSERT(tree->isValueOn(ijk)); + } + } + } + } + +#if 0 + {//erode18 + /// @todo Not implemented yet + for (int iter=1; iter<4; ++iter) { + for (int x=0; x<8; ++x) { + for (int y=0; y<8; ++y) { + for (int z=0; z<8; ++z) { + const openvdb::Coord ijk(x,y,z); + tree->clear(); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree->activeVoxelCount()); + tree->setValue(ijk, 1.0f); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeVoxelCount()); + CPPUNIT_ASSERT(tree->isValueOn(ijk)); + //openvdb::tools::dilateVoxels(*tree, iter, openvdb::tools::NN_FACE_EDGE); + openvdb::tools::dilateVoxels(*tree, iter, openvdb::tools::NN_FACE); + //std::cerr << "Dilated to: " << tree->activeVoxelCount() << std::endl; + //if (iter==1) { + // CPPUNIT_ASSERT_EQUAL(Index64(1 + 6 + 12), tree->activeVoxelCount()); + //} + openvdb::tools::erodeVoxels( *tree, iter, openvdb::tools::NN_FACE_EDGE); + CPPUNIT_ASSERT_EQUAL(Index64(1), tree->activeVoxelCount()); + CPPUNIT_ASSERT(tree->isValueOn(ijk)); + } + } + } + } + } +#endif +#if 0 + {//erode26 + /// @todo Not implemented yet + tree->clear(); + tree->setValue(openvdb::Coord(3,4,5), 1.0f); + openvdb::tools::dilateVoxels(*tree, 1, openvdb::tools::NN_FACE_EDGE_VERTEX); + CPPUNIT_ASSERT_EQUAL(Index64(1 + 6 + 12 + 8), tree->activeVoxelCount()); + openvdb::tools::erodeVoxels( *tree, 1, openvdb::tools::NN_FACE_EDGE_VERTEX); + //openvdb::tools::dilateVoxels(*tree, 12, openvdb::tools::NN_FACE_EDGE); + //openvdb::tools::erodeVoxels( *tree, 12, openvdb::tools::NN_FACE_EDGE); + CPPUNIT_ASSERT_EQUAL(1, int(tree->activeVoxelCount())); + CPPUNIT_ASSERT(tree->isValueOn(openvdb::Coord(3,4,5))); + } +#endif +} + + +void +TestTools::testActivate() +{ + using namespace openvdb; + + const Vec3s background(0.0, -1.0, 1.0), foreground(42.0); + + Vec3STree tree(background); + + const CoordBBox bbox1(Coord(-200), Coord(-181)), bbox2(Coord(51), Coord(373)); + + // Set some non-background active voxels. + tree.fill(bbox1, Vec3s(0.0), /*active=*/true); + + // Mark some background voxels as active. + tree.fill(bbox2, background, /*active=*/true); + CPPUNIT_ASSERT_EQUAL(bbox2.volume() + bbox1.volume(), tree.activeVoxelCount()); + + // Deactivate all voxels with the background value. + tools::deactivate(tree, background, /*tolerance=*/Vec3s(1.0e-6f)); + // Verify that there are no longer any active voxels with the background value. + CPPUNIT_ASSERT_EQUAL(bbox1.volume(), tree.activeVoxelCount()); + + // Set some voxels to the foreground value but leave them inactive. + tree.fill(bbox2, foreground, /*active=*/false); + // Verify that there are no active voxels with the background value. + CPPUNIT_ASSERT_EQUAL(bbox1.volume(), tree.activeVoxelCount()); + + // Activate all voxels with the foreground value. + tools::activate(tree, foreground); + // Verify that the expected number of voxels are active. + CPPUNIT_ASSERT_EQUAL(bbox1.volume() + bbox2.volume(), tree.activeVoxelCount()); +} + +void +TestTools::testFilter() +{ + openvdb::FloatGrid::Ptr referenceGrid = openvdb::FloatGrid::create(/*background=*/5.0); + + const openvdb::Coord dim(40); + const openvdb::Vec3f center(25.0f, 20.0f, 20.0f); + const float radius = 10.0f; + unittest_util::makeSphere( + dim, center, radius, *referenceGrid, unittest_util::SPHERE_DENSE); + const openvdb::FloatTree& sphere = referenceGrid->tree(); + + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(sphere.activeVoxelCount())); + openvdb::Coord xyz; + + {// test Filter::offsetFilter + openvdb::FloatGrid::Ptr grid = referenceGrid->deepCopy(); + openvdb::FloatTree& tree = grid->tree(); + openvdb::tools::Filter filter(*grid); + const float offset = 2.34f; + filter.setGrainSize(0);//i.e. disable threading + filter.offset(offset); + for (int x=0; x0.0001f) std::cerr << " failed at " << xyz << std::endl; + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0f, delta, /*tolerance=*/0.0001); + } + } + } + filter.setGrainSize(1);//i.e. enable threading + filter.offset(-offset);//default is multi-threaded + for (int x=0; x0.0001f) std::cerr << " failed at " << xyz << std::endl; + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0f, delta, /*tolerance=*/0.0001); + } + } + } + //std::cerr << "Successfully completed TestTools::testFilter offset test" << std::endl; + } + {// test Filter::median + openvdb::FloatGrid::Ptr filteredGrid = referenceGrid->deepCopy(); + openvdb::FloatTree& filteredTree = filteredGrid->tree(); + const int width = 2; + openvdb::math::DenseStencil stencil(*referenceGrid, width); + openvdb::tools::Filter filter(*filteredGrid); + filter.median(width, /*interations=*/1); + std::vector tmp; + for (int x=0; xdeepCopy(); + openvdb::FloatTree& filteredTree = filteredGrid->tree(); + const int width = 2; + openvdb::math::DenseStencil stencil(*referenceGrid, width); + openvdb::tools::Filter filter(*filteredGrid); + filter.mean(width, /*interations=*/1); + for (int x=0; xactiveVoxelCount()); + + // For a level set, tools::interiorMask() should return a mask + // of the interior of the isosurface. + lsgrid.setGridClass(GRID_LEVEL_SET); + mask = tools::interiorMask(lsgrid); + CPPUNIT_ASSERT_EQUAL(intBand.volume(), mask->activeVoxelCount()); +} + + +void +TestTools::testLevelSetSphere() +{ + const float radius = 4.3f; + const openvdb::Vec3f center(15.8f, 13.2f, 16.7f); + const float voxelSize = 1.5f, width = 3.25f; + const int dim = 32; + + openvdb::FloatGrid::Ptr grid1 = + openvdb::tools::createLevelSetSphere(radius, center, voxelSize, width); + + /// Also test ultra slow makeSphere in unittest/util.h + openvdb::FloatGrid::Ptr grid2 = openvdb::createLevelSet(voxelSize, width); + unittest_util::makeSphere( + openvdb::Coord(dim), center, radius, *grid2, unittest_util::SPHERE_SPARSE_NARROW_BAND); + + const float outside = grid1->background(), inside = -outside; + for (int i=0; itree().getValue(openvdb::Coord(i,j,k)); + const float val2 = grid2->tree().getValue(openvdb::Coord(i,j,k)); + if (dist > outside) { + CPPUNIT_ASSERT_DOUBLES_EQUAL( outside, val1, 0.0001); + CPPUNIT_ASSERT_DOUBLES_EQUAL( outside, val2, 0.0001); + } else if (dist < inside) { + CPPUNIT_ASSERT_DOUBLES_EQUAL( inside, val1, 0.0001); + CPPUNIT_ASSERT_DOUBLES_EQUAL( inside, val2, 0.0001); + } else { + CPPUNIT_ASSERT_DOUBLES_EQUAL( dist, val1, 0.0001); + CPPUNIT_ASSERT_DOUBLES_EQUAL( dist, val2, 0.0001); + } + } + } + } + + CPPUNIT_ASSERT_EQUAL(grid1->activeVoxelCount(), grid2->activeVoxelCount()); +}// testLevelSetSphere + +void +TestTools::testLevelSetPlatonic() +{ + using namespace openvdb; + + const float scale = 0.5f; + const Vec3f center(1.0f, 2.0f, 3.0f); + const float voxelSize = 0.025f, width = 2.0f, background = width*voxelSize; + const Coord ijk(int(center[0]/voxelSize), + int(center[1]/voxelSize), + int(center[2]/voxelSize));// inside + + // The tests below are not particularly good (a visual inspection + // in Houdini is much better) but at least it exercises the code + // and performs an elementary suite of tests. + + {// test tetrahedron + FloatGrid::Ptr ls = tools::createLevelSetTetrahedron(scale, center, + voxelSize, width); + CPPUNIT_ASSERT(ls->activeVoxelCount() > 0); + CPPUNIT_ASSERT(ls->tree().isValueOff(ijk)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-ls->background(), ls->tree().getValue(ijk), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, ls->background(), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(ls->background(),ls->tree().getValue(Coord(0)), 1e-6); + } + {// test cube + FloatGrid::Ptr ls = tools::createLevelSetCube(scale, center, + voxelSize, width); + CPPUNIT_ASSERT(ls->activeVoxelCount() > 0); + CPPUNIT_ASSERT(ls->tree().isValueOff(ijk)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-ls->background(),ls->tree().getValue(ijk), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, ls->background(), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(ls->background(),ls->tree().getValue(Coord(0)), 1e-6); + } + {// test octahedron + FloatGrid::Ptr ls = tools::createLevelSetOctahedron(scale, center, + voxelSize, width); + CPPUNIT_ASSERT(ls->activeVoxelCount() > 0); + CPPUNIT_ASSERT(ls->tree().isValueOff(ijk)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-ls->background(),ls->tree().getValue(ijk), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, ls->background(), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(ls->background(),ls->tree().getValue(Coord(0)), 1e-6); + } + {// test icosahedron + FloatGrid::Ptr ls = tools::createLevelSetIcosahedron(scale, center, + voxelSize, width); + CPPUNIT_ASSERT(ls->activeVoxelCount() > 0); + CPPUNIT_ASSERT(ls->tree().isValueOff(ijk)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-ls->background(),ls->tree().getValue(ijk), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, ls->background(), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(ls->background(),ls->tree().getValue(Coord(0)), 1e-6); + } + {// test dodecahedron + FloatGrid::Ptr ls = tools::createLevelSetDodecahedron(scale, center, + voxelSize, width); + CPPUNIT_ASSERT(ls->activeVoxelCount() > 0); + CPPUNIT_ASSERT(ls->tree().isValueOff(ijk)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-ls->background(),ls->tree().getValue(ijk), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, ls->background(), 1e-6); + CPPUNIT_ASSERT_DOUBLES_EQUAL(ls->background(),ls->tree().getValue(Coord(0)), 1e-6); + } + +}// testLevelSetPlatonic + +void +TestTools::testLevelSetAdvect() +{ + // Uncomment sections below to run this (time-consuming) test + using namespace openvdb; + + const int dim = 128; + const Vec3f center(0.35f, 0.35f, 0.35f); + const float radius = 0.15f, voxelSize = 1.0f/(dim-1); + const float halfWidth = 3.0f, gamma = halfWidth*voxelSize; + + using GridT = FloatGrid; + //using VectT = Vec3fGrid; + + {//test tracker::resize + GridT::Ptr grid = tools::createLevelSetSphere(radius, center, voxelSize, halfWidth); + using TrackerT = tools::LevelSetTracker; + TrackerT tracker(*grid); + tracker.setSpatialScheme(math::FIRST_BIAS); + tracker.setTemporalScheme(math::TVD_RK1); + + ASSERT_DOUBLES_EXACTLY_EQUAL( gamma, grid->background()); + ASSERT_DOUBLES_EXACTLY_EQUAL( halfWidth, tracker.getHalfWidth()); + + CPPUNIT_ASSERT(!tracker.resize()); + + {// check range of on values in a sphere w/o mask + tools::CheckRange c(-gamma, gamma); + tools::Diagnose d(*grid); + std::string str = d.check(c); + //std::cerr << "Values out of range:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {// check norm of gradient of sphere w/o mask + tools::CheckNormGrad c(*grid, 0.9f, 1.1f); + tools::Diagnose d(*grid); + std::string str = d.check(c, false, true, false, false); + //std::cerr << "NormGrad:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + + CPPUNIT_ASSERT(tracker.resize(4)); + + ASSERT_DOUBLES_EXACTLY_EQUAL( 4*voxelSize, grid->background()); + ASSERT_DOUBLES_EXACTLY_EQUAL( 4.0f, tracker.getHalfWidth()); + + {// check range of on values in a sphere w/o mask + const float g = gamma + voxelSize; + tools::CheckRange c(-g, g); + tools::Diagnose d(*grid); + std::string str = d.check(c); + //std::cerr << "Values out of range:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + {// check norm of gradient of sphere w/o mask + tools::CheckNormGrad c(*grid, 0.4f, 1.1f); + tools::Diagnose d(*grid); + std::string str = d.check(c, false, true, false, false); + //std::cerr << "NormGrad:\n" << str; + CPPUNIT_ASSERT(str.empty()); + CPPUNIT_ASSERT_EQUAL(0, int(d.valueCount())); + CPPUNIT_ASSERT_EQUAL(0, int(d.failureCount())); + } + } + /* + {//test tracker + GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + using TrackerT = openvdb::tools::LevelSetTracker; + TrackerT tracker(*grid); + tracker.setSpatialScheme(openvdb::math::HJWENO5_BIAS); + tracker.setTemporalScheme(openvdb::math::TVD_RK1); + + FrameWriter fw(dim, grid); fw("Tracker",0, 0); + //for (float t = 0, dt = 0.005f; !grid->empty() && t < 3.0f; t += dt) { + // fw("Enright", t + dt, advect.advect(t, t + dt)); + //} + for (float t = 0, dt = 0.5f; !grid->empty() && t < 1.0f; t += dt) { + tracker.track(); + fw("Tracker", 0, 0); + } + */ + + /* + {//test EnrightField + GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + using FieldT = openvdb::tools::EnrightField; + FieldT field; + + using AdvectT = openvdb::tools::LevelSetAdvection; + AdvectT advect(*grid, field); + advect.setSpatialScheme(openvdb::math::HJWENO5_BIAS); + advect.setTemporalScheme(openvdb::math::TVD_RK2); + advect.setTrackerSpatialScheme(openvdb::math::HJWENO5_BIAS); + advect.setTrackerTemporalScheme(openvdb::math::TVD_RK1); + + FrameWriter fw(dim, grid); fw("Enright",0, 0); + //for (float t = 0, dt = 0.005f; !grid->empty() && t < 3.0f; t += dt) { + // fw("Enright", t + dt, advect.advect(t, t + dt)); + //} + for (float t = 0, dt = 0.5f; !grid->empty() && t < 1.0f; t += dt) { + fw("Enright", t + dt, advect.advect(t, t + dt)); + } + } + */ + /* + {// test DiscreteGrid - Aligned + GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + VectT vect(openvdb::Vec3f(1,0,0)); + using FieldT = openvdb::tools::DiscreteField; + FieldT field(vect); + using AdvectT = openvdb::tools::LevelSetAdvection; + AdvectT advect(*grid, field); + advect.setSpatialScheme(openvdb::math::HJWENO5_BIAS); + advect.setTemporalScheme(openvdb::math::TVD_RK2); + + FrameWriter fw(dim, grid); fw("Aligned",0, 0); + //for (float t = 0, dt = 0.005f; !grid->empty() && t < 3.0f; t += dt) { + // fw("Aligned", t + dt, advect.advect(t, t + dt)); + //} + for (float t = 0, dt = 0.5f; !grid->empty() && t < 1.0f; t += dt) { + fw("Aligned", t + dt, advect.advect(t, t + dt)); + } + } + */ + /* + {// test DiscreteGrid - Transformed + GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + VectT vect(openvdb::Vec3f(0,0,0)); + VectT::Accessor acc = vect.getAccessor(); + for (openvdb::Coord ijk(0); ijk[0]; + FieldT field(vect); + using AdvectT = openvdb::tools::LevelSetAdvection; + AdvectT advect(*grid, field); + advect.setSpatialScheme(openvdb::math::HJWENO5_BIAS); + advect.setTemporalScheme(openvdb::math::TVD_RK2); + + FrameWriter fw(dim, grid); fw("Xformed",0, 0); + //for (float t = 0, dt = 0.005f; !grid->empty() && t < 3.0f; t += dt) { + // fw("Xformed", t + dt, advect.advect(t, t + dt)); + //} + for (float t = 0, dt = 0.5f; !grid->empty() && t < 1.0f; t += dt) { + fw("Xformed", t + dt, advect.advect(t, t + dt)); + } + } + */ +}//testLevelSetAdvect + + +//////////////////////////////////////// + +void +TestTools::testLevelSetMorph() +{ + using GridT = openvdb::FloatGrid; + {//test morphing overlapping but aligned spheres + const int dim = 64; + const openvdb::Vec3f C1(0.35f, 0.35f, 0.35f), C2(0.4f, 0.4f, 0.4f); + const float radius = 0.15f, voxelSize = 1.0f/(dim-1); + + GridT::Ptr source = openvdb::tools::createLevelSetSphere(radius, C1, voxelSize); + GridT::Ptr target = openvdb::tools::createLevelSetSphere(radius, C2, voxelSize); + + using MorphT = openvdb::tools::LevelSetMorphing; + MorphT morph(*source, *target); + morph.setSpatialScheme(openvdb::math::HJWENO5_BIAS); + morph.setTemporalScheme(openvdb::math::TVD_RK3); + morph.setTrackerSpatialScheme(openvdb::math::HJWENO5_BIAS); + morph.setTrackerTemporalScheme(openvdb::math::TVD_RK2); + + const std::string name("SphereToSphere"); + //FrameWriter fw(dim, source); + //fw(name, 0.0f, 0); + //util::CpuTimer timer; + const float tMax = 0.05f/voxelSize; + //std::cerr << "\nt-max = " << tMax << std::endl; + //timer.start("\nMorphing"); + for (float t = 0, dt = 0.1f; !source->empty() && t < tMax; t += dt) { + morph.advect(t, t + dt); + //fw(name, t + dt, morph.advect(t, t + dt)); + } + // timer.stop(); + + const float invDx = 1.0f/voxelSize; + openvdb::math::Stats s; + for (GridT::ValueOnCIter it = source->tree().cbeginValueOn(); it; ++it) { + s.add( invDx*(*it - target->tree().getValue(it.getCoord())) ); + } + for (GridT::ValueOnCIter it = target->tree().cbeginValueOn(); it; ++it) { + s.add( invDx*(*it - target->tree().getValue(it.getCoord())) ); + } + //s.print("Morph"); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, s.min(), 0.50); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, s.max(), 0.50); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, s.avg(), 0.02); + /* + openvdb::math::Histogram h(s, 30); + for (GridT::ValueOnCIter it = source->tree().cbeginValueOn(); it; ++it) { + h.add( invDx*(*it - target->tree().getValue(it.getCoord())) ); + } + for (GridT::ValueOnCIter it = target->tree().cbeginValueOn(); it; ++it) { + h.add( invDx*(*it - target->tree().getValue(it.getCoord())) ); + } + h.print("Morph"); + */ + } + /* + // Uncomment sections below to run this (very time-consuming) test + {//test morphing between the bunny and the buddha models loaded from files + util::CpuTimer timer; + openvdb::initialize();//required whenever I/O of OpenVDB files is performed! + openvdb::io::File sourceFile("/usr/pic1/Data/OpenVDB/LevelSetModels/bunny.vdb"); + sourceFile.open(); + GridT::Ptr source = openvdb::gridPtrCast(sourceFile.getGrids()->at(0)); + + openvdb::io::File targetFile("/usr/pic1/Data/OpenVDB/LevelSetModels/buddha.vdb"); + targetFile.open(); + GridT::Ptr target = openvdb::gridPtrCast(targetFile.getGrids()->at(0)); + + using MorphT = openvdb::tools::LevelSetMorphing; + MorphT morph(*source, *target); + morph.setSpatialScheme(openvdb::math::FIRST_BIAS); + //morph.setSpatialScheme(openvdb::math::HJWENO5_BIAS); + morph.setTemporalScheme(openvdb::math::TVD_RK2); + morph.setTrackerSpatialScheme(openvdb::math::FIRST_BIAS); + //morph.setTrackerSpatialScheme(openvdb::math::HJWENO5_BIAS); + morph.setTrackerTemporalScheme(openvdb::math::TVD_RK2); + + const std::string name("Bunny2Buddha"); + FrameWriter fw(1, source); + fw(name, 0.0f, 0); + for (float t = 0, dt = 1.0f; !source->empty() && t < 300.0f; t += dt) { + timer.start("Morphing"); + const int cflCount = morph.advect(t, t + dt); + timer.stop(); + fw(name, t + dt, cflCount); + } + } + */ + /* + // Uncomment sections below to run this (very time-consuming) test + {//test morphing between the dragon and the teapot models loaded from files + util::CpuTimer timer; + openvdb::initialize();//required whenever I/O of OpenVDB files is performed! + openvdb::io::File sourceFile("/usr/pic1/Data/OpenVDB/LevelSetModels/dragon.vdb"); + sourceFile.open(); + GridT::Ptr source = openvdb::gridPtrCast(sourceFile.getGrids()->at(0)); + + openvdb::io::File targetFile("/usr/pic1/Data/OpenVDB/LevelSetModels/utahteapot.vdb"); + targetFile.open(); + GridT::Ptr target = openvdb::gridPtrCast(targetFile.getGrids()->at(0)); + + using MorphT = openvdb::tools::LevelSetMorphing; + MorphT morph(*source, *target); + morph.setSpatialScheme(openvdb::math::FIRST_BIAS); + //morph.setSpatialScheme(openvdb::math::HJWENO5_BIAS); + morph.setTemporalScheme(openvdb::math::TVD_RK2); + //morph.setTrackerSpatialScheme(openvdb::math::HJWENO5_BIAS); + morph.setTrackerSpatialScheme(openvdb::math::FIRST_BIAS); + morph.setTrackerTemporalScheme(openvdb::math::TVD_RK2); + + const std::string name("Dragon2Teapot"); + FrameWriter fw(5, source); + fw(name, 0.0f, 0); + for (float t = 0, dt = 0.4f; !source->empty() && t < 110.0f; t += dt) { + timer.start("Morphing"); + const int cflCount = morph.advect(t, t + dt); + timer.stop(); + fw(name, t + dt, cflCount); + } + } + + */ +}//testLevelSetMorph + +//////////////////////////////////////// + +void +TestTools::testLevelSetMeasure() +{ + const double percentage = 0.1/100.0;//i.e. 0.1% + using GridT = openvdb::FloatGrid; + const int dim = 256; + openvdb::Real area, volume, mean, gauss; + + // First sphere + openvdb::Vec3f C(0.35f, 0.35f, 0.35f); + openvdb::Real r = 0.15, voxelSize = 1.0/(dim-1); + const openvdb::Real Pi = boost::math::constants::pi(); + GridT::Ptr sphere = openvdb::tools::createLevelSetSphere(float(r), C, float(voxelSize)); + + using MeasureT = openvdb::tools::LevelSetMeasure; + MeasureT m(*sphere); + + /// Test area and volume of sphere in world units + area = 4*Pi*r*r; + volume = 4.0/3.0*Pi*r*r*r; + //std::cerr << "\nArea of sphere = " << area << " " << a << std::endl; + //std::cerr << "\nVolume of sphere = " << volume << " " << v << std::endl; + // Test accuracy of computed measures to within 0.1% of the exact measure. + CPPUNIT_ASSERT_DOUBLES_EQUAL(area, m.area(), percentage*area); + CPPUNIT_ASSERT_DOUBLES_EQUAL(volume, m.volume(), percentage*volume); + + // Test area, volume and average mean curvature of sphere in world units + mean = 1.0/r; + //std::cerr << "\nArea of sphere = " << area << " " << a << std::endl; + //std::cerr << "Volume of sphere = " << volume << " " << v << std::endl; + //std::cerr << "radius in world units = " << r << std::endl; + //std::cerr << "Avg mean curvature of sphere = " << mean << " " << cm << std::endl; + // Test accuracy of computed measures to within 0.1% of the exact measure. + CPPUNIT_ASSERT_DOUBLES_EQUAL(area, m.area(), percentage*area); + CPPUNIT_ASSERT_DOUBLES_EQUAL(volume, m.volume(), percentage*volume); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mean, m.avgMeanCurvature(), percentage*mean); + + // Test area, volume, average mean curvature and average gaussian curvature of sphere in world units + gauss = 1.0/(r*r); + //std::cerr << "\nArea of sphere = " << area << " " << a << std::endl; + //std::cerr << "Volume of sphere = " << volume << " " << v << std::endl; + //std::cerr << "radius in world units = " << r << std::endl; + //std::cerr << "Avg mean curvature of sphere = " << mean << " " << cm << std::endl; + //std::cerr << "Avg gaussian curvature of sphere = " << gauss << " " << cg << std::endl; + // Test accuracy of computed measures to within 0.1% of the exact measure. + CPPUNIT_ASSERT_DOUBLES_EQUAL(area, m.area(), percentage*area); + CPPUNIT_ASSERT_DOUBLES_EQUAL(volume, m.volume(), percentage*volume); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mean, m.avgMeanCurvature(), percentage*mean); + CPPUNIT_ASSERT_DOUBLES_EQUAL(gauss, m.avgGaussianCurvature(), percentage*gauss); + CPPUNIT_ASSERT_EQUAL(0, m.genus()); + + // Test measures of sphere in voxel units + r /= voxelSize; + area = 4*Pi*r*r; + volume = 4.0/3.0*Pi*r*r*r; + mean = 1.0/r; + //std::cerr << "\nArea of sphere = " << area << " " << a << std::endl; + //std::cerr << "Volume of sphere = " << volume << " " << v << std::endl; + //std::cerr << "Avg mean curvature of sphere = " << curv << " " << cm << std::endl; + // Test accuracy of computed measures to within 0.1% of the exact measure. + CPPUNIT_ASSERT_DOUBLES_EQUAL(area, m.area(false), percentage*area); + CPPUNIT_ASSERT_DOUBLES_EQUAL(volume, m.volume(false), percentage*volume); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mean, m.avgMeanCurvature(false), percentage*mean); + + gauss = 1.0/(r*r); + //std::cerr << "\nArea of sphere = " << area << " " << a << std::endl; + //std::cerr << "Volume of sphere = " << volume << " " << v << std::endl; + //std::cerr << "radius in voxel units = " << r << std::endl; + //std::cerr << "Avg mean curvature of sphere = " << mean << " " << cm << std::endl; + //std::cerr << "Avg gaussian curvature of sphere = " << gauss << " " << cg << std::endl; + // Test accuracy of computed measures to within 0.1% of the exact measure. + CPPUNIT_ASSERT_DOUBLES_EQUAL(area, m.area(false), percentage*area); + CPPUNIT_ASSERT_DOUBLES_EQUAL(volume, m.volume(false), percentage*volume); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mean, m.avgMeanCurvature(false), percentage*mean); + CPPUNIT_ASSERT_DOUBLES_EQUAL(gauss, m.avgGaussianCurvature(false), percentage*gauss); + CPPUNIT_ASSERT_EQUAL(0, m.genus()); + + // Second sphere + C = openvdb::Vec3f(5.4f, 6.4f, 8.4f); + r = 0.57; + sphere = openvdb::tools::createLevelSetSphere(float(r), C, float(voxelSize)); + m.init(*sphere); + + // Test all measures of sphere in world units + area = 4*Pi*r*r; + volume = 4.0/3.0*Pi*r*r*r; + mean = 1.0/r; + gauss = 1.0/(r*r); + //std::cerr << "\nArea of sphere = " << area << " " << a << std::endl; + //std::cerr << "Volume of sphere = " << volume << " " << v << std::endl; + //std::cerr << "radius in world units = " << r << std::endl; + //std::cerr << "Avg mean curvature of sphere = " << mean << " " << cm << std::endl; + //std::cerr << "Avg gaussian curvature of sphere = " << gauss << " " << cg << std::endl; + // Test accuracy of computed measures to within 0.1% of the exact measure. + CPPUNIT_ASSERT_DOUBLES_EQUAL(area, m.area(), percentage*area); + CPPUNIT_ASSERT_DOUBLES_EQUAL(volume, m.volume(), percentage*volume); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mean, m.avgMeanCurvature(), percentage*mean); + CPPUNIT_ASSERT_DOUBLES_EQUAL(gauss, m.avgGaussianCurvature(), percentage*gauss); + CPPUNIT_ASSERT_EQUAL(0, m.genus()); + //CPPUNIT_ASSERT_DOUBLES_EQUAL(area, openvdb::tools::levelSetArea(*sphere), percentage*area); + //CPPUNIT_ASSERT_DOUBLES_EQUAL(volume,openvdb::tools::levelSetVolume(*sphere),percentage*volume); + //CPPUNIT_ASSERT_EQUAL(0, openvdb::tools::levelSetGenus(*sphere)); + + // Test all measures of sphere in voxel units + r /= voxelSize; + area = 4*Pi*r*r; + volume = 4.0/3.0*Pi*r*r*r; + mean = 1.0/r; + gauss = 1.0/(r*r); + //std::cerr << "\nArea of sphere = " << area << " " << a << std::endl; + //std::cerr << "Volume of sphere = " << volume << " " << v << std::endl; + //std::cerr << "radius in voxel units = " << r << std::endl; + //std::cerr << "Avg mean curvature of sphere = " << mean << " " << cm << std::endl; + //std::cerr << "Avg gaussian curvature of sphere = " << gauss << " " << cg << std::endl; + // Test accuracy of computed measures to within 0.1% of the exact measure. + CPPUNIT_ASSERT_DOUBLES_EQUAL(area, m.area(false), percentage*area); + CPPUNIT_ASSERT_DOUBLES_EQUAL(volume, m.volume(false), percentage*volume); + CPPUNIT_ASSERT_DOUBLES_EQUAL(mean, m.avgMeanCurvature(false), percentage*mean); + CPPUNIT_ASSERT_DOUBLES_EQUAL(gauss, m.avgGaussianCurvature(false), percentage*gauss); + CPPUNIT_ASSERT_DOUBLES_EQUAL(area, openvdb::tools::levelSetArea(*sphere,false), + percentage*area); + CPPUNIT_ASSERT_DOUBLES_EQUAL(volume,openvdb::tools::levelSetVolume(*sphere,false), + percentage*volume); + CPPUNIT_ASSERT_EQUAL(0, openvdb::tools::levelSetGenus(*sphere)); + + // Read level set from file + /* + util::CpuTimer timer; + openvdb::initialize();//required whenever I/O of OpenVDB files is performed! + openvdb::io::File sourceFile("/usr/pic1/Data/OpenVDB/LevelSetModels/venusstatue.vdb"); + sourceFile.open(); + GridT::Ptr model = openvdb::gridPtrCast(sourceFile.getGrids()->at(0)); + m.reinit(*model); + + //m.setGrainSize(1); + timer.start("\nParallel measure of area and volume"); + m.measure(a, v, false); + timer.stop(); + std::cerr << "Model: area = " << a << ", volume = " << v << std::endl; + + timer.start("\nParallel measure of area, volume and curvature"); + m.measure(a, v, c, false); + timer.stop(); + std::cerr << "Model: area = " << a << ", volume = " << v + << ", average curvature = " << c << std::endl; + + m.setGrainSize(0); + timer.start("\nSerial measure of area and volume"); + m.measure(a, v, false); + timer.stop(); + std::cerr << "Model: area = " << a << ", volume = " << v << std::endl; + + timer.start("\nSerial measure of area, volume and curvature"); + m.measure(a, v, c, false); + timer.stop(); + std::cerr << "Model: area = " << a << ", volume = " << v + << ", average curvature = " << c << std::endl; + */ + + {// testing total genus of multiple disjoint level set spheres with different radius + const float dx = 0.5f, r = 50.0f; + auto grid = openvdb::createLevelSet(dx); + CPPUNIT_ASSERT_THROW(openvdb::tools::levelSetGenus(*grid), openvdb::RuntimeError); + for (int i=1; i<=3; ++i) { + auto sphere = openvdb::tools::createLevelSetSphere(r+float(i)*5.0f , openvdb::Vec3f(100.0f*float(i)), dx); + openvdb::tools::csgUnion(*grid, *sphere); + const int x = openvdb::tools::levelSetEulerCharacteristic(*grid);// since they are not overlapping re-normalization is not required + //std::cerr << "Euler characteristics of " << i << " sphere(s) = " << x << std::endl; + CPPUNIT_ASSERT_EQUAL(2*i, x); + } + } + {// testing total genus of multiple disjoint level set cubes of different size + const float dx = 0.5f, size = 50.0f; + auto grid = openvdb::createLevelSet(dx); + CPPUNIT_ASSERT_THROW(openvdb::tools::levelSetGenus(*grid), openvdb::RuntimeError); + for (int i=1; i<=2; ++i) { + auto shape = openvdb::tools::createLevelSetCube(size, openvdb::Vec3f(100.0f*float(i)), dx); + openvdb::tools::csgUnion(*grid, *shape); + const int x = openvdb::tools::levelSetEulerCharacteristic(*grid); + //std::cerr << "Euler characteristics of " << i << " cubes(s) = " << x << std::endl; + CPPUNIT_ASSERT_EQUAL(2*i, x); + } + } + {// testing Euler characteristic and total genus of multiple intersecting (connected) level set spheres + const float dx = 0.5f, r = 50.0f; + auto grid = openvdb::createLevelSet(dx); + CPPUNIT_ASSERT_THROW(openvdb::tools::levelSetGenus(*grid), openvdb::RuntimeError); + for (int i=1; i<=4; ++i) { + auto sphere = openvdb::tools::createLevelSetSphere( r , openvdb::Vec3f(30.0f*float(i), 0.0f, 0.0f), dx); + openvdb::tools::csgUnion(*grid, *sphere); + const int genus = openvdb::tools::levelSetGenus(*grid); + const int x = openvdb::tools::levelSetEulerCharacteristic(*grid); + //std::cerr << "Genus of " << i << " sphere(s) = " << genus << std::endl; + CPPUNIT_ASSERT_EQUAL(0, genus); + //std::cerr << "Euler characteristics of " << i << " sphere(s) = " << genus << std::endl; + CPPUNIT_ASSERT_EQUAL(2, x); + } + } + +}//testLevelSetMeasure + +void +TestTools::testMagnitude() +{ + using namespace openvdb; + { + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Coord dim(64,64,64); + const Vec3f center(35.0f, 30.0f, 40.0f); + const float radius=0.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + VectorGrid::Ptr gradGrid = tools::gradient(*grid); + CPPUNIT_ASSERT_EQUAL(int(tree.activeVoxelCount()), int(gradGrid->activeVoxelCount())); + + FloatGrid::Ptr mag = tools::magnitude(*gradGrid); + CPPUNIT_ASSERT_EQUAL(int(tree.activeVoxelCount()), int(mag->activeVoxelCount())); + + FloatGrid::ConstAccessor accessor = mag->getConstAccessor(); + + Coord xyz(35,30,30); + float v = accessor.getValue(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, v, 0.01); + + xyz.reset(35,10,40); + v = accessor.getValue(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, v, 0.01); + } + { + // Test on a grid with (only) tile values. + + Vec3fGrid grid; + Vec3fTree& tree = grid.tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Vec3f v(1.f, 2.f, 2.f); + const float expectedLength = v.length(); + + tree.addTile(/*level=*/1, Coord(-100), v, /*active=*/true); + tree.addTile(/*level=*/1, Coord(100), v, /*active=*/true); + + CPPUNIT_ASSERT(!tree.empty()); + + FloatGrid::Ptr length = tools::magnitude(grid); + + CPPUNIT_ASSERT_EQUAL(int(tree.activeVoxelCount()), int(length->activeVoxelCount())); + + for (auto it = length->cbeginValueOn(); it; ++it) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(expectedLength, *it, 1.0e-6); + } + } +} + + +void +TestTools::testMaskedMagnitude() +{ + using namespace openvdb; + { + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/5.0); + FloatTree& tree = grid->tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Coord dim(64,64,64); + const Vec3f center(35.0f, 30.0f, 40.0f); + const float radius=0.0f; + unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT(!tree.empty()); + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + + VectorGrid::Ptr gradGrid = tools::gradient(*grid); + CPPUNIT_ASSERT_EQUAL(int(tree.activeVoxelCount()), int(gradGrid->activeVoxelCount())); + + // create a masking grid + const CoordBBox maskbbox(Coord(35, 30, 30), Coord(41, 41, 41)); + BoolGrid::Ptr maskGrid = BoolGrid::create(false); + maskGrid->fill(maskbbox, true/*value*/, true/*activate*/); + + // compute the magnitude in masked region + FloatGrid::Ptr mag = tools::magnitude(*gradGrid, *maskGrid); + + FloatGrid::ConstAccessor accessor = mag->getConstAccessor(); + + // test in the masked region + Coord xyz(35,30,30); + CPPUNIT_ASSERT(maskbbox.isInside(xyz)); + float v = accessor.getValue(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, v, 0.01); + + // test outside the masked region + xyz.reset(35,10,40); + CPPUNIT_ASSERT(!maskbbox.isInside(xyz)); + v = accessor.getValue(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, v, 0.01); + } + { + // Test on a grid with (only) tile values. + + Vec3fGrid grid; + Vec3fTree& tree = grid.tree(); + CPPUNIT_ASSERT(tree.empty()); + + const Vec3f v(1.f, 2.f, 2.f); + const float expectedLength = v.length(); + + tree.addTile(/*level=*/1, Coord(100), v, /*active=*/true); + const int expectedActiveVoxelCount = int(tree.activeVoxelCount()); + tree.addTile(/*level=*/1, Coord(-100), v, /*active=*/true); + + CPPUNIT_ASSERT(!tree.empty()); + + BoolGrid mask; + mask.fill(CoordBBox(Coord(90), Coord(200)), true, true); + + FloatGrid::Ptr length = tools::magnitude(grid, mask); + + CPPUNIT_ASSERT_EQUAL(expectedActiveVoxelCount, int(length->activeVoxelCount())); + + for (auto it = length->cbeginValueOn(); it; ++it) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(expectedLength, *it, 1.0e-6); + } + } +} + + +void +TestTools::testNormalize() +{ + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(5.0); + openvdb::FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f); + const float radius=10.0f; + unittest_util::makeSphere( + dim,center,radius,*grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + openvdb::Coord xyz(10, 20, 30); + + openvdb::VectorGrid::Ptr grad = openvdb::tools::gradient(*grid); + + using Vec3Type = openvdb::VectorGrid::ValueType; + + using ValueIter = openvdb::VectorGrid::ValueOnIter; + + struct Local { + static inline Vec3Type op(const Vec3Type &x) { return x * 2.0f; } + static inline void visit(const ValueIter& it) { it.setValue(op(*it)); } + }; + + openvdb::tools::foreach(grad->beginValueOn(), Local::visit, true); + + openvdb::VectorGrid::ConstAccessor accessor = grad->getConstAccessor(); + + xyz = openvdb::Coord(35,10,40); + Vec3Type v = accessor.getValue(xyz); + //std::cerr << "\nPassed testNormalize(" << xyz << ")=" << v.length() << std::endl; + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0,v.length(),0.001); + openvdb::VectorGrid::Ptr norm = openvdb::tools::normalize(*grad); + + accessor = norm->getConstAccessor(); + v = accessor.getValue(xyz); + //std::cerr << "\nPassed testNormalize(" << xyz << ")=" << v.length() << std::endl; + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, v.length(), 0.0001); +} + + +void +TestTools::testMaskedNormalize() +{ + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(5.0); + openvdb::FloatTree& tree = grid->tree(); + + const openvdb::Coord dim(64,64,64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f); + const float radius=10.0f; + unittest_util::makeSphere( + dim,center,radius,*grid, unittest_util::SPHERE_DENSE); + + CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(tree.activeVoxelCount())); + openvdb::Coord xyz(10, 20, 30); + + openvdb::VectorGrid::Ptr grad = openvdb::tools::gradient(*grid); + + using Vec3Type = openvdb::VectorGrid::ValueType; + + using ValueIter = openvdb::VectorGrid::ValueOnIter; + + struct Local { + static inline Vec3Type op(const Vec3Type &x) { return x * 2.0f; } + static inline void visit(const ValueIter& it) { it.setValue(op(*it)); } + }; + + openvdb::tools::foreach(grad->beginValueOn(), Local::visit, true); + + openvdb::VectorGrid::ConstAccessor accessor = grad->getConstAccessor(); + + xyz = openvdb::Coord(35,10,40); + Vec3Type v = accessor.getValue(xyz); + + // create a masking grid + + const openvdb::CoordBBox maskbbox(openvdb::Coord(35, 30, 30), openvdb::Coord(41, 41, 41)); + openvdb::BoolGrid::Ptr maskGrid = openvdb::BoolGrid::create(false); + maskGrid->fill(maskbbox, true/*value*/, true/*activate*/); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0,v.length(),0.001); + + // compute the normalized valued in the masked region + openvdb::VectorGrid::Ptr norm = openvdb::tools::normalize(*grad, *maskGrid); + + accessor = norm->getConstAccessor(); + { // outside the masked region + CPPUNIT_ASSERT(!maskbbox.isInside(xyz)); + v = accessor.getValue(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, v.length(), 0.0001); + } + { // inside the masked region + xyz.reset(35, 30, 30); + v = accessor.getValue(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, v.length(), 0.0001); + } +} + +//////////////////////////////////////// + + +void +TestTools::testPointAdvect() +{ + { + // Setup: Advect a number of points in a uniform velocity field (1,1,1). + // over a time dt=1 with each of the 4 different advection schemes. + // Points initialized at latice points. + // + // Uses: FloatTree (velocity), collocated sampling, advection + // + // Expected: All advection schemes will have the same result. Each point will + // be advanced to a new latice point. The i-th point will be at (i+1,i+1,i+1) + // + + const size_t numPoints = 2000000; + + // create a uniform velocity field in SINGLE PRECISION + const openvdb::Vec3f velocityBackground(1, 1, 1); + openvdb::Vec3fGrid::Ptr velocityGrid = openvdb::Vec3fGrid::create(velocityBackground); + + // using all the default template arguments + openvdb::tools::PointAdvect<> advectionTool(*velocityGrid); + + // create points + std::vector pointList(numPoints); /// larger than the tbb chunk size + for (size_t i = 0; i < numPoints; i++) { + pointList[i] = openvdb::Vec3f(float(i), float(i), float(i)); + } + + for (unsigned int order = 1; order < 5; ++order) { + // check all four time integrations schemes + // construct an advection tool. By default the number of cpt iterations is zero + advectionTool.setIntegrationOrder(order); + advectionTool.advect(pointList, /*dt=*/1.0, /*iterations=*/1); + + // check locations + for (size_t i = 0; i < numPoints; i++) { + openvdb::Vec3f expected(float(i + 1), float(i + 1), float(i + 1)); + CPPUNIT_ASSERT_EQUAL(expected, pointList[i]); + } + // reset values + for (size_t i = 0; i < numPoints; i++) { + pointList[i] = openvdb::Vec3f(float(i), float(i), float(i)); + } + } + + } + + { + // Setup: Advect a number of points in a uniform velocity field (1,1,1). + // over a time dt=1 with each of the 4 different advection schemes. + // And then project the point location onto the x-y plane + // Points initialized at latice points. + // + // Uses: DoubleTree (velocity), staggered sampling, constraint projection, advection + // + // Expected: All advection schemes will have the same result. Modes 1-4: Each point will + // be advanced to a new latice point and projected to x-y plane. + // The i-th point will be at (i+1,i+1,0). For mode 0 (no advection), i-th point + // will be found at (i, i, 0) + + const size_t numPoints = 4; + + // create a uniform velocity field in DOUBLE PRECISION + const openvdb::Vec3d velocityBackground(1, 1, 1); + openvdb::Vec3dGrid::Ptr velocityGrid = openvdb::Vec3dGrid::create(velocityBackground); + + // create a simple (horizontal) constraint field valid for a + // (-10,10)x(-10,10)x(-10,10) + const openvdb::Vec3d cptBackground(0, 0, 0); + openvdb::Vec3dGrid::Ptr cptGrid = openvdb::Vec3dGrid::create(cptBackground); + openvdb::Vec3dTree& cptTree = cptGrid->tree(); + + // create points + std::vector pointList(numPoints); + for (unsigned int i = 0; i < numPoints; i++) pointList[i] = openvdb::Vec3d(i, i, i); + + // Initialize the constraint field in a [-10,10]x[-10,10]x[-10,10] box + // this test will only work if the points remain in the box + openvdb::Coord ijk(0, 0, 0); + for (int i = -10; i < 11; i++) { + ijk.setX(i); + for (int j = -10; j < 11; j++) { + ijk.setY(j); + for (int k = -10; k < 11; k++) { + ijk.setZ(k); + // set the value as projection onto the x-y plane + cptTree.setValue(ijk, openvdb::Vec3d(i, j, 0)); + } + } + } + + // construct an advection tool. By default the number of cpt iterations is zero + openvdb::tools::ConstrainedPointAdvect, true> constrainedAdvectionTool(*velocityGrid, *cptGrid, 0); + constrainedAdvectionTool.setThreaded(false); + + // change the number of constraint interation from default 0 to 5 + constrainedAdvectionTool.setConstraintIterations(5); + + // test the pure-projection mode (order = 0) + constrainedAdvectionTool.setIntegrationOrder(0); + + // change the number of constraint interation (from 0 to 5) + constrainedAdvectionTool.setConstraintIterations(5); + + constrainedAdvectionTool.advect(pointList, /*dt=*/1.0, /*iterations=*/1); + + // check locations + for (unsigned int i = 0; i < numPoints; i++) { + openvdb::Vec3d expected(i, i, 0); // location (i, i, i) projected on to x-y plane + for (int n=0; n<3; ++n) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected[n], pointList[i][n], /*tolerance=*/1e-6); + } + } + + // reset values + for (unsigned int i = 0; i < numPoints; i++) pointList[i] = openvdb::Vec3d(i, i, i); + + // test all four time integrations schemes + for (unsigned int order = 1; order < 5; ++order) { + + constrainedAdvectionTool.setIntegrationOrder(order); + + constrainedAdvectionTool.advect(pointList, /*dt=*/1.0, /*iterations=*/1); + + // check locations + for (unsigned int i = 0; i < numPoints; i++) { + openvdb::Vec3d expected(i+1, i+1, 0); // location (i,i,i) projected onto x-y plane + for (int n=0; n<3; ++n) { + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected[n], pointList[i][n], /*tolerance=*/1e-6); + } + } + // reset values + for (unsigned int i = 0; i < numPoints; i++) pointList[i] = openvdb::Vec3d(i, i, i); + } + } +} + + +//////////////////////////////////////// + + +namespace { + + struct PointList + { + struct Point { double x,y,z; }; + std::vector list; + openvdb::Index64 size() const { return openvdb::Index64(list.size()); } + void add(const openvdb::Vec3d &p) { Point q={p[0],p[1],p[2]}; list.push_back(q); } + }; +} + + +void +TestTools::testPointScatter() +{ + using GridType = openvdb::FloatGrid; + const openvdb::Coord dim(64, 64, 64); + const openvdb::Vec3f center(35.0f, 30.0f, 40.0f); + const float radius = 20.0; + using RandGen = std::mersenne_twister_engine; // mt11213b + RandGen mtRand; + + GridType::Ptr grid = GridType::create(/*background=*/2.0); + unittest_util::makeSphere( + dim, center, radius, *grid, unittest_util::SPHERE_DENSE_NARROW_BAND); + + {// test fixed point count scattering + const openvdb::Index64 pointCount = 1000; + PointList points; + openvdb::tools::UniformPointScatter scatter(points, pointCount, mtRand); + scatter.operator()(*grid); + CPPUNIT_ASSERT_EQUAL( pointCount, scatter.getPointCount() ); + CPPUNIT_ASSERT_EQUAL( pointCount, points.size() ); + } + {// test uniform density scattering + const float density = 1.0f;//per volume = per voxel since voxel size = 1 + PointList points; + openvdb::tools::UniformPointScatter scatter(points, density, mtRand); + scatter.operator()(*grid); + CPPUNIT_ASSERT_EQUAL( scatter.getVoxelCount(), scatter.getPointCount() ); + CPPUNIT_ASSERT_EQUAL( scatter.getVoxelCount(), points.size() ); + } + {// test non-uniform density scattering + const float density = 1.0f;//per volume = per voxel since voxel size = 1 + PointList points; + openvdb::tools::NonUniformPointScatter scatter(points, density, mtRand); + scatter.operator()(*grid); + CPPUNIT_ASSERT( scatter.getVoxelCount() < scatter.getPointCount() ); + CPPUNIT_ASSERT_EQUAL( scatter.getPointCount(), points.size() ); + } + {// test dense uniform scattering + const size_t pointsPerVoxel = 8; + PointList points; + openvdb::tools::DenseUniformPointScatter + scatter(points, pointsPerVoxel, mtRand); + scatter.operator()(*grid); + CPPUNIT_ASSERT_EQUAL( scatter.getVoxelCount()*pointsPerVoxel, scatter.getPointCount() ); + CPPUNIT_ASSERT_EQUAL( scatter.getPointCount(), points.size() ); + } +} + +//////////////////////////////////////// + +void +TestTools::testVolumeAdvect() +{ + using namespace openvdb; + + Vec3fGrid velocity(Vec3f(1.0f, 0.0f, 0.0f)); + using GridT = FloatGrid; + using AdvT = tools::VolumeAdvection; + using SamplerT = tools::Sampler<1>; + + {//test non-uniform grids (throws) + GridT::Ptr density0 = GridT::create(0.0f); + density0->transform().preScale(Vec3d(1.0, 2.0, 3.0));//i.e. non-uniform voxels + AdvT a(velocity); + CPPUNIT_ASSERT_THROW((a.advect(*density0, 0.1f)), RuntimeError); + } + + {// test spatialOrder and temporalOrder + AdvT a(velocity); + + // Default should be SEMI + CPPUNIT_ASSERT_EQUAL(1, a.spatialOrder()); + CPPUNIT_ASSERT_EQUAL(1, a.temporalOrder()); + CPPUNIT_ASSERT(!a.isLimiterOn()); + + a.setIntegrator(tools::Scheme::SEMI); + CPPUNIT_ASSERT_EQUAL(1, a.spatialOrder()); + CPPUNIT_ASSERT_EQUAL(1, a.temporalOrder()); + CPPUNIT_ASSERT(!a.isLimiterOn()); + + a.setIntegrator(tools::Scheme::MID); + CPPUNIT_ASSERT_EQUAL(1, a.spatialOrder()); + CPPUNIT_ASSERT_EQUAL(2, a.temporalOrder()); + CPPUNIT_ASSERT(!a.isLimiterOn()); + + a.setIntegrator(tools::Scheme::RK3); + CPPUNIT_ASSERT_EQUAL(1, a.spatialOrder()); + CPPUNIT_ASSERT_EQUAL(3, a.temporalOrder()); + CPPUNIT_ASSERT(!a.isLimiterOn()); + + a.setIntegrator(tools::Scheme::RK4); + CPPUNIT_ASSERT_EQUAL(1, a.spatialOrder()); + CPPUNIT_ASSERT_EQUAL(4, a.temporalOrder()); + CPPUNIT_ASSERT(!a.isLimiterOn()); + + a.setIntegrator(tools::Scheme::MAC); + CPPUNIT_ASSERT_EQUAL(2, a.spatialOrder()); + CPPUNIT_ASSERT_EQUAL(2, a.temporalOrder()); + CPPUNIT_ASSERT( a.isLimiterOn()); + + a.setIntegrator(tools::Scheme::BFECC); + CPPUNIT_ASSERT_EQUAL(2, a.spatialOrder()); + CPPUNIT_ASSERT_EQUAL(2, a.temporalOrder()); + CPPUNIT_ASSERT( a.isLimiterOn()); + + a.setLimiter(tools::Scheme::NO_LIMITER); + CPPUNIT_ASSERT_EQUAL(2, a.spatialOrder()); + CPPUNIT_ASSERT_EQUAL(2, a.temporalOrder()); + CPPUNIT_ASSERT(!a.isLimiterOn()); + } + + {//test RK4 advect without a mask + GridT::Ptr density0 = GridT::create(0.0f), density1; + density0->fill(CoordBBox(Coord(0),Coord(6)), 1.0f); + CPPUNIT_ASSERT_EQUAL(density0->tree().getValue(Coord( 3,3,3)), 1.0f); + CPPUNIT_ASSERT_EQUAL(density0->tree().getValue(Coord(24,3,3)), 0.0f); + CPPUNIT_ASSERT( density0->tree().isValueOn(Coord( 3,3,3))); + CPPUNIT_ASSERT(!density0->tree().isValueOn(Coord(24,3,3))); + + AdvT a(velocity); + a.setIntegrator(tools::Scheme::RK4); + for (int i=1; i<=240; ++i) { + density1 = a.advect(*density0, 0.1f); + //std::ostringstream ostr; + //ostr << "densityAdvect" << "_" << i << ".vdb"; + //std::cerr << "Writing " << ostr.str() << std::endl; + //openvdb::io::File file(ostr.str()); + //openvdb::GridPtrVec grids; + //grids.push_back(density1); + //file.write(grids); + density0 = density1; + } + CPPUNIT_ASSERT_EQUAL(density0->tree().getValue(Coord(3,3,3)), 0.0f); + CPPUNIT_ASSERT(density0->tree().getValue(Coord(24,3,3)) > 0.0f); + CPPUNIT_ASSERT(!density0->tree().isValueOn(Coord( 3,3,3))); + CPPUNIT_ASSERT( density0->tree().isValueOn(Coord(24,3,3))); + } + {//test MAC advect without a mask + GridT::Ptr density0 = GridT::create(0.0f), density1; + density0->fill(CoordBBox(Coord(0),Coord(6)), 1.0f); + CPPUNIT_ASSERT_EQUAL(density0->tree().getValue(Coord( 3,3,3)), 1.0f); + CPPUNIT_ASSERT_EQUAL(density0->tree().getValue(Coord(24,3,3)), 0.0f); + CPPUNIT_ASSERT( density0->tree().isValueOn(Coord( 3,3,3))); + CPPUNIT_ASSERT(!density0->tree().isValueOn(Coord(24,3,3))); + + AdvT a(velocity); + a.setIntegrator(tools::Scheme::BFECC); + for (int i=1; i<=240; ++i) { + density1 = a.advect(*density0, 0.1f); + //std::ostringstream ostr; + //ostr << "densityAdvect" << "_" << i << ".vdb"; + //std::cerr << "Writing " << ostr.str() << std::endl; + //openvdb::io::File file(ostr.str()); + //openvdb::GridPtrVec grids; + //grids.push_back(density1); + //file.write(grids); + density0 = density1; + } + CPPUNIT_ASSERT_EQUAL(density0->tree().getValue(Coord(3,3,3)), 0.0f); + CPPUNIT_ASSERT(density0->tree().getValue(Coord(24,3,3)) > 0.0f); + CPPUNIT_ASSERT(!density0->tree().isValueOn(Coord( 3,3,3))); + CPPUNIT_ASSERT( density0->tree().isValueOn(Coord(24,3,3))); + } + {//test advect with a mask + GridT::Ptr density0 = GridT::create(0.0f), density1; + density0->fill(CoordBBox(Coord(0),Coord(6)), 1.0f); + CPPUNIT_ASSERT_EQUAL(density0->tree().getValue(Coord( 3,3,3)), 1.0f); + CPPUNIT_ASSERT_EQUAL(density0->tree().getValue(Coord(24,3,3)), 0.0f); + CPPUNIT_ASSERT( density0->tree().isValueOn(Coord( 3,3,3))); + CPPUNIT_ASSERT(!density0->tree().isValueOn(Coord(24,3,3))); + + BoolGrid::Ptr mask = BoolGrid::create(false); + mask->fill(CoordBBox(Coord(4,0,0),Coord(30,8,8)), true); + + AdvT a(velocity); + a.setGrainSize(0); + a.setIntegrator(tools::Scheme::MAC); + //a.setIntegrator(tools::Scheme::BFECC); + //a.setIntegrator(tools::Scheme::RK4); + for (int i=1; i<=240; ++i) { + density1 = a.advect(*density0, *mask, 0.1f); + //std::ostringstream ostr; + //ostr << "densityAdvectMask" << "_" << i << ".vdb"; + //std::cerr << "Writing " << ostr.str() << std::endl; + //openvdb::io::File file(ostr.str()); + //openvdb::GridPtrVec grids; + //grids.push_back(density1); + //file.write(grids); + density0 = density1; + } + CPPUNIT_ASSERT_EQUAL(density0->tree().getValue(Coord(3,3,3)), 1.0f); + CPPUNIT_ASSERT(density0->tree().getValue(Coord(24,3,3)) > 0.0f); + CPPUNIT_ASSERT(density0->tree().isValueOn(Coord( 3,3,3))); + CPPUNIT_ASSERT(density0->tree().isValueOn(Coord(24,3,3))); + } + + /* + {//benchmark on a sphere + util::CpuTimer timer; + GridT::Ptr density0 = GridT::create(0.0f), density1; + density0->fill(CoordBBox(Coord(0), Coord(600)), 1.0f); + timer.start("densify"); + density0->tree().voxelizeActiveTiles(); + timer.stop(); + AdvT a(velocity); + a.setGrainSize(1); + //a.setLimiter(tools::Scheme::NO_LIMITER); + //a.setIntegrator(tools::Scheme::MAC); + //a.setIntegrator(tools::Scheme::BFECC); + a.setIntegrator(tools::Scheme::RK4); + + for (int i=1; i<=10; ++i) { + timer.start("Volume Advection"); + density1 = a.advect(*density0, 0.1f); + timer.stop(); + std::ostringstream ostr; + ostr << "densityAdvectMask" << "_" << i << ".vdb"; + std::cerr << "Writing " << ostr.str() << std::endl; + io::File file(ostr.str()); + GridPtrVec grids; + grids.push_back(density1); + file.write(grids); + density0.swap(density1); + } + } + */ +}// testVolumeAdvect + +//////////////////////////////////////// + + +void +TestTools::testFloatApply() +{ + using ValueIter = openvdb::FloatTree::ValueOnIter; + + struct Local { + static inline float op(float x) { return x * 2.f; } + static inline void visit(const ValueIter& it) { it.setValue(op(*it)); } + }; + + const float background = 1.0; + openvdb::FloatTree tree(background); + + const int MIN = -1000, MAX = 1000, STEP = 50; + openvdb::Coord xyz; + for (int z = MIN; z < MAX; z += STEP) { + xyz.setZ(z); + for (int y = MIN; y < MAX; y += STEP) { + xyz.setY(y); + for (int x = MIN; x < MAX; x += STEP) { + xyz.setX(x); + tree.setValue(xyz, float(x + y + z)); + } + } + } + /// @todo set some tile values + + openvdb::tools::foreach(tree.begin(), Local::visit, /*threaded=*/true); + + float expected = Local::op(background); + //CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, tree.background(), /*tolerance=*/0.0); + //expected = Local::op(-background); + //CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, -tree.background(), /*tolerance=*/0.0); + + for (openvdb::FloatTree::ValueOnCIter it = tree.cbeginValueOn(); it; ++it) { + xyz = it.getCoord(); + expected = Local::op(float(xyz[0] + xyz[1] + xyz[2])); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, it.getValue(), /*tolerance=*/0.0); + } +} + + +//////////////////////////////////////// + + +namespace { + +template +struct MatMul { + openvdb::math::Mat3s mat; + MatMul(const openvdb::math::Mat3s& _mat): mat(_mat) {} + openvdb::Vec3s xform(const openvdb::Vec3s& v) const { return mat.transform(v); } + void operator()(const IterT& it) const { it.setValue(xform(*it)); } +}; + +} + + +void +TestTools::testVectorApply() +{ + using ValueIter = openvdb::VectorTree::ValueOnIter; + + const openvdb::Vec3s background(1, 1, 1); + openvdb::VectorTree tree(background); + + const int MIN = -1000, MAX = 1000, STEP = 80; + openvdb::Coord xyz; + for (int z = MIN; z < MAX; z += STEP) { + xyz.setZ(z); + for (int y = MIN; y < MAX; y += STEP) { + xyz.setY(y); + for (int x = MIN; x < MAX; x += STEP) { + xyz.setX(x); + tree.setValue(xyz, openvdb::Vec3s(float(x), float(y), float(z))); + } + } + } + /// @todo set some tile values + + MatMul op(openvdb::math::Mat3s(1, 2, 3, -1, -2, -3, 3, 2, 1)); + openvdb::tools::foreach(tree.beginValueOn(), op, /*threaded=*/true); + + openvdb::Vec3s expected; + for (openvdb::VectorTree::ValueOnCIter it = tree.cbeginValueOn(); it; ++it) { + xyz = it.getCoord(); + expected = op.xform(openvdb::Vec3s(float(xyz[0]), float(xyz[1]), float(xyz[2]))); + CPPUNIT_ASSERT_EQUAL(expected, it.getValue()); + } +} + + +//////////////////////////////////////// + + +namespace { + +struct AccumSum { + int64_t sum; int joins; + AccumSum(): sum(0), joins(0) {} + void operator()(const openvdb::Int32Tree::ValueOnCIter& it) + { + if (it.isVoxelValue()) sum += *it; + else sum += (*it) * it.getVoxelCount(); + } + void join(AccumSum& other) { sum += other.sum; joins += 1 + other.joins; } +}; + + +struct AccumLeafVoxelCount { + using LeafRange = openvdb::tree::LeafManager::LeafRange; + openvdb::Index64 count; + AccumLeafVoxelCount(): count(0) {} + void operator()(const LeafRange::Iterator& it) { count += it->onVoxelCount(); } + void join(AccumLeafVoxelCount& other) { count += other.count; } +}; + +} + + +void +TestTools::testAccumulate() +{ + using namespace openvdb; + + const int value = 2; + Int32Tree tree(/*background=*/0); + tree.fill(CoordBBox::createCube(Coord(0), 198), value, /*active=*/true); + + const int64_t expected = tree.activeVoxelCount() * value; + { + AccumSum op; + tools::accumulate(tree.cbeginValueOn(), op, /*threaded=*/false); + CPPUNIT_ASSERT_EQUAL(expected, op.sum); + CPPUNIT_ASSERT_EQUAL(0, op.joins); + } + { + AccumSum op; + tools::accumulate(tree.cbeginValueOn(), op, /*threaded=*/true); + CPPUNIT_ASSERT_EQUAL(expected, op.sum); + } + { + AccumLeafVoxelCount op; + tree::LeafManager mgr(tree); + tools::accumulate(mgr.leafRange().begin(), op, /*threaded=*/true); + CPPUNIT_ASSERT_EQUAL(tree.activeLeafVoxelCount(), op.count); + } +} + + +//////////////////////////////////////// + + +namespace { + +template +struct FloatToVec +{ + using ValueT = typename InIterT::ValueT; + using Accessor = typename openvdb::tree::ValueAccessor; + + // Transform a scalar value into a vector value. + static openvdb::Vec3s toVec(const ValueT& v) { return openvdb::Vec3s(v, v*2, v*3); } + + FloatToVec() { numTiles = 0; } + + void operator()(const InIterT& it, Accessor& acc) + { + if (it.isVoxelValue()) { // set a single voxel + acc.setValue(it.getCoord(), toVec(*it)); + } else { // fill an entire tile + numTiles.fetch_and_increment(); + openvdb::CoordBBox bbox; + it.getBoundingBox(bbox); + acc.tree().fill(bbox, toVec(*it)); + } + } + + tbb::atomic numTiles; +}; + +} + + +void +TestTools::testTransformValues() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + using openvdb::Vec3s; + + using Tree323f = openvdb::tree::Tree4::Type; + using Tree323v = openvdb::tree::Tree4::Type; + + const float background = 1.0; + Tree323f ftree(background); + + const int MIN = -1000, MAX = 1000, STEP = 80; + Coord xyz; + for (int z = MIN; z < MAX; z += STEP) { + xyz.setZ(z); + for (int y = MIN; y < MAX; y += STEP) { + xyz.setY(y); + for (int x = MIN; x < MAX; x += STEP) { + xyz.setX(x); + ftree.setValue(xyz, float(x + y + z)); + } + } + } + // Set some tile values. + ftree.fill(CoordBBox(Coord(1024), Coord(1024 + 8 - 1)), 3 * 1024); // level-1 tile + ftree.fill(CoordBBox(Coord(2048), Coord(2048 + 32 - 1)), 3 * 2048); // level-2 tile + ftree.fill(CoordBBox(Coord(3072), Coord(3072 + 256 - 1)), 3 * 3072); // level-3 tile + + for (int shareOp = 0; shareOp <= 1; ++shareOp) { + FloatToVec op; + Tree323v vtree; + openvdb::tools::transformValues(ftree.cbeginValueOn(), vtree, op, + /*threaded=*/true, shareOp); + + // The tile count is accurate only if the functor is shared. Otherwise, + // it is initialized to zero in the main thread and never changed. + CPPUNIT_ASSERT_EQUAL(shareOp ? 3 : 0, int(op.numTiles)); + + Vec3s expected; + for (Tree323v::ValueOnCIter it = vtree.cbeginValueOn(); it; ++it) { + xyz = it.getCoord(); + expected = op.toVec(float(xyz[0] + xyz[1] + xyz[2])); + CPPUNIT_ASSERT_EQUAL(expected, it.getValue()); + } + // Check values inside the tiles. + CPPUNIT_ASSERT_EQUAL(op.toVec(3 * 1024), vtree.getValue(Coord(1024 + 4))); + CPPUNIT_ASSERT_EQUAL(op.toVec(3 * 2048), vtree.getValue(Coord(2048 + 16))); + CPPUNIT_ASSERT_EQUAL(op.toVec(3 * 3072), vtree.getValue(Coord(3072 + 128))); + } +} + + +//////////////////////////////////////// + + +void +TestTools::testUtil() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + using openvdb::Vec3s; + + using CharTree = openvdb::tree::Tree4::Type; + + // Test boolean operators + CharTree treeA(false), treeB(false); + + treeA.fill(CoordBBox(Coord(-10), Coord(10)), true); + treeA.voxelizeActiveTiles(); + + treeB.fill(CoordBBox(Coord(-10), Coord(10)), true); + treeB.voxelizeActiveTiles(); + + const size_t voxelCountA = treeA.activeVoxelCount(); + const size_t voxelCountB = treeB.activeVoxelCount(); + + CPPUNIT_ASSERT_EQUAL(voxelCountA, voxelCountB); + + CharTree::Ptr tree = openvdb::util::leafTopologyDifference(treeA, treeB); + CPPUNIT_ASSERT(tree->activeVoxelCount() == 0); + + tree = openvdb::util::leafTopologyIntersection(treeA, treeB); + CPPUNIT_ASSERT(tree->activeVoxelCount() == voxelCountA); + + treeA.fill(CoordBBox(Coord(-10), Coord(22)), true); + treeA.voxelizeActiveTiles(); + + const size_t voxelCount = treeA.activeVoxelCount(); + + tree = openvdb::util::leafTopologyDifference(treeA, treeB); + CPPUNIT_ASSERT(tree->activeVoxelCount() == (voxelCount - voxelCountA)); + + tree = openvdb::util::leafTopologyIntersection(treeA, treeB); + CPPUNIT_ASSERT(tree->activeVoxelCount() == voxelCountA); +} + + +//////////////////////////////////////// + + +void +TestTools::testVectorTransformer() +{ + using namespace openvdb; + + Mat4d xform = Mat4d::identity(); + xform.preTranslate(Vec3d(0.1, -2.5, 3)); + xform.preScale(Vec3d(0.5, 1.1, 2)); + xform.preRotate(math::X_AXIS, 30.0 * M_PI / 180.0); + xform.preRotate(math::Y_AXIS, 300.0 * M_PI / 180.0); + + Mat4d invXform = xform.inverse(); + invXform = invXform.transpose(); + + { + // Set some vector values in a grid, then verify that tools::transformVectors() + // transforms them as expected for each VecType. + + const Vec3s refVec0(0, 0, 0), refVec1(1, 0, 0), refVec2(0, 1, 0), refVec3(0, 0, 1); + + Vec3SGrid grid; + Vec3SGrid::Accessor acc = grid.getAccessor(); + +#define resetGrid() \ + { \ + grid.clear(); \ + acc.setValue(Coord(0), refVec0); \ + acc.setValue(Coord(1), refVec1); \ + acc.setValue(Coord(2), refVec2); \ + acc.setValue(Coord(3), refVec3); \ + } + + // Verify that grid values are in world space by default. + CPPUNIT_ASSERT(grid.isInWorldSpace()); + + resetGrid(); + grid.setVectorType(VEC_INVARIANT); + tools::transformVectors(grid, xform); + CPPUNIT_ASSERT(acc.getValue(Coord(0)).eq(refVec0)); + CPPUNIT_ASSERT(acc.getValue(Coord(1)).eq(refVec1)); + CPPUNIT_ASSERT(acc.getValue(Coord(2)).eq(refVec2)); + CPPUNIT_ASSERT(acc.getValue(Coord(3)).eq(refVec3)); + + resetGrid(); + grid.setVectorType(VEC_COVARIANT); + tools::transformVectors(grid, xform); + CPPUNIT_ASSERT(acc.getValue(Coord(0)).eq(invXform.transform3x3(refVec0))); + CPPUNIT_ASSERT(acc.getValue(Coord(1)).eq(invXform.transform3x3(refVec1))); + CPPUNIT_ASSERT(acc.getValue(Coord(2)).eq(invXform.transform3x3(refVec2))); + CPPUNIT_ASSERT(acc.getValue(Coord(3)).eq(invXform.transform3x3(refVec3))); + + resetGrid(); + grid.setVectorType(VEC_COVARIANT_NORMALIZE); + tools::transformVectors(grid, xform); + CPPUNIT_ASSERT_EQUAL(refVec0, acc.getValue(Coord(0))); + CPPUNIT_ASSERT(acc.getValue(Coord(1)).eq(invXform.transform3x3(refVec1).unit())); + CPPUNIT_ASSERT(acc.getValue(Coord(2)).eq(invXform.transform3x3(refVec2).unit())); + CPPUNIT_ASSERT(acc.getValue(Coord(3)).eq(invXform.transform3x3(refVec3).unit())); + + resetGrid(); + grid.setVectorType(VEC_CONTRAVARIANT_RELATIVE); + tools::transformVectors(grid, xform); + CPPUNIT_ASSERT(acc.getValue(Coord(0)).eq(xform.transform3x3(refVec0))); + CPPUNIT_ASSERT(acc.getValue(Coord(1)).eq(xform.transform3x3(refVec1))); + CPPUNIT_ASSERT(acc.getValue(Coord(2)).eq(xform.transform3x3(refVec2))); + CPPUNIT_ASSERT(acc.getValue(Coord(3)).eq(xform.transform3x3(refVec3))); + + resetGrid(); + grid.setVectorType(VEC_CONTRAVARIANT_ABSOLUTE); + /// @todo This doesn't really test the behavior w.r.t. homogeneous coords. + tools::transformVectors(grid, xform); + CPPUNIT_ASSERT(acc.getValue(Coord(0)).eq(xform.transformH(refVec0))); + CPPUNIT_ASSERT(acc.getValue(Coord(1)).eq(xform.transformH(refVec1))); + CPPUNIT_ASSERT(acc.getValue(Coord(2)).eq(xform.transformH(refVec2))); + CPPUNIT_ASSERT(acc.getValue(Coord(3)).eq(xform.transformH(refVec3))); + + // Verify that transformVectors() has no effect on local-space grids. + resetGrid(); + grid.setVectorType(VEC_CONTRAVARIANT_RELATIVE); + grid.setIsInWorldSpace(false); + tools::transformVectors(grid, xform); + CPPUNIT_ASSERT(acc.getValue(Coord(0)).eq(refVec0)); + CPPUNIT_ASSERT(acc.getValue(Coord(1)).eq(refVec1)); + CPPUNIT_ASSERT(acc.getValue(Coord(2)).eq(refVec2)); + CPPUNIT_ASSERT(acc.getValue(Coord(3)).eq(refVec3)); + +#undef resetGrid + } + { + // Verify that transformVectors() operates only on vector-valued grids. + FloatGrid scalarGrid; + CPPUNIT_ASSERT_THROW(tools::transformVectors(scalarGrid, xform), TypeError); + } +} + + +//////////////////////////////////////// + + +void +TestTools::testPrune() +{ + /// @todo Add more unit-tests! + + using namespace openvdb; + + {// try prunning a tree with const values + const float value = 5.345f; + + FloatTree tree(value); + CPPUNIT_ASSERT_EQUAL(Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), tree.nonLeafCount()); // root node + CPPUNIT_ASSERT(tree.empty()); + + tree.fill(CoordBBox(Coord(-10), Coord(10)), value, /*active=*/false); + CPPUNIT_ASSERT(!tree.empty()); + + tools::prune(tree); + + CPPUNIT_ASSERT_EQUAL(Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), tree.nonLeafCount()); // root node + CPPUNIT_ASSERT(tree.empty()); + } + + {// Prune a tree with a single leaf node with random values in the range [0,1] + using LeafNodeT = tree::LeafNode; + const float val = 1.0, tol = 1.1f; + + // Fill a leaf node with random values in the range [0,1] + LeafNodeT *leaf = new LeafNodeT(Coord(0), val, true); + math::Random01 r(145); + std::vector data(LeafNodeT::NUM_VALUES); + for (Index i=0; isetValueOnly(i, v); + } + + // Insert leaf node into an empty tree + FloatTree tree(val); + tree.addLeaf(leaf); + + CPPUNIT_ASSERT_EQUAL(Index32(1), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(3), tree.nonLeafCount()); // root+2*internal + + tools::prune(tree);// tolerance is zero + + CPPUNIT_ASSERT_EQUAL(Index32(1), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(3), tree.nonLeafCount()); // root+2*internal + + tools::prune(tree, tol); + + CPPUNIT_ASSERT_EQUAL(Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(3), tree.nonLeafCount()); // root+2*internal + + std::sort(data.begin(), data.end()); + const float median = data[(LeafNodeT::NUM_VALUES-1)>>1]; + + ASSERT_DOUBLES_EXACTLY_EQUAL(median, tree.getValue(Coord(0))); + } + + /* + {// Benchmark serial prune + util::CpuTimer timer; + initialize();//required whenever I/O of OpenVDB files is performed! + io::File sourceFile("/usr/pic1/Data/OpenVDB/LevelSetModels/crawler.vdb"); + sourceFile.open(false);//disable delayed loading + FloatGrid::Ptr grid = gridPtrCast(sourceFile.getGrids()->at(0)); + const Index32 leafCount = grid->tree().leafCount(); + + timer.start("\nSerial tolerance prune"); + grid->tree().prune(); + timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, grid->tree().leafCount()); + } + {// Benchmark parallel prune + util::CpuTimer timer; + initialize();//required whenever I/O of OpenVDB files is performed! + io::File sourceFile("/usr/pic1/Data/OpenVDB/LevelSetModels/crawler.vdb"); + sourceFile.open(false);//disable delayed loading + FloatGrid::Ptr grid = gridPtrCast(sourceFile.getGrids()->at(0)); + const Index32 leafCount = grid->tree().leafCount(); + + timer.start("\nParallel tolerance prune"); + tools::prune(grid->tree()); + timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, grid->tree().leafCount()); + } + */ +} diff --git a/openvdb/unittest/TestTopologyToLevelSet.cc b/openvdb/unittest/TestTopologyToLevelSet.cc new file mode 100644 index 00000000..839c29b1 --- /dev/null +++ b/openvdb/unittest/TestTopologyToLevelSet.cc @@ -0,0 +1,54 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + + +class TopologyToLevelSet: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TopologyToLevelSet); + CPPUNIT_TEST(testConversion); + CPPUNIT_TEST_SUITE_END(); + + void testConversion(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TopologyToLevelSet); + +void +TopologyToLevelSet::testConversion() +{ + typedef openvdb::tree::Tree4::Type Tree543b; + typedef openvdb::Grid BoolGrid; + + typedef openvdb::tree::Tree4::Type Tree543f; + typedef openvdb::Grid FloatGrid; + + ///// + + const float voxelSize = 0.1f; + const openvdb::math::Transform::Ptr transform = + openvdb::math::Transform::createLinearTransform(voxelSize); + + BoolGrid maskGrid(false); + maskGrid.setTransform(transform); + + // Define active region + maskGrid.fill(openvdb::CoordBBox(openvdb::Coord(0), openvdb::Coord(7)), true); + maskGrid.tree().voxelizeActiveTiles(); + + FloatGrid::Ptr sdfGrid = openvdb::tools::topologyToLevelSet(maskGrid); + + CPPUNIT_ASSERT(sdfGrid.get() != NULL); + CPPUNIT_ASSERT(!sdfGrid->empty()); + CPPUNIT_ASSERT_EQUAL(int(openvdb::GRID_LEVEL_SET), int(sdfGrid->getGridClass())); + + // test inside coord value + CPPUNIT_ASSERT(sdfGrid->tree().getValue(openvdb::Coord(3,3,3)) < 0.0f); + + // test outside coord value + CPPUNIT_ASSERT(sdfGrid->tree().getValue(openvdb::Coord(10,10,10)) > 0.0f); +} + diff --git a/openvdb/unittest/TestTransform.cc b/openvdb/unittest/TestTransform.cc new file mode 100644 index 00000000..ecef2d77 --- /dev/null +++ b/openvdb/unittest/TestTransform.cc @@ -0,0 +1,535 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + + +class TestTransform: public CppUnit::TestCase +{ +public: + virtual void setUp(); + virtual void tearDown(); + + CPPUNIT_TEST_SUITE(TestTransform); + CPPUNIT_TEST(testLinearTransform); + CPPUNIT_TEST(testTransformEquality); + CPPUNIT_TEST(testBackwardCompatibility); + CPPUNIT_TEST(testIsIdentity); + CPPUNIT_TEST(testBoundingBoxes); + CPPUNIT_TEST_SUITE_END(); + + void testLinearTransform(); + void testTransformEquality(); + void testBackwardCompatibility(); + void testIsIdentity(); + void testBoundingBoxes(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestTransform); + + +//////////////////////////////////////// + + +void +TestTransform::setUp() +{ + openvdb::math::MapRegistry::clear(); + openvdb::math::AffineMap::registerMap(); + openvdb::math::ScaleMap::registerMap(); + openvdb::math::UniformScaleMap::registerMap(); + openvdb::math::TranslationMap::registerMap(); + openvdb::math::ScaleTranslateMap::registerMap(); + openvdb::math::UniformScaleTranslateMap::registerMap(); +} + + +void +TestTransform::tearDown() +{ + openvdb::math::MapRegistry::clear(); +} + + +////openvdb:://////////////////////////////////// + + +void +TestTransform::testLinearTransform() +{ + using namespace openvdb; + double TOL = 1e-7; + + // Test: Scaling + math::Transform::Ptr t = math::Transform::createLinearTransform(0.5); + + Vec3R voxelSize = t->voxelSize(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, voxelSize[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, voxelSize[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, voxelSize[2], TOL); + + CPPUNIT_ASSERT(t->hasUniformScale()); + + // world to index space + Vec3R xyz(-1.0, 2.0, 4.0); + xyz = t->worldToIndex(xyz); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-2.0, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 4.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 8.0, xyz[2], TOL); + + xyz = Vec3R(-0.7, 2.4, 4.7); + + // cell centered conversion + Coord ijk = t->worldToIndexCellCentered(xyz); + CPPUNIT_ASSERT_EQUAL(Coord(-1, 5, 9), ijk); + + // node centrered conversion + ijk = t->worldToIndexNodeCentered(xyz); + CPPUNIT_ASSERT_EQUAL(Coord(-2, 4, 9), ijk); + + // index to world space + ijk = Coord(4, 2, -8); + xyz = t->indexToWorld(ijk); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 2.0, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-4.0, xyz[2], TOL); + + // I/O test + { + std::stringstream + ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + t->write(ss); + + t = math::Transform::createLinearTransform(); + + // Since we wrote only a fragment of a VDB file (in particular, we didn't + // write the header), set the file format version number explicitly. + io::setCurrentVersion(ss); + + t->read(ss); + } + + // check map type + CPPUNIT_ASSERT_EQUAL(math::UniformScaleMap::mapType(), t->baseMap()->type()); + + voxelSize = t->voxelSize(); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, voxelSize[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, voxelSize[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, voxelSize[2], TOL); + + ////////// + + // Test: Scale, translation & rotation + t = math::Transform::createLinearTransform(2.0); + + // rotate, 180 deg, (produces a diagonal matrix that can be simplified into a scale map) + // with diagonal -2, 2, -2 + const double PI = std::atan(1.0)*4; + t->preRotate(PI, math::Y_AXIS); + + // this is just a rotation so it will have uniform scale + CPPUNIT_ASSERT(t->hasUniformScale()); + + CPPUNIT_ASSERT_EQUAL(math::ScaleMap::mapType(), t->baseMap()->type()); + + voxelSize = t->voxelSize(); + xyz = t->worldToIndex(Vec3R(-2.0, -2.0, -2.0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[2], TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, xyz[2], TOL); + + // translate + t->postTranslate(Vec3d(1.0, 0.0, 1.0)); + + CPPUNIT_ASSERT_EQUAL(math::ScaleTranslateMap::mapType(), t->baseMap()->type()); + + voxelSize = t->voxelSize(); + xyz = t->worldToIndex(Vec3R(-2.0, -2.0, -2.0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[2], TOL); + + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.5, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.5, xyz[2], TOL); + + + // I/O test + { + std::stringstream + ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + t->write(ss); + + t = math::Transform::createLinearTransform(); + + // Since we wrote only a fragment of a VDB file (in particular, we didn't + // write the header), set the file format version number explicitly. + io::setCurrentVersion(ss); + + t->read(ss); + } + + // check map type + CPPUNIT_ASSERT_EQUAL(math::ScaleTranslateMap::mapType(), t->baseMap()->type()); + + voxelSize = t->voxelSize(); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[2], TOL); + + xyz = t->worldToIndex(Vec3R(-2.0, -2.0, -2.0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.5, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.5, xyz[2], TOL); + + // new transform + t = math::Transform::createLinearTransform(1.0); + + // rotate 90 deg + t->preRotate( std::atan(1.0) * 2 , math::Y_AXIS); + + // check map type + CPPUNIT_ASSERT_EQUAL(math::AffineMap::mapType(), t->baseMap()->type()); + + xyz = t->worldToIndex(Vec3R(1.0, 1.0, 1.0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, xyz[2], TOL); + + // I/O test + { + std::stringstream + ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + t->write(ss); + + t = math::Transform::createLinearTransform(); + + CPPUNIT_ASSERT_EQUAL(math::UniformScaleMap::mapType(), t->baseMap()->type()); + + xyz = t->worldToIndex(Vec3R(1.0, 1.0, 1.0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, xyz[2], TOL); + + // Since we wrote only a fragment of a VDB file (in particular, we didn't + // write the header), set the file format version number explicitly. + io::setCurrentVersion(ss); + + t->read(ss); + } + + // check map type + CPPUNIT_ASSERT_EQUAL(math::AffineMap::mapType(), t->baseMap()->type()); + + xyz = t->worldToIndex(Vec3R(1.0, 1.0, 1.0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, xyz[2], TOL); +} + + +//////////////////////////////////////// + +void +TestTransform::testTransformEquality() +{ + using namespace openvdb; + + // maps created in different ways may be equivalent + math::Transform::Ptr t1 = math::Transform::createLinearTransform(0.5); + math::Mat4d mat = math::Mat4d::identity(); + mat.preScale(math::Vec3d(0.5, 0.5, 0.5)); + math::Transform::Ptr t2 = math::Transform::createLinearTransform(mat); + + CPPUNIT_ASSERT( *t1 == *t2); + + // test that the auto-convert to the simplest form worked + CPPUNIT_ASSERT( t1->mapType() == t2->mapType()); + + + mat.preScale(math::Vec3d(1., 1., .4)); + math::Transform::Ptr t3 = math::Transform::createLinearTransform(mat); + + CPPUNIT_ASSERT( *t1 != *t3); + + // test equality between different but equivalent maps + math::UniformScaleTranslateMap::Ptr ustmap( + new math::UniformScaleTranslateMap(1.0, math::Vec3d(0,0,0))); + math::Transform::Ptr t4( new math::Transform( ustmap) ); + CPPUNIT_ASSERT( t4->baseMap()->isType() ); + math::Transform::Ptr t5( new math::Transform); // constructs with a scale map + CPPUNIT_ASSERT( t5->baseMap()->isType() ); + + CPPUNIT_ASSERT( *t5 == *t4); + + CPPUNIT_ASSERT( t5->mapType() != t4->mapType() ); + + // test inequatlity of two maps of the same type + math::UniformScaleTranslateMap::Ptr ustmap2( + new math::UniformScaleTranslateMap(1.0, math::Vec3d(1,0,0))); + math::Transform::Ptr t6( new math::Transform( ustmap2) ); + CPPUNIT_ASSERT( t6->baseMap()->isType() ); + CPPUNIT_ASSERT( *t6 != *t4); + + // test comparison of linear to nonlinear map + openvdb::BBoxd bbox(math::Vec3d(0), math::Vec3d(100)); + math::Transform::Ptr frustum = math::Transform::createFrustumTransform(bbox, 0.25, 10); + + CPPUNIT_ASSERT( *frustum != *t1 ); + + +} +//////////////////////////////////////// + +void +TestTransform::testBackwardCompatibility() +{ + using namespace openvdb; + double TOL = 1e-7; + + // Register maps + math::MapRegistry::clear(); + math::AffineMap::registerMap(); + math::ScaleMap::registerMap(); + math::TranslationMap::registerMap(); + math::ScaleTranslateMap::registerMap(); + + std::stringstream + ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + + ////////// + + // Construct and write out an old transform that gets converted + // into a ScaleMap on read. + + // First write the old transform type name + writeString(ss, Name("LinearTransform")); + + // Second write the old transform's base class membes. + Coord tmpMin(0), tmpMax(1); + ss.write(reinterpret_cast(&tmpMin), sizeof(Coord::ValueType) * 3); + ss.write(reinterpret_cast(&tmpMax), sizeof(Coord::ValueType) * 3); + + // Last write out the old linear transform's members + math::Mat4d tmpLocalToWorld = math::Mat4d::identity(), + tmpWorldToLocal = math::Mat4d::identity(), + tmpVoxelToLocal = math::Mat4d::identity(), + tmpLocalToVoxel = math::Mat4d::identity(); + + tmpVoxelToLocal.preScale(math::Vec3d(0.5, 0.5, 0.5)); + + tmpLocalToWorld.write(ss); + tmpWorldToLocal.write(ss); + tmpVoxelToLocal.write(ss); + tmpLocalToVoxel.write(ss); + + // Read in the old transform and converting it to the new map based implementation. + + math::Transform::Ptr t = math::Transform::createLinearTransform(1.0); + + t->read(ss); + + // check map type + CPPUNIT_ASSERT_EQUAL(math::UniformScaleMap::mapType(), t->baseMap()->type()); + + Vec3d voxelSize = t->voxelSize(); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, voxelSize[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, voxelSize[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, voxelSize[2], TOL); + + Vec3d xyz = t->worldToIndex(Vec3d(-1.0, 2.0, 4.0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(-2.0, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 4.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 8.0, xyz[2], TOL); + + + ////////// + + // Construct and write out an old transform that gets converted + // into a ScaleTranslateMap on read. + + ss.clear(); + writeString(ss, Name("LinearTransform")); + ss.write(reinterpret_cast(&tmpMin), sizeof(Coord::ValueType) * 3); + ss.write(reinterpret_cast(&tmpMax), sizeof(Coord::ValueType) * 3); + tmpLocalToWorld = math::Mat4d::identity(), + tmpWorldToLocal = math::Mat4d::identity(), + tmpVoxelToLocal = math::Mat4d::identity(), + tmpLocalToVoxel = math::Mat4d::identity(); + + tmpVoxelToLocal.preScale(math::Vec3d(2.0, 2.0, 2.0)); + tmpLocalToWorld.setTranslation(math::Vec3d(1.0, 0.0, 1.0)); + + tmpLocalToWorld.write(ss); + tmpWorldToLocal.write(ss); + tmpVoxelToLocal.write(ss); + tmpLocalToVoxel.write(ss); + + // Read in the old transform and converting it to the new map based implementation. + + t = math::Transform::createLinearTransform(); // rest transform + t->read(ss); + + CPPUNIT_ASSERT_EQUAL(math::UniformScaleTranslateMap::mapType(), t->baseMap()->type()); + + voxelSize = t->voxelSize(); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, voxelSize[2], TOL); + + xyz = t->worldToIndex(Vec3d(1.0, 1.0, 1.0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, xyz[2], TOL); + + + ////////// + + // Construct and write out an old transform that gets converted + // into a AffineMap on read. + + ss.clear(); + writeString(ss, Name("LinearTransform")); + ss.write(reinterpret_cast(&tmpMin), sizeof(Coord::ValueType) * 3); + ss.write(reinterpret_cast(&tmpMax), sizeof(Coord::ValueType) * 3); + tmpLocalToWorld = math::Mat4d::identity(), + tmpWorldToLocal = math::Mat4d::identity(), + tmpVoxelToLocal = math::Mat4d::identity(), + tmpLocalToVoxel = math::Mat4d::identity(); + + tmpVoxelToLocal.preScale(math::Vec3d(1.0, 1.0, 1.0)); + tmpLocalToWorld.preRotate( math::Y_AXIS, std::atan(1.0) * 2); + + tmpLocalToWorld.write(ss); + tmpWorldToLocal.write(ss); + tmpVoxelToLocal.write(ss); + tmpLocalToVoxel.write(ss); + + // Read in the old transform and converting it to the new map based implementation. + + t = math::Transform::createLinearTransform(); // rest transform + t->read(ss); + + CPPUNIT_ASSERT_EQUAL(math::AffineMap::mapType(), t->baseMap()->type()); + + voxelSize = t->voxelSize(); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, voxelSize[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, voxelSize[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, voxelSize[2], TOL); + + xyz = t->worldToIndex(Vec3d(1.0, 1.0, 1.0)); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, xyz[0], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, xyz[1], TOL); + CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, xyz[2], TOL); +} + + +void +TestTransform::testIsIdentity() +{ + using namespace openvdb; + math::Transform::Ptr t = math::Transform::createLinearTransform(1.0); + + CPPUNIT_ASSERT(t->isIdentity()); + + t->preScale(Vec3d(2,2,2)); + + CPPUNIT_ASSERT(!t->isIdentity()); + + t->preScale(Vec3d(0.5,0.5,0.5)); + CPPUNIT_ASSERT(t->isIdentity()); + + BBoxd bbox(math::Vec3d(-5,-5,0), Vec3d(5,5,10)); + math::Transform::Ptr f = math::Transform::createFrustumTransform(bbox, + /*taper*/ 1, + /*depth*/ 1, + /*voxel size*/ 1); + f->preScale(Vec3d(10,10,10)); + + CPPUNIT_ASSERT(f->isIdentity()); + + // rotate by PI/2 + f->postRotate(std::atan(1.0)*2, math::Y_AXIS); + CPPUNIT_ASSERT(!f->isIdentity()); + + f->postRotate(std::atan(1.0)*6, math::Y_AXIS); + CPPUNIT_ASSERT(f->isIdentity()); +} + + +void +TestTransform::testBoundingBoxes() +{ + using namespace openvdb; + + { + math::Transform::ConstPtr t = math::Transform::createLinearTransform(0.5); + + const BBoxd bbox(Vec3d(-8.0), Vec3d(16.0)); + + BBoxd xBBox = t->indexToWorld(bbox); + CPPUNIT_ASSERT_EQUAL(Vec3d(-4.0), xBBox.min()); + CPPUNIT_ASSERT_EQUAL(Vec3d(8.0), xBBox.max()); + + xBBox = t->worldToIndex(xBBox); + CPPUNIT_ASSERT_EQUAL(bbox.min(), xBBox.min()); + CPPUNIT_ASSERT_EQUAL(bbox.max(), xBBox.max()); + } + { + const double PI = std::atan(1.0) * 4.0, SQRT2 = std::sqrt(2.0); + + math::Transform::Ptr t = math::Transform::createLinearTransform(1.0); + t->preRotate(PI / 4.0, math::Z_AXIS); + + const BBoxd bbox(Vec3d(-10.0), Vec3d(10.0)); + + BBoxd xBBox = t->indexToWorld(bbox); // expand in x and y by sqrt(2) + CPPUNIT_ASSERT(Vec3d(-10.0 * SQRT2, -10.0 * SQRT2, -10.0).eq(xBBox.min())); + CPPUNIT_ASSERT(Vec3d(10.0 * SQRT2, 10.0 * SQRT2, 10.0).eq(xBBox.max())); + + xBBox = t->worldToIndex(xBBox); // expand again in x and y by sqrt(2) + CPPUNIT_ASSERT(Vec3d(-20.0, -20.0, -10.0).eq(xBBox.min())); + CPPUNIT_ASSERT(Vec3d(20.0, 20.0, 10.0).eq(xBBox.max())); + } + + /// @todo frustum transform +} + + +//////////////////////////////////////// + + +/// @todo Test the new frustum transform. +/* +void +TestTransform::testNonlinearTransform() +{ + using namespace openvdb; + double TOL = 1e-7; +} +*/ diff --git a/openvdb/unittest/TestTree.cc b/openvdb/unittest/TestTree.cc new file mode 100644 index 00000000..bb111350 --- /dev/null +++ b/openvdb/unittest/TestTree.cc @@ -0,0 +1,3166 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include // for remove() +#include +#include +#include +#include +#include +#include +#include // for tools::setValueOnMin(), et al. +#include +#include // for io::RealToHalf +#include // for Abs() +#include +#include +#include +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + + +using ValueType = float; +using LeafNodeType = openvdb::tree::LeafNode; +using InternalNodeType1 = openvdb::tree::InternalNode; +using InternalNodeType2 = openvdb::tree::InternalNode; +using RootNodeType = openvdb::tree::RootNode; + + +class TestTree: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestTree); + CPPUNIT_TEST(testChangeBackground); + CPPUNIT_TEST(testHalf); + CPPUNIT_TEST(testValues); + CPPUNIT_TEST(testSetValue); + CPPUNIT_TEST(testSetValueOnly); + CPPUNIT_TEST(testSetValueInPlace); + CPPUNIT_TEST(testEvalMinMax); + CPPUNIT_TEST(testResize); + CPPUNIT_TEST(testHasSameTopology); + CPPUNIT_TEST(testTopologyCopy); + CPPUNIT_TEST(testIterators); + CPPUNIT_TEST(testIO); + CPPUNIT_TEST(testNegativeIndexing); + CPPUNIT_TEST(testDeepCopy); + CPPUNIT_TEST(testMerge); + CPPUNIT_TEST(testVoxelizeActiveTiles); + CPPUNIT_TEST(testTopologyUnion); + CPPUNIT_TEST(testTopologyIntersection); + CPPUNIT_TEST(testTopologyDifference); + CPPUNIT_TEST(testFill); + CPPUNIT_TEST(testSignedFloodFill); + CPPUNIT_TEST(testPruneInactive); + CPPUNIT_TEST(testPruneLevelSet); + CPPUNIT_TEST(testTouchLeaf); + CPPUNIT_TEST(testProbeLeaf); + CPPUNIT_TEST(testAddLeaf); + CPPUNIT_TEST(testAddTile); + CPPUNIT_TEST(testGetNodes); + CPPUNIT_TEST(testStealNodes); + CPPUNIT_TEST(testProcessBBox); + CPPUNIT_TEST(testStealNode); +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + CPPUNIT_TEST(testNodeCount); +#endif + CPPUNIT_TEST(testRootNode); + CPPUNIT_TEST(testInternalNode); + CPPUNIT_TEST_SUITE_END(); + + void testChangeBackground(); + void testHalf(); + void testValues(); + void testSetValue(); + void testSetValueOnly(); + void testSetValueInPlace(); + void testEvalMinMax(); + void testResize(); + void testHasSameTopology(); + void testTopologyCopy(); + void testIterators(); + void testIO(); + void testNegativeIndexing(); + void testDeepCopy(); + void testMerge(); + void testVoxelizeActiveTiles(); + void testTopologyUnion(); + void testTopologyIntersection(); + void testTopologyDifference(); + void testFill(); + void testSignedFloodFill(); + void testPruneLevelSet(); + void testPruneInactive(); + void testTouchLeaf(); + void testProbeLeaf(); + void testAddLeaf(); + void testAddTile(); + void testGetNodes(); + void testStealNodes(); + void testProcessBBox(); + void testStealNode(); +#if OPENVDB_ABI_VERSION_NUMBER >= 7 + void testNodeCount(); +#endif + void testRootNode(); + void testInternalNode(); + +private: + template void testWriteHalf(); + template void doTestMerge(openvdb::MergePolicy); + template void doTestTopologyDifference(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestTree); + +void +TestTree::testChangeBackground() +{ + const int dim = 128; + const openvdb::Vec3f center(0.35f, 0.35f, 0.35f); + const float + radius = 0.15f, + voxelSize = 1.0f / (dim-1), + halfWidth = 4, + gamma = halfWidth * voxelSize; + using GridT = openvdb::FloatGrid; + const openvdb::Coord inside(int(center[0]*dim), int(center[1]*dim), int(center[2]*dim)); + const openvdb::Coord outside(dim); + + {//changeBackground + GridT::Ptr grid = openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, halfWidth); + openvdb::FloatTree& tree = grid->tree(); + + CPPUNIT_ASSERT(grid->tree().isValueOff(outside)); + ASSERT_DOUBLES_EXACTLY_EQUAL( gamma, tree.getValue(outside)); + + CPPUNIT_ASSERT(tree.isValueOff(inside)); + ASSERT_DOUBLES_EXACTLY_EQUAL(-gamma, tree.getValue(inside)); + + const float background = gamma*3.43f; + openvdb::tools::changeBackground(tree, background); + + CPPUNIT_ASSERT(grid->tree().isValueOff(outside)); + ASSERT_DOUBLES_EXACTLY_EQUAL( background, tree.getValue(outside)); + + CPPUNIT_ASSERT(tree.isValueOff(inside)); + ASSERT_DOUBLES_EXACTLY_EQUAL(-background, tree.getValue(inside)); + } + + {//changeLevelSetBackground + GridT::Ptr grid = openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, halfWidth); + openvdb::FloatTree& tree = grid->tree(); + + CPPUNIT_ASSERT(grid->tree().isValueOff(outside)); + ASSERT_DOUBLES_EXACTLY_EQUAL( gamma, tree.getValue(outside)); + + CPPUNIT_ASSERT(tree.isValueOff(inside)); + ASSERT_DOUBLES_EXACTLY_EQUAL(-gamma, tree.getValue(inside)); + + const float v1 = gamma*3.43f, v2 = -gamma*6.457f; + openvdb::tools::changeAsymmetricLevelSetBackground(tree, v1, v2); + + CPPUNIT_ASSERT(grid->tree().isValueOff(outside)); + ASSERT_DOUBLES_EXACTLY_EQUAL( v1, tree.getValue(outside)); + + CPPUNIT_ASSERT(tree.isValueOff(inside)); + ASSERT_DOUBLES_EXACTLY_EQUAL( v2, tree.getValue(inside)); + } +} + + +void +TestTree::testHalf() +{ + testWriteHalf(); + testWriteHalf(); + testWriteHalf(); + testWriteHalf(); + testWriteHalf(); + testWriteHalf(); + + // Verify that non-floating-point grids are saved correctly. + testWriteHalf(); + testWriteHalf(); + testWriteHalf(); +} + + +template +void +TestTree::testWriteHalf() +{ + using GridType = openvdb::Grid; + using ValueT = typename TreeType::ValueType; + ValueT background(5); + GridType grid(background); + + unittest_util::makeSphere(openvdb::Coord(64, 64, 64), + openvdb::Vec3f(35, 30, 40), + /*radius=*/10, grid, + /*dx=*/1.0f, unittest_util::SPHERE_DENSE); + CPPUNIT_ASSERT(!grid.tree().empty()); + + // Write grid blocks in both float and half formats. + std::ostringstream outFull(std::ios_base::binary); + grid.setSaveFloatAsHalf(false); + grid.writeBuffers(outFull); + outFull.flush(); + const size_t fullBytes = outFull.str().size(); + CPPUNIT_ASSERT_MESSAGE("wrote empty full float buffers", fullBytes > 0); + + std::ostringstream outHalf(std::ios_base::binary); + grid.setSaveFloatAsHalf(true); + grid.writeBuffers(outHalf); + outHalf.flush(); + const size_t halfBytes = outHalf.str().size(); + CPPUNIT_ASSERT_MESSAGE("wrote empty half float buffers", halfBytes > 0); + + if (openvdb::io::RealToHalf::isReal) { + // Verify that the half float file is "significantly smaller" than the full float file. + std::ostringstream ostr; + ostr << "half float buffers not significantly smaller than full float (" + << halfBytes << " vs. " << fullBytes << " bytes)"; + CPPUNIT_ASSERT_MESSAGE(ostr.str(), halfBytes < size_t(0.75 * double(fullBytes))); + } else { + // For non-real data types, "half float" and "full float" file sizes should be the same. + CPPUNIT_ASSERT_MESSAGE("full float and half float file sizes differ for data of type " + + std::string(openvdb::typeNameAsString()), halfBytes == fullBytes); + } + + // Read back the half float data (converting back to full float in the process), + // then write it out again in half float format. Verify that the resulting file + // is identical to the original half float file. + { + openvdb::Grid gridCopy(grid); + gridCopy.setSaveFloatAsHalf(true); + std::istringstream is(outHalf.str(), std::ios_base::binary); + + // Since the input stream doesn't include a VDB header with file format version info, + // tag the input stream explicitly with the current version number. + openvdb::io::setCurrentVersion(is); + + gridCopy.readBuffers(is); + + std::ostringstream outDiff(std::ios_base::binary); + gridCopy.writeBuffers(outDiff); + outDiff.flush(); + + CPPUNIT_ASSERT_MESSAGE("half-from-full and half-from-half buffers differ", + outHalf.str() == outDiff.str()); + } +} + + +void +TestTree::testValues() +{ + ValueType background=5.0f; + + { + const openvdb::Coord c0(5,10,20), c1(50000,20000,30000); + RootNodeType root_node(background); + const float v0=0.234f, v1=4.5678f; + CPPUNIT_ASSERT(root_node.empty()); + ASSERT_DOUBLES_EXACTLY_EQUAL(root_node.getValue(c0), background); + ASSERT_DOUBLES_EXACTLY_EQUAL(root_node.getValue(c1), background); + root_node.setValueOn(c0, v0); + root_node.setValueOn(c1, v1); + ASSERT_DOUBLES_EXACTLY_EQUAL(v0,root_node.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(v1,root_node.getValue(c1)); + int count=0; + for (int i =0; i<256; ++i) { + for (int j=0; j<256; ++j) { + for (int k=0; k<256; ++k) { + if (root_node.getValue(openvdb::Coord(i,j,k))<1.0f) ++count; + } + } + } + CPPUNIT_ASSERT(count == 1); + } + + { + const openvdb::Coord min(-30,-25,-60), max(60,80,100); + const openvdb::Coord c0(-5,-10,-20), c1(50,20,90), c2(59,67,89); + const float v0=0.234f, v1=4.5678f, v2=-5.673f; + RootNodeType root_node(background); + CPPUNIT_ASSERT(root_node.empty()); + ASSERT_DOUBLES_EXACTLY_EQUAL(background,root_node.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background,root_node.getValue(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background,root_node.getValue(c2)); + root_node.setValueOn(c0, v0); + root_node.setValueOn(c1, v1); + root_node.setValueOn(c2, v2); + ASSERT_DOUBLES_EXACTLY_EQUAL(v0,root_node.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(v1,root_node.getValue(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(v2,root_node.getValue(c2)); + int count=0; + for (int i =min[0]; i(const T& other) const { return value > other.value; } + bool operator==(const T& other) const { return value == other.value; } + + friend std::ostream& operator<<(std::ostream &stream, const T& other) + { + stream << other.value; + return stream; + } +}; + +} // namespace + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +OPENVDB_EXACT_IS_APPROX_EQUAL(FloatThrowOnCopy) + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +void +TestTree::testSetValueInPlace() +{ + using FloatThrowOnCopyTree = openvdb::tree::Tree4::Type; + using FloatThrowOnCopyGrid = openvdb::Grid; + + FloatThrowOnCopyGrid::registerGrid(); + + FloatThrowOnCopyTree tree; + const openvdb::Coord c0(5, 10, 20), c1(-5,-10,-20); + + // tile values can legitimately be copied to assess whether a change in value + // requires the tile to be voxelized, so activate and voxelize active tiles first + + tree.setActiveState(c0, true); + tree.setActiveState(c1, true); + + tree.voxelizeActiveTiles(/*threaded=*/true); + + CPPUNIT_ASSERT_NO_THROW(tree.modifyValue(c0, + [](FloatThrowOnCopy& lhs) { lhs.value = 1.4f; } + )); + + CPPUNIT_ASSERT_NO_THROW(tree.modifyValueAndActiveState(c1, + [](FloatThrowOnCopy& lhs, bool& b) { lhs.value = 2.7f; b = false; } + )); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.4f, tree.getValue(c0).value, 1.0e-7); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.7f, tree.getValue(c1).value, 1.0e-7); + + CPPUNIT_ASSERT(tree.isValueOn(c0)); + CPPUNIT_ASSERT(!tree.isValueOn(c1)); + + // use slower de-allocation to ensure that no value copying occurs + + tree.root().clear(); +} + +namespace { + +/// Helper function to test openvdb::tree::Tree::evalMinMax() for various tree types +template +void +evalMinMaxTest() +{ + using ValueT = typename TreeT::ValueType; + + struct Local { + static bool isEqual(const ValueT& a, const ValueT& b) { + using namespace openvdb; // for operator>() + return !(math::Abs(a - b) > zeroVal()); + } + }; + + const ValueT + zero = openvdb::zeroVal(), + minusTwo = zero + (-2), + plusTwo = zero + 2, + five = zero + 5; + + TreeT tree(/*background=*/five); + + // No set voxels (defaults to min = max = zero) + ValueT minVal = five, maxVal = five; + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT(Local::isEqual(minVal, zero)); + CPPUNIT_ASSERT(Local::isEqual(maxVal, zero)); + + // Only one set voxel + tree.setValue(openvdb::Coord(0, 0, 0), minusTwo); + minVal = maxVal = five; + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT(Local::isEqual(minVal, minusTwo)); + CPPUNIT_ASSERT(Local::isEqual(maxVal, minusTwo)); + + // Multiple set voxels, single value + tree.setValue(openvdb::Coord(10, 10, 10), minusTwo); + minVal = maxVal = five; + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT(Local::isEqual(minVal, minusTwo)); + CPPUNIT_ASSERT(Local::isEqual(maxVal, minusTwo)); + + // Multiple set voxels, multiple values + tree.setValue(openvdb::Coord(10, 10, 10), plusTwo); + tree.setValue(openvdb::Coord(-10, -10, -10), zero); + minVal = maxVal = five; + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT(Local::isEqual(minVal, minusTwo)); + CPPUNIT_ASSERT(Local::isEqual(maxVal, plusTwo)); +} + +/// Specialization for boolean trees +template<> +void +evalMinMaxTest() +{ + openvdb::BoolTree tree(/*background=*/false); + + // No set voxels (defaults to min = max = zero) + bool minVal = true, maxVal = false; + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(false, minVal); + CPPUNIT_ASSERT_EQUAL(false, maxVal); + + // Only one set voxel + tree.setValue(openvdb::Coord(0, 0, 0), true); + minVal = maxVal = false; + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(true, minVal); + CPPUNIT_ASSERT_EQUAL(true, maxVal); + + // Multiple set voxels, single value + tree.setValue(openvdb::Coord(-10, -10, -10), true); + minVal = maxVal = false; + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(true, minVal); + CPPUNIT_ASSERT_EQUAL(true, maxVal); + + // Multiple set voxels, multiple values + tree.setValue(openvdb::Coord(10, 10, 10), false); + minVal = true; maxVal = false; + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(false, minVal); + CPPUNIT_ASSERT_EQUAL(true, maxVal); +} + +/// Specialization for string trees +template<> +void +evalMinMaxTest() +{ + const std::string + echidna("echidna"), loris("loris"), pangolin("pangolin"); + + openvdb::StringTree tree(/*background=*/loris); + + // No set voxels (defaults to min = max = zero) + std::string minVal, maxVal; + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(std::string(), minVal); + CPPUNIT_ASSERT_EQUAL(std::string(), maxVal); + + // Only one set voxel + tree.setValue(openvdb::Coord(0, 0, 0), pangolin); + minVal.clear(); maxVal.clear(); + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(pangolin, minVal); + CPPUNIT_ASSERT_EQUAL(pangolin, maxVal); + + // Multiple set voxels, single value + tree.setValue(openvdb::Coord(-10, -10, -10), pangolin); + minVal.clear(); maxVal.clear(); + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(pangolin, minVal); + CPPUNIT_ASSERT_EQUAL(pangolin, maxVal); + + // Multiple set voxels, multiple values + tree.setValue(openvdb::Coord(10, 10, 10), echidna); + minVal.clear(); maxVal.clear(); + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(echidna, minVal); + CPPUNIT_ASSERT_EQUAL(pangolin, maxVal); +} + +/// Specialization for Coord trees +template<> +void +evalMinMaxTest() +{ + using CoordTree = openvdb::tree::Tree4::Type; + const openvdb::Coord backg(5,4,-6), a(5,4,-7), b(5,5,-6); + + CoordTree tree(backg); + + // No set voxels (defaults to min = max = zero) + openvdb::Coord minVal=openvdb::Coord::max(), maxVal=openvdb::Coord::min(); + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), minVal); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(0), maxVal); + + // Only one set voxel + tree.setValue(openvdb::Coord(0, 0, 0), a); + minVal=openvdb::Coord::max(); + maxVal=openvdb::Coord::min(); + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(a, minVal); + CPPUNIT_ASSERT_EQUAL(a, maxVal); + + // Multiple set voxels + tree.setValue(openvdb::Coord(-10, -10, -10), b); + minVal=openvdb::Coord::max(); + maxVal=openvdb::Coord::min(); + tree.evalMinMax(minVal, maxVal); + CPPUNIT_ASSERT_EQUAL(a, minVal); + CPPUNIT_ASSERT_EQUAL(b, maxVal); +} + +} // unnamed namespace + +void +TestTree::testEvalMinMax() +{ + evalMinMaxTest(); + evalMinMaxTest(); + evalMinMaxTest(); + evalMinMaxTest(); + evalMinMaxTest(); + evalMinMaxTest(); + evalMinMaxTest(); +} + + +void +TestTree::testResize() +{ + ValueType background=5.0f; + //use this when resize is implemented + RootNodeType root_node(background); + CPPUNIT_ASSERT(root_node.getLevel()==3); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, root_node.getValue(openvdb::Coord(5,10,20))); + //fprintf(stdout,"Root grid dim=(%i,%i,%i)\n", + // root_node.getGridDim(0), root_node.getGridDim(1), root_node.getGridDim(2)); + root_node.setValueOn(openvdb::Coord(5,10,20),0.234f); + ASSERT_DOUBLES_EXACTLY_EQUAL(root_node.getValue(openvdb::Coord(5,10,20)) , 0.234f); + root_node.setValueOn(openvdb::Coord(500,200,300),4.5678f); + ASSERT_DOUBLES_EXACTLY_EQUAL(root_node.getValue(openvdb::Coord(500,200,300)) , 4.5678f); + { + ValueType sum=0.0f; + for (RootNodeType::ChildOnIter root_iter = root_node.beginChildOn(); + root_iter.test(); ++root_iter) + { + for (InternalNodeType2::ChildOnIter internal_iter2 = root_iter->beginChildOn(); + internal_iter2.test(); ++internal_iter2) + { + for (InternalNodeType1::ChildOnIter internal_iter1 = + internal_iter2->beginChildOn(); internal_iter1.test(); ++internal_iter1) + { + for (LeafNodeType::ValueOnIter block_iter = + internal_iter1->beginValueOn(); block_iter.test(); ++block_iter) + { + sum += *block_iter; + } + } + } + } + ASSERT_DOUBLES_EXACTLY_EQUAL(sum, (0.234f + 4.5678f)); + } + + CPPUNIT_ASSERT(root_node.getLevel()==3); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, root_node.getValue(openvdb::Coord(5,11,20))); + { + ValueType sum=0.0f; + for (RootNodeType::ChildOnIter root_iter = root_node.beginChildOn(); + root_iter.test(); ++root_iter) + { + for (InternalNodeType2::ChildOnIter internal_iter2 = root_iter->beginChildOn(); + internal_iter2.test(); ++internal_iter2) + { + for (InternalNodeType1::ChildOnIter internal_iter1 = + internal_iter2->beginChildOn(); internal_iter1.test(); ++internal_iter1) + { + for (LeafNodeType::ValueOnIter block_iter = + internal_iter1->beginValueOn(); block_iter.test(); ++block_iter) + { + sum += *block_iter; + } + } + } + } + ASSERT_DOUBLES_EXACTLY_EQUAL(sum, (0.234f + 4.5678f)); + } + +} + + +void +TestTree::testHasSameTopology() +{ + // Test using trees of the same type. + { + const float background1=5.0f; + openvdb::FloatTree tree1(background1); + + const float background2=6.0f; + openvdb::FloatTree tree2(background2); + + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + + tree1.setValue(openvdb::Coord(-10,40,845),3.456f); + CPPUNIT_ASSERT(!tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(!tree2.hasSameTopology(tree1)); + + tree2.setValue(openvdb::Coord(-10,40,845),-3.456f); + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + + tree1.setValue(openvdb::Coord(1,-500,-8), 1.0f); + CPPUNIT_ASSERT(!tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(!tree2.hasSameTopology(tree1)); + + tree2.setValue(openvdb::Coord(1,-500,-8),1.0f); + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + } + // Test using trees of different types. + { + const float background1=5.0f; + openvdb::FloatTree tree1(background1); + + const openvdb::Vec3f background2(1.0f,3.4f,6.0f); + openvdb::Vec3fTree tree2(background2); + + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + + tree1.setValue(openvdb::Coord(-10,40,845),3.456f); + CPPUNIT_ASSERT(!tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(!tree2.hasSameTopology(tree1)); + + tree2.setValue(openvdb::Coord(-10,40,845),openvdb::Vec3f(1.0f,2.0f,-3.0f)); + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + + tree1.setValue(openvdb::Coord(1,-500,-8), 1.0f); + CPPUNIT_ASSERT(!tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(!tree2.hasSameTopology(tree1)); + + tree2.setValue(openvdb::Coord(1,-500,-8),openvdb::Vec3f(1.0f,2.0f,-3.0f)); + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + } +} + + +void +TestTree::testTopologyCopy() +{ + // Test using trees of the same type. + { + const float background1=5.0f; + openvdb::FloatTree tree1(background1); + tree1.setValue(openvdb::Coord(-10,40,845),3.456f); + tree1.setValue(openvdb::Coord(1,-50,-8), 1.0f); + + const float background2=6.0f, setValue2=3.0f; + openvdb::FloatTree tree2(tree1,background2,setValue2,openvdb::TopologyCopy()); + + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + + ASSERT_DOUBLES_EXACTLY_EQUAL(background2, tree2.getValue(openvdb::Coord(1,2,3))); + ASSERT_DOUBLES_EXACTLY_EQUAL(setValue2, tree2.getValue(openvdb::Coord(-10,40,845))); + ASSERT_DOUBLES_EXACTLY_EQUAL(setValue2, tree2.getValue(openvdb::Coord(1,-50,-8))); + + tree1.setValue(openvdb::Coord(1,-500,-8), 1.0f); + CPPUNIT_ASSERT(!tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(!tree2.hasSameTopology(tree1)); + + tree2.setValue(openvdb::Coord(1,-500,-8),1.0f); + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + } + // Test using trees of different types. + { + const openvdb::Vec3f background1(1.0f,3.4f,6.0f); + openvdb::Vec3fTree tree1(background1); + tree1.setValue(openvdb::Coord(-10,40,845),openvdb::Vec3f(3.456f,-2.3f,5.6f)); + tree1.setValue(openvdb::Coord(1,-50,-8), openvdb::Vec3f(1.0f,3.0f,4.5f)); + + const float background2=6.0f, setValue2=3.0f; + openvdb::FloatTree tree2(tree1,background2,setValue2,openvdb::TopologyCopy()); + + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + + ASSERT_DOUBLES_EXACTLY_EQUAL(background2, tree2.getValue(openvdb::Coord(1,2,3))); + ASSERT_DOUBLES_EXACTLY_EQUAL(setValue2, tree2.getValue(openvdb::Coord(-10,40,845))); + ASSERT_DOUBLES_EXACTLY_EQUAL(setValue2, tree2.getValue(openvdb::Coord(1,-50,-8))); + + tree1.setValue(openvdb::Coord(1,-500,-8), openvdb::Vec3f(1.0f,0.0f,-3.0f)); + CPPUNIT_ASSERT(!tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(!tree2.hasSameTopology(tree1)); + + tree2.setValue(openvdb::Coord(1,-500,-8), 1.0f); + CPPUNIT_ASSERT(tree1.hasSameTopology(tree2)); + CPPUNIT_ASSERT(tree2.hasSameTopology(tree1)); + } +} + + +void +TestTree::testIterators() +{ + ValueType background=5.0f; + RootNodeType root_node(background); + root_node.setValueOn(openvdb::Coord(5,10,20),0.234f); + root_node.setValueOn(openvdb::Coord(50000,20000,30000),4.5678f); + { + ValueType sum=0.0f; + for (RootNodeType::ChildOnIter root_iter = root_node.beginChildOn(); + root_iter.test(); ++root_iter) + { + for (InternalNodeType2::ChildOnIter internal_iter2 = root_iter->beginChildOn(); + internal_iter2.test(); ++internal_iter2) + { + for (InternalNodeType1::ChildOnIter internal_iter1 = + internal_iter2->beginChildOn(); internal_iter1.test(); ++internal_iter1) + { + for (LeafNodeType::ValueOnIter block_iter = + internal_iter1->beginValueOn(); block_iter.test(); ++block_iter) + { + sum += *block_iter; + } + } + } + } + ASSERT_DOUBLES_EXACTLY_EQUAL((0.234f + 4.5678f), sum); + } + { + // As above, but using dense iterators. + ValueType sum = 0.0f, val = 0.0f; + for (RootNodeType::ChildAllIter rootIter = root_node.beginChildAll(); + rootIter.test(); ++rootIter) + { + if (!rootIter.isChildNode()) continue; + + for (InternalNodeType2::ChildAllIter internalIter2 = + rootIter.probeChild(val)->beginChildAll(); internalIter2; ++internalIter2) + { + if (!internalIter2.isChildNode()) continue; + + for (InternalNodeType1::ChildAllIter internalIter1 = + internalIter2.probeChild(val)->beginChildAll(); internalIter1; ++internalIter1) + { + if (!internalIter1.isChildNode()) continue; + + for (LeafNodeType::ValueOnIter leafIter = + internalIter1.probeChild(val)->beginValueOn(); leafIter; ++leafIter) + { + sum += *leafIter; + } + } + } + } + ASSERT_DOUBLES_EXACTLY_EQUAL((0.234f + 4.5678f), sum); + } + { + ValueType v_sum=0.0f; + openvdb::Coord xyz0, xyz1, xyz2, xyz3, xyzSum(0, 0, 0); + for (RootNodeType::ChildOnIter root_iter = root_node.beginChildOn(); + root_iter.test(); ++root_iter) + { + root_iter.getCoord(xyz3); + for (InternalNodeType2::ChildOnIter internal_iter2 = root_iter->beginChildOn(); + internal_iter2.test(); ++internal_iter2) + { + internal_iter2.getCoord(xyz2); + xyz2 = xyz2 - internal_iter2.parent().origin(); + for (InternalNodeType1::ChildOnIter internal_iter1 = + internal_iter2->beginChildOn(); internal_iter1.test(); ++internal_iter1) + { + internal_iter1.getCoord(xyz1); + xyz1 = xyz1 - internal_iter1.parent().origin(); + for (LeafNodeType::ValueOnIter block_iter = + internal_iter1->beginValueOn(); block_iter.test(); ++block_iter) + { + block_iter.getCoord(xyz0); + xyz0 = xyz0 - block_iter.parent().origin(); + v_sum += *block_iter; + xyzSum = xyzSum + xyz0 + xyz1 + xyz2 + xyz3; + } + } + } + } + ASSERT_DOUBLES_EXACTLY_EQUAL((0.234f + 4.5678f), v_sum); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(5 + 50000, 10 + 20000, 20 + 30000), xyzSum); + } +} + + +void +TestTree::testIO() +{ + const char* filename = "testIO.dbg"; + openvdb::SharedPtr scopedFile(filename, ::remove); + { + ValueType background=5.0f; + RootNodeType root_node(background); + root_node.setValueOn(openvdb::Coord(5,10,20),0.234f); + root_node.setValueOn(openvdb::Coord(50000,20000,30000),4.5678f); + + std::ofstream os(filename, std::ios_base::binary); + root_node.writeTopology(os); + root_node.writeBuffers(os); + CPPUNIT_ASSERT(!os.fail()); + } + { + ValueType background=2.0f; + RootNodeType root_node(background); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, root_node.getValue(openvdb::Coord(5,10,20))); + { + std::ifstream is(filename, std::ios_base::binary); + // Since the test file doesn't include a VDB header with file format version info, + // tag the input stream explicitly with the current version number. + openvdb::io::setCurrentVersion(is); + root_node.readTopology(is); + root_node.readBuffers(is); + CPPUNIT_ASSERT(!is.fail()); + } + + ASSERT_DOUBLES_EXACTLY_EQUAL(0.234f, root_node.getValue(openvdb::Coord(5,10,20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(5.0f, root_node.getValue(openvdb::Coord(5,11,20))); + ValueType sum=0.0f; + for (RootNodeType::ChildOnIter root_iter = root_node.beginChildOn(); + root_iter.test(); ++root_iter) + { + for (InternalNodeType2::ChildOnIter internal_iter2 = root_iter->beginChildOn(); + internal_iter2.test(); ++internal_iter2) + { + for (InternalNodeType1::ChildOnIter internal_iter1 = + internal_iter2->beginChildOn(); internal_iter1.test(); ++internal_iter1) + { + for (LeafNodeType::ValueOnIter block_iter = + internal_iter1->beginValueOn(); block_iter.test(); ++block_iter) + { + sum += *block_iter; + } + } + } + } + ASSERT_DOUBLES_EXACTLY_EQUAL(sum, (0.234f + 4.5678f)); + } +} + + +void +TestTree::testNegativeIndexing() +{ + ValueType background=5.0f; + openvdb::FloatTree tree(background); + CPPUNIT_ASSERT(tree.empty()); + ASSERT_DOUBLES_EXACTLY_EQUAL(tree.getValue(openvdb::Coord(5,-10,20)), background); + ASSERT_DOUBLES_EXACTLY_EQUAL(tree.getValue(openvdb::Coord(-5000,2000,3000)), background); + tree.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree.setValue(openvdb::Coord(-5, 10, 20),0.1f); + tree.setValue(openvdb::Coord( 5,-10, 20),0.2f); + tree.setValue(openvdb::Coord( 5, 10,-20),0.3f); + tree.setValue(openvdb::Coord(-5,-10, 20),0.4f); + tree.setValue(openvdb::Coord(-5, 10,-20),0.5f); + tree.setValue(openvdb::Coord( 5,-10,-20),0.6f); + tree.setValue(openvdb::Coord(-5,-10,-20),0.7f); + tree.setValue(openvdb::Coord(-5000, 2000,-3000),4.5678f); + tree.setValue(openvdb::Coord( 5000,-2000,-3000),4.5678f); + tree.setValue(openvdb::Coord(-5000,-2000, 3000),4.5678f); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.0f, tree.getValue(openvdb::Coord( 5, 10, 20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.1f, tree.getValue(openvdb::Coord(-5, 10, 20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.2f, tree.getValue(openvdb::Coord( 5,-10, 20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.3f, tree.getValue(openvdb::Coord( 5, 10,-20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.4f, tree.getValue(openvdb::Coord(-5,-10, 20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.5f, tree.getValue(openvdb::Coord(-5, 10,-20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.6f, tree.getValue(openvdb::Coord( 5,-10,-20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.7f, tree.getValue(openvdb::Coord(-5,-10,-20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(4.5678f, tree.getValue(openvdb::Coord(-5000, 2000,-3000))); + ASSERT_DOUBLES_EXACTLY_EQUAL(4.5678f, tree.getValue(openvdb::Coord( 5000,-2000,-3000))); + ASSERT_DOUBLES_EXACTLY_EQUAL(4.5678f, tree.getValue(openvdb::Coord(-5000,-2000, 3000))); + int count=0; + for (int i =-25; i<25; ++i) { + for (int j=-25; j<25; ++j) { + for (int k=-25; k<25; ++k) { + if (tree.getValue(openvdb::Coord(i,j,k))<1.0f) { + //fprintf(stderr,"(%i,%i,%i)=%f\n",i,j,k,tree.getValue(openvdb::Coord(i,j,k))); + ++count; + } + } + } + } + CPPUNIT_ASSERT(count == 8); + int count2 = 0; + openvdb::Coord xyz; + for (openvdb::FloatTree::ValueOnCIter iter = tree.cbeginValueOn(); iter; ++iter) { + ++count2; + xyz = iter.getCoord(); + //std::cerr << xyz << " = " << *iter << "\n"; + } + CPPUNIT_ASSERT(count2 == 11); + CPPUNIT_ASSERT(tree.activeVoxelCount() == 11); + { + count2 = 0; + for (openvdb::FloatTree::ValueOnCIter iter = tree.cbeginValueOn(); iter; ++iter) { + ++count2; + xyz = iter.getCoord(); + //std::cerr << xyz << " = " << *iter << "\n"; + } + CPPUNIT_ASSERT(count2 == 11); + CPPUNIT_ASSERT(tree.activeVoxelCount() == 11); + } +} + + +void +TestTree::testDeepCopy() +{ + // set up a tree + const float fillValue1=5.0f; + openvdb::FloatTree tree1(fillValue1); + tree1.setValue(openvdb::Coord(-10,40,845), 3.456f); + tree1.setValue(openvdb::Coord(1,-50,-8), 1.0f); + + // make a deep copy of the tree + openvdb::TreeBase::Ptr newTree = tree1.copy(); + + // cast down to the concrete type to query values + openvdb::FloatTree *pTree2 = dynamic_cast(newTree.get()); + + // compare topology + CPPUNIT_ASSERT(tree1.hasSameTopology(*pTree2)); + CPPUNIT_ASSERT(pTree2->hasSameTopology(tree1)); + + // trees should be equal + ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue1, pTree2->getValue(openvdb::Coord(1,2,3))); + ASSERT_DOUBLES_EXACTLY_EQUAL(3.456f, pTree2->getValue(openvdb::Coord(-10,40,845))); + ASSERT_DOUBLES_EXACTLY_EQUAL(1.0f, pTree2->getValue(openvdb::Coord(1,-50,-8))); + + // change 1 value in tree2 + openvdb::Coord changeCoord(1, -500, -8); + pTree2->setValue(changeCoord, 1.0f); + + // topology should no longer match + CPPUNIT_ASSERT(!tree1.hasSameTopology(*pTree2)); + CPPUNIT_ASSERT(!pTree2->hasSameTopology(tree1)); + + // query changed value and make sure it's different between trees + ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue1, tree1.getValue(changeCoord)); + ASSERT_DOUBLES_EXACTLY_EQUAL(1.0f, pTree2->getValue(changeCoord)); +} + + +void +TestTree::testMerge() +{ + ValueType background=5.0f; + openvdb::FloatTree tree0(background), tree1(background), tree2(background); + CPPUNIT_ASSERT(tree2.empty()); + tree0.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree0.setValue(openvdb::Coord(-5, 10, 20),0.1f); + tree0.setValue(openvdb::Coord( 5,-10, 20),0.2f); + tree0.setValue(openvdb::Coord( 5, 10,-20),0.3f); + tree1.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree1.setValue(openvdb::Coord(-5, 10, 20),0.1f); + tree1.setValue(openvdb::Coord( 5,-10, 20),0.2f); + tree1.setValue(openvdb::Coord( 5, 10,-20),0.3f); + + tree0.setValue(openvdb::Coord(-5,-10, 20),0.4f); + tree0.setValue(openvdb::Coord(-5, 10,-20),0.5f); + tree0.setValue(openvdb::Coord( 5,-10,-20),0.6f); + tree0.setValue(openvdb::Coord(-5,-10,-20),0.7f); + tree0.setValue(openvdb::Coord(-5000, 2000,-3000),4.5678f); + tree0.setValue(openvdb::Coord( 5000,-2000,-3000),4.5678f); + tree0.setValue(openvdb::Coord(-5000,-2000, 3000),4.5678f); + tree2.setValue(openvdb::Coord(-5,-10, 20),0.4f); + tree2.setValue(openvdb::Coord(-5, 10,-20),0.5f); + tree2.setValue(openvdb::Coord( 5,-10,-20),0.6f); + tree2.setValue(openvdb::Coord(-5,-10,-20),0.7f); + tree2.setValue(openvdb::Coord(-5000, 2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord( 5000,-2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord(-5000,-2000, 3000),4.5678f); + + CPPUNIT_ASSERT(tree0.leafCount()!=tree1.leafCount()); + CPPUNIT_ASSERT(tree0.leafCount()!=tree2.leafCount()); + + CPPUNIT_ASSERT(!tree2.empty()); + tree1.merge(tree2, openvdb::MERGE_ACTIVE_STATES); + CPPUNIT_ASSERT(tree2.empty()); + CPPUNIT_ASSERT(tree0.leafCount()==tree1.leafCount()); + CPPUNIT_ASSERT(tree0.nonLeafCount()==tree1.nonLeafCount()); + CPPUNIT_ASSERT(tree0.activeLeafVoxelCount()==tree1.activeLeafVoxelCount()); + CPPUNIT_ASSERT(tree0.inactiveLeafVoxelCount()==tree1.inactiveLeafVoxelCount()); + CPPUNIT_ASSERT(tree0.activeVoxelCount()==tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree0.inactiveVoxelCount()==tree1.inactiveVoxelCount()); + + for (openvdb::FloatTree::ValueOnCIter iter0 = tree0.cbeginValueOn(); iter0; ++iter0) { + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter0,tree1.getValue(iter0.getCoord())); + } + + // Test active tile support. + { + using namespace openvdb; + FloatTree treeA(/*background*/0.0), treeB(/*background*/0.0); + + treeA.fill(CoordBBox(Coord(16,16,16), Coord(31,31,31)), /*value*/1.0); + treeB.fill(CoordBBox(Coord(0,0,0), Coord(15,15,15)), /*value*/1.0); + + CPPUNIT_ASSERT_EQUAL(4096, int(treeA.activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(4096, int(treeB.activeVoxelCount())); + + treeA.merge(treeB, MERGE_ACTIVE_STATES); + + CPPUNIT_ASSERT_EQUAL(8192, int(treeA.activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(0, int(treeB.activeVoxelCount())); + } + + doTestMerge(openvdb::MERGE_NODES); + doTestMerge(openvdb::MERGE_ACTIVE_STATES); + doTestMerge(openvdb::MERGE_ACTIVE_STATES_AND_NODES); + + doTestMerge(openvdb::MERGE_NODES); + doTestMerge(openvdb::MERGE_ACTIVE_STATES); + doTestMerge(openvdb::MERGE_ACTIVE_STATES_AND_NODES); +} + + +template +void +TestTree::doTestMerge(openvdb::MergePolicy policy) +{ + using namespace openvdb; + + TreeType treeA, treeB; + + using RootT = typename TreeType::RootNodeType; + using LeafT = typename TreeType::LeafNodeType; + + const typename TreeType::ValueType val(1); + const int + depth = static_cast(treeA.treeDepth()), + leafDim = static_cast(LeafT::dim()), + leafSize = static_cast(LeafT::size()); + // Coords that are in a different top-level branch than (0, 0, 0) + const Coord pos(static_cast(RootT::getChildDim())); + + treeA.setValueOff(pos, val); + treeA.setValueOff(-pos, val); + + treeB.setValueOff(Coord(0), val); + treeB.fill(CoordBBox(pos, pos.offsetBy(leafDim - 1)), val, /*active=*/true); + treeB.setValueOn(-pos, val); + + // treeA treeB . + // . + // R R . + // / \ /|\ . + // I I I I I . + // / \ / | \ . + // I I I I I . + // / \ / | on x SIZE . + // L L L L . + // off off on off . + + CPPUNIT_ASSERT_EQUAL(0, int(treeA.activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(leafSize + 1, int(treeB.activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(2, int(treeA.leafCount())); + CPPUNIT_ASSERT_EQUAL(2, int(treeB.leafCount())); + CPPUNIT_ASSERT_EQUAL(2*(depth-2)+1, int(treeA.nonLeafCount())); // 2 branches (II+II+R) + CPPUNIT_ASSERT_EQUAL(3*(depth-2)+1, int(treeB.nonLeafCount())); // 3 branches (II+II+II+R) + + treeA.merge(treeB, policy); + + // MERGE_NODES MERGE_ACTIVE_STATES MERGE_ACTIVE_STATES_AND_NODES . + // . + // R R R . + // /|\ /|\ /|\ . + // I I I I I I I I I . + // / | \ / | \ / | \ . + // I I I I I I I I I . + // / | \ / | on x SIZE / | \ . + // L L L L L L L L . + // off off off on off on off on x SIZE . + + switch (policy) { + case MERGE_NODES: + CPPUNIT_ASSERT_EQUAL(0, int(treeA.activeVoxelCount())); + CPPUNIT_ASSERT_EQUAL(2 + 1, int(treeA.leafCount())); // 1 leaf node stolen from B + CPPUNIT_ASSERT_EQUAL(3*(depth-2)+1, int(treeA.nonLeafCount())); // 3 branches (II+II+II+R) + break; + case MERGE_ACTIVE_STATES: + CPPUNIT_ASSERT_EQUAL(2, int(treeA.leafCount())); // 1 leaf stolen, 1 replaced with tile + CPPUNIT_ASSERT_EQUAL(3*(depth-2)+1, int(treeA.nonLeafCount())); // 3 branches (II+II+II+R) + CPPUNIT_ASSERT_EQUAL(leafSize + 1, int(treeA.activeVoxelCount())); + break; + case MERGE_ACTIVE_STATES_AND_NODES: + CPPUNIT_ASSERT_EQUAL(2 + 1, int(treeA.leafCount())); // 1 leaf node stolen from B + CPPUNIT_ASSERT_EQUAL(3*(depth-2)+1, int(treeA.nonLeafCount())); // 3 branches (II+II+II+R) + CPPUNIT_ASSERT_EQUAL(leafSize + 1, int(treeA.activeVoxelCount())); + break; + } + CPPUNIT_ASSERT(treeB.empty()); +} + + +void +TestTree::testVoxelizeActiveTiles() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + // Use a small custom tree so we don't run out of memory when + // tiles are converted to dense leafs :) + using MyTree = openvdb::tree::Tree4::Type; + float background=5.0f; + const Coord xyz[] = {Coord(-1,-2,-3),Coord( 1, 2, 3)}; + //check two leaf nodes and two tiles at each level 1, 2 and 3 + const int tile_size[4]={0, 1<<2, 1<<(2*2), 1<<(3*2)}; + // serial version + for (int level=0; level<=3; ++level) { + + MyTree tree(background); + CPPUNIT_ASSERT_EQUAL(-1,tree.getValueDepth(xyz[0])); + CPPUNIT_ASSERT_EQUAL(-1,tree.getValueDepth(xyz[1])); + + if (level==0) { + tree.setValue(xyz[0], 1.0f); + tree.setValue(xyz[1], 1.0f); + } else { + const int n = tile_size[level]; + tree.fill(CoordBBox::createCube(Coord(-n,-n,-n), n), 1.0f, true); + tree.fill(CoordBBox::createCube(Coord( 0, 0, 0), n), 1.0f, true); + } + + CPPUNIT_ASSERT_EQUAL(3-level,tree.getValueDepth(xyz[0])); + CPPUNIT_ASSERT_EQUAL(3-level,tree.getValueDepth(xyz[1])); + + tree.voxelizeActiveTiles(false); + + CPPUNIT_ASSERT_EQUAL(3 ,tree.getValueDepth(xyz[0])); + CPPUNIT_ASSERT_EQUAL(3 ,tree.getValueDepth(xyz[1])); + } + // multi-threaded version + for (int level=0; level<=3; ++level) { + + MyTree tree(background); + CPPUNIT_ASSERT_EQUAL(-1,tree.getValueDepth(xyz[0])); + CPPUNIT_ASSERT_EQUAL(-1,tree.getValueDepth(xyz[1])); + + if (level==0) { + tree.setValue(xyz[0], 1.0f); + tree.setValue(xyz[1], 1.0f); + } else { + const int n = tile_size[level]; + tree.fill(CoordBBox::createCube(Coord(-n,-n,-n), n), 1.0f, true); + tree.fill(CoordBBox::createCube(Coord( 0, 0, 0), n), 1.0f, true); + } + + CPPUNIT_ASSERT_EQUAL(3-level,tree.getValueDepth(xyz[0])); + CPPUNIT_ASSERT_EQUAL(3-level,tree.getValueDepth(xyz[1])); + + tree.voxelizeActiveTiles(true); + + CPPUNIT_ASSERT_EQUAL(3 ,tree.getValueDepth(xyz[0])); + CPPUNIT_ASSERT_EQUAL(3 ,tree.getValueDepth(xyz[1])); + } +#if 0 + const CoordBBox bbox(openvdb::Coord(-30,-50,-30), openvdb::Coord(530,610,623)); + {// benchmark serial + MyTree tree(background); + tree.sparseFill( bbox, 1.0f, /*state*/true); + openvdb::util::CpuTimer timer("\nserial voxelizeActiveTiles"); + tree.voxelizeActiveTiles(/*threaded*/false); + timer.stop(); + } + {// benchmark parallel + MyTree tree(background); + tree.sparseFill( bbox, 1.0f, /*state*/true); + openvdb::util::CpuTimer timer("\nparallel voxelizeActiveTiles"); + tree.voxelizeActiveTiles(/*threaded*/true); + timer.stop(); + } +#endif +} + + +void +TestTree::testTopologyUnion() +{ + {//super simple test with only two active values + const ValueType background=0.0f; + openvdb::FloatTree tree0(background), tree1(background); + tree0.setValue(openvdb::Coord( 500, 300, 200), 1.0f); + tree1.setValue(openvdb::Coord( 8, 11, 11), 2.0f); + openvdb::FloatTree tree2(tree1); + + tree1.topologyUnion(tree0); + + for (openvdb::FloatTree::ValueOnCIter iter = tree0.cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT(tree1.isValueOn(iter.getCoord())); + } + for (openvdb::FloatTree::ValueOnCIter iter = tree2.cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT(tree1.isValueOn(iter.getCoord())); + } + for (openvdb::FloatTree::ValueOnCIter iter = tree1.cbeginValueOn(); iter; ++iter) { + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter,tree2.getValue(iter.getCoord())); + } + } + {// test using setValue + ValueType background=5.0f; + openvdb::FloatTree tree0(background), tree1(background), tree2(background); + CPPUNIT_ASSERT(tree2.empty()); + // tree0 = tree1.topologyUnion(tree2) + tree0.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree0.setValue(openvdb::Coord(-5, 10, 20),0.1f); + tree0.setValue(openvdb::Coord( 5,-10, 20),0.2f); + tree0.setValue(openvdb::Coord( 5, 10,-20),0.3f); + tree1.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree1.setValue(openvdb::Coord(-5, 10, 20),0.1f); + tree1.setValue(openvdb::Coord( 5,-10, 20),0.2f); + tree1.setValue(openvdb::Coord( 5, 10,-20),0.3f); + + tree0.setValue(openvdb::Coord(-5,-10, 20),background); + tree0.setValue(openvdb::Coord(-5, 10,-20),background); + tree0.setValue(openvdb::Coord( 5,-10,-20),background); + tree0.setValue(openvdb::Coord(-5,-10,-20),background); + tree0.setValue(openvdb::Coord(-5000, 2000,-3000),background); + tree0.setValue(openvdb::Coord( 5000,-2000,-3000),background); + tree0.setValue(openvdb::Coord(-5000,-2000, 3000),background); + tree2.setValue(openvdb::Coord(-5,-10, 20),0.4f); + tree2.setValue(openvdb::Coord(-5, 10,-20),0.5f); + tree2.setValue(openvdb::Coord( 5,-10,-20),0.6f); + tree2.setValue(openvdb::Coord(-5,-10,-20),0.7f); + tree2.setValue(openvdb::Coord(-5000, 2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord( 5000,-2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord(-5000,-2000, 3000),4.5678f); + + // tree3 has the same topology as tree2 but a different value type + const openvdb::Vec3f background2(1.0f,3.4f,6.0f), vec_val(3.1f,5.3f,-9.5f); + openvdb::Vec3fTree tree3(background2); + for (openvdb::FloatTree::ValueOnCIter iter2 = tree2.cbeginValueOn(); iter2; ++iter2) { + tree3.setValue(iter2.getCoord(), vec_val); + } + + CPPUNIT_ASSERT(tree0.leafCount()!=tree1.leafCount()); + CPPUNIT_ASSERT(tree0.leafCount()!=tree2.leafCount()); + CPPUNIT_ASSERT(tree0.leafCount()!=tree3.leafCount()); + + CPPUNIT_ASSERT(!tree2.empty()); + CPPUNIT_ASSERT(!tree3.empty()); + openvdb::FloatTree tree1_copy(tree1); + + //tree1.topologyUnion(tree2);//should make tree1 = tree0 + tree1.topologyUnion(tree3);//should make tree1 = tree0 + + CPPUNIT_ASSERT(tree0.leafCount()==tree1.leafCount()); + CPPUNIT_ASSERT(tree0.nonLeafCount()==tree1.nonLeafCount()); + CPPUNIT_ASSERT(tree0.activeLeafVoxelCount()==tree1.activeLeafVoxelCount()); + CPPUNIT_ASSERT(tree0.inactiveLeafVoxelCount()==tree1.inactiveLeafVoxelCount()); + CPPUNIT_ASSERT(tree0.activeVoxelCount()==tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree0.inactiveVoxelCount()==tree1.inactiveVoxelCount()); + + CPPUNIT_ASSERT(tree1.hasSameTopology(tree0)); + CPPUNIT_ASSERT(tree0.hasSameTopology(tree1)); + + for (openvdb::FloatTree::ValueOnCIter iter2 = tree2.cbeginValueOn(); iter2; ++iter2) { + CPPUNIT_ASSERT(tree1.isValueOn(iter2.getCoord())); + } + for (openvdb::FloatTree::ValueOnCIter iter1 = tree1.cbeginValueOn(); iter1; ++iter1) { + CPPUNIT_ASSERT(tree0.isValueOn(iter1.getCoord())); + } + for (openvdb::FloatTree::ValueOnCIter iter0 = tree0.cbeginValueOn(); iter0; ++iter0) { + CPPUNIT_ASSERT(tree1.isValueOn(iter0.getCoord())); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter0,tree1.getValue(iter0.getCoord())); + } + for (openvdb::FloatTree::ValueOnCIter iter = tree1_copy.cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT(tree1.isValueOn(iter.getCoord())); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter,tree1.getValue(iter.getCoord())); + } + for (openvdb::FloatTree::ValueOnCIter iter = tree1.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree3.isValueOn(p) || tree1_copy.isValueOn(p)); + } + } + { + ValueType background=5.0f; + openvdb::FloatTree tree0(background), tree1(background), tree2(background); + CPPUNIT_ASSERT(tree2.empty()); + // tree0 = tree1.topologyUnion(tree2) + tree0.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree0.setValue(openvdb::Coord(-5, 10, 20),0.1f); + tree0.setValue(openvdb::Coord( 5,-10, 20),0.2f); + tree0.setValue(openvdb::Coord( 5, 10,-20),0.3f); + tree1.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree1.setValue(openvdb::Coord(-5, 10, 20),0.1f); + tree1.setValue(openvdb::Coord( 5,-10, 20),0.2f); + tree1.setValue(openvdb::Coord( 5, 10,-20),0.3f); + + tree0.setValue(openvdb::Coord(-5,-10, 20),background); + tree0.setValue(openvdb::Coord(-5, 10,-20),background); + tree0.setValue(openvdb::Coord( 5,-10,-20),background); + tree0.setValue(openvdb::Coord(-5,-10,-20),background); + tree0.setValue(openvdb::Coord(-5000, 2000,-3000),background); + tree0.setValue(openvdb::Coord( 5000,-2000,-3000),background); + tree0.setValue(openvdb::Coord(-5000,-2000, 3000),background); + tree2.setValue(openvdb::Coord(-5,-10, 20),0.4f); + tree2.setValue(openvdb::Coord(-5, 10,-20),0.5f); + tree2.setValue(openvdb::Coord( 5,-10,-20),0.6f); + tree2.setValue(openvdb::Coord(-5,-10,-20),0.7f); + tree2.setValue(openvdb::Coord(-5000, 2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord( 5000,-2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord(-5000,-2000, 3000),4.5678f); + + // tree3 has the same topology as tree2 but a different value type + const openvdb::Vec3f background2(1.0f,3.4f,6.0f), vec_val(3.1f,5.3f,-9.5f); + openvdb::Vec3fTree tree3(background2); + + for (openvdb::FloatTree::ValueOnCIter iter2 = tree2.cbeginValueOn(); iter2; ++iter2) { + tree3.setValue(iter2.getCoord(), vec_val); + } + + openvdb::FloatTree tree4(tree1);//tree4 = tree1 + openvdb::FloatTree tree5(tree1);//tree5 = tree1 + + tree1.topologyUnion(tree3);//should make tree1 = tree0 + + CPPUNIT_ASSERT(tree1.hasSameTopology(tree0)); + + for (openvdb::Vec3fTree::ValueOnCIter iter3 = tree3.cbeginValueOn(); iter3; ++iter3) { + tree4.setValueOn(iter3.getCoord()); + const openvdb::Coord p = iter3.getCoord(); + ASSERT_DOUBLES_EXACTLY_EQUAL(tree1.getValue(p),tree5.getValue(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(tree4.getValue(p),tree5.getValue(p)); + } + + CPPUNIT_ASSERT(tree4.hasSameTopology(tree0)); + + for (openvdb::FloatTree::ValueOnCIter iter4 = tree4.cbeginValueOn(); iter4; ++iter4) { + const openvdb::Coord p = iter4.getCoord(); + ASSERT_DOUBLES_EXACTLY_EQUAL(tree0.getValue(p),tree5.getValue(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(tree1.getValue(p),tree5.getValue(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(tree4.getValue(p),tree5.getValue(p)); + } + + for (openvdb::FloatTree::ValueOnCIter iter = tree1.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree3.isValueOn(p) || tree4.isValueOn(p)); + } + } + {// test overlapping spheres + const float background=5.0f, R0=10.0f, R1=5.6f; + const openvdb::Vec3f C0(35.0f, 30.0f, 40.0f), C1(22.3f, 30.5f, 31.0f); + const openvdb::Coord dim(32, 32, 32); + openvdb::FloatGrid grid0(background); + openvdb::FloatGrid grid1(background); + unittest_util::makeSphere(dim, C0, R0, grid0, + 1.0f, unittest_util::SPHERE_SPARSE_NARROW_BAND); + unittest_util::makeSphere(dim, C1, R1, grid1, + 1.0f, unittest_util::SPHERE_SPARSE_NARROW_BAND); + openvdb::FloatTree& tree0 = grid0.tree(); + openvdb::FloatTree& tree1 = grid1.tree(); + openvdb::FloatTree tree0_copy(tree0); + + tree0.topologyUnion(tree1); + + const openvdb::Index64 n0 = tree0_copy.activeVoxelCount(); + const openvdb::Index64 n = tree0.activeVoxelCount(); + const openvdb::Index64 n1 = tree1.activeVoxelCount(); + + //fprintf(stderr,"Union of spheres: n=%i, n0=%i n1=%i n0+n1=%i\n",n,n0,n1, n0+n1); + + CPPUNIT_ASSERT( n > n0 ); + CPPUNIT_ASSERT( n > n1 ); + CPPUNIT_ASSERT( n < n0 + n1 ); + + for (openvdb::FloatTree::ValueOnCIter iter = tree1.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree0.isValueOn(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(tree0.getValue(p), tree0_copy.getValue(p)); + } + for (openvdb::FloatTree::ValueOnCIter iter = tree0_copy.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree0.isValueOn(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(tree0.getValue(p), *iter); + } + } + + {// test union of a leaf and a tile + if (openvdb::FloatTree::DEPTH > 2) { + const int leafLevel = openvdb::FloatTree::DEPTH - 1; + const int tileLevel = leafLevel - 1; + const openvdb::Coord xyz(0); + + openvdb::FloatTree tree0; + tree0.addTile(tileLevel, xyz, /*value=*/0, /*activeState=*/true); + CPPUNIT_ASSERT(tree0.isValueOn(xyz)); + + openvdb::FloatTree tree1; + tree1.touchLeaf(xyz)->setValuesOn(); + CPPUNIT_ASSERT(tree1.isValueOn(xyz)); + + tree0.topologyUnion(tree1); + CPPUNIT_ASSERT(tree0.isValueOn(xyz)); + CPPUNIT_ASSERT_EQUAL(tree0.getValueDepth(xyz), leafLevel); + } + } + +}// testTopologyUnion + +void +TestTree::testTopologyIntersection() +{ + {//no overlapping voxels + const ValueType background=0.0f; + openvdb::FloatTree tree0(background), tree1(background); + tree0.setValue(openvdb::Coord( 500, 300, 200), 1.0f); + tree1.setValue(openvdb::Coord( 8, 11, 11), 2.0f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(1), tree0.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(1), tree1.activeVoxelCount()); + + tree1.topologyIntersection(tree0); + + CPPUNIT_ASSERT_EQUAL(tree1.activeVoxelCount(), openvdb::Index64(0)); + CPPUNIT_ASSERT(!tree1.empty()); + openvdb::tools::pruneInactive(tree1); + CPPUNIT_ASSERT(tree1.empty()); + } + {//two overlapping voxels + const ValueType background=0.0f; + openvdb::FloatTree tree0(background), tree1(background); + tree0.setValue(openvdb::Coord( 500, 300, 200), 1.0f); + + tree1.setValue(openvdb::Coord( 8, 11, 11), 2.0f); + tree1.setValue(openvdb::Coord( 500, 300, 200), 1.0f); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(1), tree0.activeVoxelCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(2), tree1.activeVoxelCount() ); + + tree1.topologyIntersection(tree0); + + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(1), tree1.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree1.empty()); + openvdb::tools::pruneInactive(tree1); + CPPUNIT_ASSERT(!tree1.empty()); + } + {//4 overlapping voxels + const ValueType background=0.0f; + openvdb::FloatTree tree0(background), tree1(background); + tree0.setValue(openvdb::Coord( 500, 300, 200), 1.0f); + tree0.setValue(openvdb::Coord( 400, 30, 20), 2.0f); + tree0.setValue(openvdb::Coord( 8, 11, 11), 3.0f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree0.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree0.leafCount() ); + + tree1.setValue(openvdb::Coord( 500, 301, 200), 4.0f); + tree1.setValue(openvdb::Coord( 400, 30, 20), 5.0f); + tree1.setValue(openvdb::Coord( 8, 11, 11), 6.0f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree1.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree1.leafCount() ); + + tree1.topologyIntersection(tree0); + + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(3), tree1.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(2), tree1.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree1.empty()); + openvdb::tools::pruneInactive(tree1); + CPPUNIT_ASSERT(!tree1.empty()); + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(2), tree1.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(2), tree1.activeVoxelCount() ); + } + {//passive tile + const ValueType background=0.0f; + const openvdb::Index64 dim = openvdb::FloatTree::RootNodeType::ChildNodeType::DIM; + openvdb::FloatTree tree0(background), tree1(background); + tree0.fill(openvdb::CoordBBox(openvdb::Coord(0),openvdb::Coord(dim-1)),2.0f, false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(0), tree0.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree0.leafCount() ); + + tree1.setValue(openvdb::Coord( 500, 301, 200), 4.0f); + tree1.setValue(openvdb::Coord( 400, 30, 20), 5.0f); + tree1.setValue(openvdb::Coord( dim, 11, 11), 6.0f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree1.leafCount() ); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree1.activeVoxelCount()); + + tree1.topologyIntersection(tree0); + + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(0), tree1.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(0), tree1.activeVoxelCount() ); + CPPUNIT_ASSERT(tree1.empty()); + } + {//active tile + const ValueType background=0.0f; + const openvdb::Index64 dim = openvdb::FloatTree::RootNodeType::ChildNodeType::DIM; + openvdb::FloatTree tree0(background), tree1(background); + tree1.fill(openvdb::CoordBBox(openvdb::Coord(0),openvdb::Coord(dim-1)),2.0f, true); + CPPUNIT_ASSERT_EQUAL(dim*dim*dim, tree1.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree1.leafCount() ); + + tree0.setValue(openvdb::Coord( 500, 301, 200), 4.0f); + tree0.setValue(openvdb::Coord( 400, 30, 20), 5.0f); + tree0.setValue(openvdb::Coord( dim, 11, 11), 6.0f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree0.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree0.leafCount() ); + + tree1.topologyIntersection(tree0); + + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(2), tree1.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(2), tree1.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree1.empty()); + openvdb::tools::pruneInactive(tree1); + CPPUNIT_ASSERT(!tree1.empty()); + } + {// use tree with different voxel type + ValueType background=5.0f; + openvdb::FloatTree tree0(background), tree1(background), tree2(background); + CPPUNIT_ASSERT(tree2.empty()); + // tree0 = tree1.topologyIntersection(tree2) + tree0.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree0.setValue(openvdb::Coord(-5, 10,-20),0.1f); + tree0.setValue(openvdb::Coord( 5,-10,-20),0.2f); + tree0.setValue(openvdb::Coord(-5,-10,-20),0.3f); + + tree1.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree1.setValue(openvdb::Coord(-5, 10,-20),0.1f); + tree1.setValue(openvdb::Coord( 5,-10,-20),0.2f); + tree1.setValue(openvdb::Coord(-5,-10,-20),0.3f); + + tree2.setValue(openvdb::Coord( 5, 10, 20),0.4f); + tree2.setValue(openvdb::Coord(-5, 10,-20),0.5f); + tree2.setValue(openvdb::Coord( 5,-10,-20),0.6f); + tree2.setValue(openvdb::Coord(-5,-10,-20),0.7f); + + tree2.setValue(openvdb::Coord(-5000, 2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord( 5000,-2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord(-5000,-2000, 3000),4.5678f); + + openvdb::FloatTree tree1_copy(tree1); + + // tree3 has the same topology as tree2 but a different value type + const openvdb::Vec3f background2(1.0f,3.4f,6.0f), vec_val(3.1f,5.3f,-9.5f); + openvdb::Vec3fTree tree3(background2); + for (openvdb::FloatTree::ValueOnCIter iter = tree2.cbeginValueOn(); iter; ++iter) { + tree3.setValue(iter.getCoord(), vec_val); + } + + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(4), tree0.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(4), tree1.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(7), tree2.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(7), tree3.leafCount()); + + + //tree1.topologyInterection(tree2);//should make tree1 = tree0 + tree1.topologyIntersection(tree3);//should make tree1 = tree0 + + CPPUNIT_ASSERT(tree0.leafCount()==tree1.leafCount()); + CPPUNIT_ASSERT(tree0.nonLeafCount()==tree1.nonLeafCount()); + CPPUNIT_ASSERT(tree0.activeLeafVoxelCount()==tree1.activeLeafVoxelCount()); + CPPUNIT_ASSERT(tree0.inactiveLeafVoxelCount()==tree1.inactiveLeafVoxelCount()); + CPPUNIT_ASSERT(tree0.activeVoxelCount()==tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree0.inactiveVoxelCount()==tree1.inactiveVoxelCount()); + CPPUNIT_ASSERT(tree1.hasSameTopology(tree0)); + CPPUNIT_ASSERT(tree0.hasSameTopology(tree1)); + + for (openvdb::FloatTree::ValueOnCIter iter = tree0.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree1.isValueOn(p)); + CPPUNIT_ASSERT(tree2.isValueOn(p)); + CPPUNIT_ASSERT(tree3.isValueOn(p)); + CPPUNIT_ASSERT(tree1_copy.isValueOn(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter,tree1.getValue(p)); + } + for (openvdb::FloatTree::ValueOnCIter iter = tree1_copy.cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT(tree1.isValueOn(iter.getCoord())); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter,tree1.getValue(iter.getCoord())); + } + for (openvdb::FloatTree::ValueOnCIter iter = tree1.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree0.isValueOn(p)); + CPPUNIT_ASSERT(tree2.isValueOn(p)); + CPPUNIT_ASSERT(tree3.isValueOn(p)); + CPPUNIT_ASSERT(tree1_copy.isValueOn(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter,tree0.getValue(p)); + } + } + + {// test overlapping spheres + const float background=5.0f, R0=10.0f, R1=5.6f; + const openvdb::Vec3f C0(35.0f, 30.0f, 40.0f), C1(22.3f, 30.5f, 31.0f); + const openvdb::Coord dim(32, 32, 32); + openvdb::FloatGrid grid0(background); + openvdb::FloatGrid grid1(background); + unittest_util::makeSphere(dim, C0, R0, grid0, + 1.0f, unittest_util::SPHERE_SPARSE_NARROW_BAND); + unittest_util::makeSphere(dim, C1, R1, grid1, + 1.0f, unittest_util::SPHERE_SPARSE_NARROW_BAND); + openvdb::FloatTree& tree0 = grid0.tree(); + openvdb::FloatTree& tree1 = grid1.tree(); + openvdb::FloatTree tree0_copy(tree0); + + tree0.topologyIntersection(tree1); + + const openvdb::Index64 n0 = tree0_copy.activeVoxelCount(); + const openvdb::Index64 n = tree0.activeVoxelCount(); + const openvdb::Index64 n1 = tree1.activeVoxelCount(); + + //fprintf(stderr,"Intersection of spheres: n=%i, n0=%i n1=%i n0+n1=%i\n",n,n0,n1, n0+n1); + + CPPUNIT_ASSERT( n < n0 ); + CPPUNIT_ASSERT( n < n1 ); + + for (openvdb::FloatTree::ValueOnCIter iter = tree0.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree1.isValueOn(p)); + CPPUNIT_ASSERT(tree0_copy.isValueOn(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter, tree0_copy.getValue(p)); + } + } + + {// Test based on boolean grids + openvdb::CoordBBox bigRegion(openvdb::Coord(-9), openvdb::Coord(10)); + openvdb::CoordBBox smallRegion(openvdb::Coord( 1), openvdb::Coord(10)); + + openvdb::BoolGrid::Ptr gridBig = openvdb::BoolGrid::create(false); + gridBig->fill(bigRegion, true/*value*/, true /*make active*/); + CPPUNIT_ASSERT_EQUAL(8, int(gridBig->tree().activeTileCount())); + CPPUNIT_ASSERT_EQUAL((20 * 20 * 20), int(gridBig->activeVoxelCount())); + + openvdb::BoolGrid::Ptr gridSmall = openvdb::BoolGrid::create(false); + gridSmall->fill(smallRegion, true/*value*/, true /*make active*/); + CPPUNIT_ASSERT_EQUAL(0, int(gridSmall->tree().activeTileCount())); + CPPUNIT_ASSERT_EQUAL((10 * 10 * 10), int(gridSmall->activeVoxelCount())); + + // change the topology of gridBig by intersecting with gridSmall + gridBig->topologyIntersection(*gridSmall); + + // Should be unchanged + CPPUNIT_ASSERT_EQUAL(0, int(gridSmall->tree().activeTileCount())); + CPPUNIT_ASSERT_EQUAL((10 * 10 * 10), int(gridSmall->activeVoxelCount())); + + // In this case the interesection should be exactly "small" + CPPUNIT_ASSERT_EQUAL(0, int(gridBig->tree().activeTileCount())); + CPPUNIT_ASSERT_EQUAL((10 * 10 * 10), int(gridBig->activeVoxelCount())); + + } + +}// testTopologyIntersection + +void +TestTree::testTopologyDifference() +{ + {//no overlapping voxels + const ValueType background=0.0f; + openvdb::FloatTree tree0(background), tree1(background); + tree0.setValue(openvdb::Coord( 500, 300, 200), 1.0f); + tree1.setValue(openvdb::Coord( 8, 11, 11), 2.0f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(1), tree0.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(1), tree1.activeVoxelCount()); + + tree1.topologyDifference(tree0); + + CPPUNIT_ASSERT_EQUAL(tree1.activeVoxelCount(), openvdb::Index64(1)); + CPPUNIT_ASSERT(!tree1.empty()); + openvdb::tools::pruneInactive(tree1); + CPPUNIT_ASSERT(!tree1.empty()); + } + {//two overlapping voxels + const ValueType background=0.0f; + openvdb::FloatTree tree0(background), tree1(background); + tree0.setValue(openvdb::Coord( 500, 300, 200), 1.0f); + + tree1.setValue(openvdb::Coord( 8, 11, 11), 2.0f); + tree1.setValue(openvdb::Coord( 500, 300, 200), 1.0f); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(1), tree0.activeVoxelCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(2), tree1.activeVoxelCount() ); + + CPPUNIT_ASSERT( tree0.isValueOn(openvdb::Coord( 500, 300, 200))); + CPPUNIT_ASSERT( tree1.isValueOn(openvdb::Coord( 500, 300, 200))); + CPPUNIT_ASSERT( tree1.isValueOn(openvdb::Coord( 8, 11, 11))); + + tree1.topologyDifference(tree0); + + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(1), tree1.activeVoxelCount() ); + CPPUNIT_ASSERT( tree0.isValueOn(openvdb::Coord( 500, 300, 200))); + CPPUNIT_ASSERT(!tree1.isValueOn(openvdb::Coord( 500, 300, 200))); + CPPUNIT_ASSERT( tree1.isValueOn(openvdb::Coord( 8, 11, 11))); + + CPPUNIT_ASSERT(!tree1.empty()); + openvdb::tools::pruneInactive(tree1); + CPPUNIT_ASSERT(!tree1.empty()); + } + {//4 overlapping voxels + const ValueType background=0.0f; + openvdb::FloatTree tree0(background), tree1(background); + tree0.setValue(openvdb::Coord( 500, 300, 200), 1.0f); + tree0.setValue(openvdb::Coord( 400, 30, 20), 2.0f); + tree0.setValue(openvdb::Coord( 8, 11, 11), 3.0f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree0.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree0.leafCount() ); + + tree1.setValue(openvdb::Coord( 500, 301, 200), 4.0f); + tree1.setValue(openvdb::Coord( 400, 30, 20), 5.0f); + tree1.setValue(openvdb::Coord( 8, 11, 11), 6.0f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree1.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree1.leafCount() ); + + tree1.topologyDifference(tree0); + + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(3), tree1.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(1), tree1.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree1.empty()); + openvdb::tools::pruneInactive(tree1); + CPPUNIT_ASSERT(!tree1.empty()); + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(1), tree1.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(1), tree1.activeVoxelCount() ); + } + {//passive tile + const ValueType background=0.0f; + const openvdb::Index64 dim = openvdb::FloatTree::RootNodeType::ChildNodeType::DIM; + openvdb::FloatTree tree0(background), tree1(background); + tree0.fill(openvdb::CoordBBox(openvdb::Coord(0),openvdb::Coord(dim-1)),2.0f, false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(0), tree0.activeVoxelCount()); + CPPUNIT_ASSERT(!tree0.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(0), tree0.root().onTileCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree0.leafCount() ); + + tree1.setValue(openvdb::Coord( 500, 301, 200), 4.0f); + tree1.setValue(openvdb::Coord( 400, 30, 20), 5.0f); + tree1.setValue(openvdb::Coord( dim, 11, 11), 6.0f); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree1.activeVoxelCount()); + CPPUNIT_ASSERT(!tree1.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree1.leafCount() ); + + tree1.topologyDifference(tree0); + + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(3), tree1.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(3), tree1.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree1.empty()); + openvdb::tools::pruneInactive(tree1); + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(3), tree1.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(3), tree1.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree1.empty()); + } + {//active tile + const ValueType background=0.0f; + const openvdb::Index64 dim = openvdb::FloatTree::RootNodeType::ChildNodeType::DIM; + openvdb::FloatTree tree0(background), tree1(background); + tree1.fill(openvdb::CoordBBox(openvdb::Coord(0),openvdb::Coord(dim-1)),2.0f, true); + CPPUNIT_ASSERT_EQUAL(dim*dim*dim, tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree1.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(1), tree1.root().onTileCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree0.leafCount() ); + + tree0.setValue(openvdb::Coord( 500, 301, 200), 4.0f); + tree0.setValue(openvdb::Coord( 400, 30, 20), 5.0f); + tree0.setValue(openvdb::Coord( int(dim), 11, 11), 6.0f); + CPPUNIT_ASSERT(!tree0.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree0.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree0.leafCount() ); + CPPUNIT_ASSERT( tree0.isValueOn(openvdb::Coord( int(dim), 11, 11))); + CPPUNIT_ASSERT(!tree1.isValueOn(openvdb::Coord( int(dim), 11, 11))); + + tree1.topologyDifference(tree0); + + CPPUNIT_ASSERT(tree1.root().onTileCount() > 1); + CPPUNIT_ASSERT_EQUAL( dim*dim*dim - 2, tree1.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree1.empty()); + openvdb::tools::pruneInactive(tree1); + CPPUNIT_ASSERT_EQUAL( dim*dim*dim - 2, tree1.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree1.empty()); + } + {//active tile + const ValueType background=0.0f; + const openvdb::Index64 dim = openvdb::FloatTree::RootNodeType::ChildNodeType::DIM; + openvdb::FloatTree tree0(background), tree1(background); + tree1.fill(openvdb::CoordBBox(openvdb::Coord(0),openvdb::Coord(dim-1)),2.0f, true); + CPPUNIT_ASSERT_EQUAL(dim*dim*dim, tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree1.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(1), tree1.root().onTileCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree0.leafCount() ); + + tree0.setValue(openvdb::Coord( 500, 301, 200), 4.0f); + tree0.setValue(openvdb::Coord( 400, 30, 20), 5.0f); + tree0.setValue(openvdb::Coord( dim, 11, 11), 6.0f); + CPPUNIT_ASSERT(!tree0.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree0.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree0.leafCount() ); + + tree0.topologyDifference(tree1); + + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(1), tree0.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(1), tree0.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree0.empty()); + openvdb::tools::pruneInactive(tree0); + CPPUNIT_ASSERT_EQUAL( openvdb::Index32(1), tree0.leafCount() ); + CPPUNIT_ASSERT_EQUAL( openvdb::Index64(1), tree0.activeVoxelCount() ); + CPPUNIT_ASSERT(!tree1.empty()); + } + {// use tree with different voxel type + ValueType background=5.0f; + openvdb::FloatTree tree0(background), tree1(background), tree2(background); + CPPUNIT_ASSERT(tree2.empty()); + // tree0 = tree1.topologyIntersection(tree2) + tree0.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree0.setValue(openvdb::Coord(-5, 10,-20),0.1f); + tree0.setValue(openvdb::Coord( 5,-10,-20),0.2f); + tree0.setValue(openvdb::Coord(-5,-10,-20),0.3f); + + tree1.setValue(openvdb::Coord( 5, 10, 20),0.0f); + tree1.setValue(openvdb::Coord(-5, 10,-20),0.1f); + tree1.setValue(openvdb::Coord( 5,-10,-20),0.2f); + tree1.setValue(openvdb::Coord(-5,-10,-20),0.3f); + + tree2.setValue(openvdb::Coord( 5, 10, 20),0.4f); + tree2.setValue(openvdb::Coord(-5, 10,-20),0.5f); + tree2.setValue(openvdb::Coord( 5,-10,-20),0.6f); + tree2.setValue(openvdb::Coord(-5,-10,-20),0.7f); + + tree2.setValue(openvdb::Coord(-5000, 2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord( 5000,-2000,-3000),4.5678f); + tree2.setValue(openvdb::Coord(-5000,-2000, 3000),4.5678f); + + openvdb::FloatTree tree1_copy(tree1); + + // tree3 has the same topology as tree2 but a different value type + const openvdb::Vec3f background2(1.0f,3.4f,6.0f), vec_val(3.1f,5.3f,-9.5f); + openvdb::Vec3fTree tree3(background2); + for (openvdb::FloatTree::ValueOnCIter iter = tree2.cbeginValueOn(); iter; ++iter) { + tree3.setValue(iter.getCoord(), vec_val); + } + + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(4), tree0.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(4), tree1.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(7), tree2.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(7), tree3.leafCount()); + + + //tree1.topologyInterection(tree2);//should make tree1 = tree0 + tree1.topologyIntersection(tree3);//should make tree1 = tree0 + + CPPUNIT_ASSERT(tree0.leafCount()==tree1.leafCount()); + CPPUNIT_ASSERT(tree0.nonLeafCount()==tree1.nonLeafCount()); + CPPUNIT_ASSERT(tree0.activeLeafVoxelCount()==tree1.activeLeafVoxelCount()); + CPPUNIT_ASSERT(tree0.inactiveLeafVoxelCount()==tree1.inactiveLeafVoxelCount()); + CPPUNIT_ASSERT(tree0.activeVoxelCount()==tree1.activeVoxelCount()); + CPPUNIT_ASSERT(tree0.inactiveVoxelCount()==tree1.inactiveVoxelCount()); + CPPUNIT_ASSERT(tree1.hasSameTopology(tree0)); + CPPUNIT_ASSERT(tree0.hasSameTopology(tree1)); + + for (openvdb::FloatTree::ValueOnCIter iter = tree0.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree1.isValueOn(p)); + CPPUNIT_ASSERT(tree2.isValueOn(p)); + CPPUNIT_ASSERT(tree3.isValueOn(p)); + CPPUNIT_ASSERT(tree1_copy.isValueOn(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter,tree1.getValue(p)); + } + for (openvdb::FloatTree::ValueOnCIter iter = tree1_copy.cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT(tree1.isValueOn(iter.getCoord())); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter,tree1.getValue(iter.getCoord())); + } + for (openvdb::FloatTree::ValueOnCIter iter = tree1.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree0.isValueOn(p)); + CPPUNIT_ASSERT(tree2.isValueOn(p)); + CPPUNIT_ASSERT(tree3.isValueOn(p)); + CPPUNIT_ASSERT(tree1_copy.isValueOn(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter,tree0.getValue(p)); + } + } + {// test overlapping spheres + const float background=5.0f, R0=10.0f, R1=5.6f; + const openvdb::Vec3f C0(35.0f, 30.0f, 40.0f), C1(22.3f, 30.5f, 31.0f); + const openvdb::Coord dim(32, 32, 32); + openvdb::FloatGrid grid0(background); + openvdb::FloatGrid grid1(background); + unittest_util::makeSphere(dim, C0, R0, grid0, + 1.0f, unittest_util::SPHERE_SPARSE_NARROW_BAND); + unittest_util::makeSphere(dim, C1, R1, grid1, + 1.0f, unittest_util::SPHERE_SPARSE_NARROW_BAND); + openvdb::FloatTree& tree0 = grid0.tree(); + openvdb::FloatTree& tree1 = grid1.tree(); + openvdb::FloatTree tree0_copy(tree0); + + tree0.topologyDifference(tree1); + + const openvdb::Index64 n0 = tree0_copy.activeVoxelCount(); + const openvdb::Index64 n = tree0.activeVoxelCount(); + + CPPUNIT_ASSERT( n < n0 ); + + for (openvdb::FloatTree::ValueOnCIter iter = tree0.cbeginValueOn(); iter; ++iter) { + const openvdb::Coord p = iter.getCoord(); + CPPUNIT_ASSERT(tree1.isValueOff(p)); + CPPUNIT_ASSERT(tree0_copy.isValueOn(p)); + ASSERT_DOUBLES_EXACTLY_EQUAL(*iter, tree0_copy.getValue(p)); + } + } +} // testTopologyDifference + + +//////////////////////////////////////// + + +void +TestTree::testFill() +{ + // Use a custom tree configuration to ensure we flood-fill at all levels! + using LeafT = openvdb::tree::LeafNode;//4^3 + using InternalT = openvdb::tree::InternalNode;//4^3 + using RootT = openvdb::tree::RootNode;// child nodes are 16^3 + using TreeT = openvdb::tree::Tree; + + const float outside = 2.0f, inside = -outside; + const openvdb::CoordBBox + bbox{openvdb::Coord{-3, -50, 30}, openvdb::Coord{13, 11, 323}}, + otherBBox{openvdb::Coord{400, 401, 402}, openvdb::Coord{600}}; + + {// sparse fill + openvdb::Grid::Ptr grid = openvdb::Grid::create(outside); + TreeT& tree = grid->tree(); + CPPUNIT_ASSERT(!tree.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(0), tree.activeVoxelCount()); + for (openvdb::CoordBBox::Iterator ijk(bbox); ijk; ++ijk) { + ASSERT_DOUBLES_EXACTLY_EQUAL(outside, tree.getValue(*ijk)); + } + tree.sparseFill(bbox, inside, /*active=*/true); + CPPUNIT_ASSERT(tree.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(bbox.volume()), tree.activeVoxelCount()); + for (openvdb::CoordBBox::Iterator ijk(bbox); ijk; ++ijk) { + ASSERT_DOUBLES_EXACTLY_EQUAL(inside, tree.getValue(*ijk)); + } + } + {// dense fill + openvdb::Grid::Ptr grid = openvdb::Grid::create(outside); + TreeT& tree = grid->tree(); + CPPUNIT_ASSERT(!tree.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(0), tree.activeVoxelCount()); + for (openvdb::CoordBBox::Iterator ijk(bbox); ijk; ++ijk) { + ASSERT_DOUBLES_EXACTLY_EQUAL(outside, tree.getValue(*ijk)); + } + + // Add some active tiles. + tree.sparseFill(otherBBox, inside, /*active=*/true); + CPPUNIT_ASSERT(tree.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(otherBBox.volume(), tree.activeVoxelCount()); + + tree.denseFill(bbox, inside, /*active=*/true); + + // In OpenVDB 4.0.0 and earlier, denseFill() densified active tiles + // throughout the tree. Verify that it no longer does that. + CPPUNIT_ASSERT(tree.hasActiveTiles()); // i.e., otherBBox + + CPPUNIT_ASSERT_EQUAL(bbox.volume() + otherBBox.volume(), tree.activeVoxelCount()); + for (openvdb::CoordBBox::Iterator ijk(bbox); ijk; ++ijk) { + ASSERT_DOUBLES_EXACTLY_EQUAL(inside, tree.getValue(*ijk)); + } + + tree.clear(); + CPPUNIT_ASSERT(!tree.hasActiveTiles()); + tree.sparseFill(otherBBox, inside, /*active=*/true); + CPPUNIT_ASSERT(tree.hasActiveTiles()); + tree.denseFill(bbox, inside, /*active=*/false); + CPPUNIT_ASSERT(tree.hasActiveTiles()); // i.e., otherBBox + CPPUNIT_ASSERT_EQUAL(otherBBox.volume(), tree.activeVoxelCount()); + + // In OpenVDB 4.0.0 and earlier, denseFill() filled sparsely if given + // an inactive fill value. Verify that it now fills densely. + const int leafDepth = int(tree.treeDepth()) - 1; + for (openvdb::CoordBBox::Iterator ijk(bbox); ijk; ++ijk) { + CPPUNIT_ASSERT_EQUAL(leafDepth, tree.getValueDepth(*ijk)); + ASSERT_DOUBLES_EXACTLY_EQUAL(inside, tree.getValue(*ijk)); + } + } + +}// testFill + +void +TestTree::testSignedFloodFill() +{ + // Use a custom tree configuration to ensure we flood-fill at all levels! + using LeafT = openvdb::tree::LeafNode;//4^3 + using InternalT = openvdb::tree::InternalNode;//4^3 + using RootT = openvdb::tree::RootNode;// child nodes are 16^3 + using TreeT = openvdb::tree::Tree; + + const float outside = 2.0f, inside = -outside, radius = 20.0f; + + {//first test flood filling of a leaf node + + const LeafT::ValueType fill0=5, fill1=-fill0; + openvdb::tools::SignedFloodFillOp sff(fill0, fill1); + + int D = LeafT::dim(), C=D/2; + openvdb::Coord origin(0,0,0), left(0,0,C-1), right(0,0,C); + LeafT leaf(origin,fill0); + for (int i=0; i::Ptr grid = openvdb::Grid::create(outside); + TreeT& tree = grid->tree(); + const RootT& root = tree.root(); + const openvdb::Coord dim(3*16, 3*16, 3*16); + const openvdb::Coord C(16+8,16+8,16+8); + + CPPUNIT_ASSERT(!tree.isValueOn(C)); + CPPUNIT_ASSERT(root.getTableSize()==0); + + //make narrow band of sphere without setting sign for the background values! + openvdb::Grid::Accessor acc = grid->getAccessor(); + const openvdb::Vec3f center(static_cast(C[0]), + static_cast(C[1]), + static_cast(C[2])); + openvdb::Coord xyz; + for (xyz[0]=0; xyz[0]transform().indexToWorld(xyz); + const float dist = float((p-center).length() - radius); + if (fabs(dist) > outside) continue; + acc.setValue(xyz, dist); + } + } + } + // Check narrow band with incorrect background + const size_t size_before = root.getTableSize(); + CPPUNIT_ASSERT(size_before>0); + CPPUNIT_ASSERT(!tree.isValueOn(C)); + ASSERT_DOUBLES_EXACTLY_EQUAL(outside,tree.getValue(C)); + for (xyz[0]=0; xyz[0]transform().indexToWorld(xyz); + const float dist = float((p-center).length() - radius); + const float val = acc.getValue(xyz); + if (dist < inside) { + ASSERT_DOUBLES_EXACTLY_EQUAL( val, outside); + } else if (dist>outside) { + ASSERT_DOUBLES_EXACTLY_EQUAL( val, outside); + } else { + ASSERT_DOUBLES_EXACTLY_EQUAL( val, dist ); + } + } + } + } + + CPPUNIT_ASSERT(tree.getValueDepth(C) == -1);//i.e. background value + openvdb::tools::signedFloodFill(tree); + CPPUNIT_ASSERT(tree.getValueDepth(C) == 0);//added inside tile to root + + // Check narrow band with correct background + for (xyz[0]=0; xyz[0]transform().indexToWorld(xyz); + const float dist = float((p-center).length() - radius); + const float val = acc.getValue(xyz); + if (dist < inside) { + ASSERT_DOUBLES_EXACTLY_EQUAL( val, inside); + } else if (dist>outside) { + ASSERT_DOUBLES_EXACTLY_EQUAL( val, outside); + } else { + ASSERT_DOUBLES_EXACTLY_EQUAL( val, dist ); + } + } + } + } + + CPPUNIT_ASSERT(root.getTableSize()>size_before);//added inside root tiles + CPPUNIT_ASSERT(!tree.isValueOn(C)); + ASSERT_DOUBLES_EXACTLY_EQUAL(inside,tree.getValue(C)); +} + + +void +TestTree::testPruneInactive() +{ + using openvdb::Coord; + using openvdb::Index32; + using openvdb::Index64; + + const float background = 5.0; + + openvdb::FloatTree tree(background); + + // Verify that the newly-constructed tree is empty and that pruning it has no effect. + CPPUNIT_ASSERT(tree.empty()); + openvdb::tools::prune(tree); + CPPUNIT_ASSERT(tree.empty()); + openvdb::tools::pruneInactive(tree); + CPPUNIT_ASSERT(tree.empty()); + + // Set some active values. + tree.setValue(Coord(-5, 10, 20), 0.1f); + tree.setValue(Coord(-5,-10, 20), 0.4f); + tree.setValue(Coord(-5, 10,-20), 0.5f); + tree.setValue(Coord(-5,-10,-20), 0.7f); + tree.setValue(Coord( 5, 10, 20), 0.0f); + tree.setValue(Coord( 5,-10, 20), 0.2f); + tree.setValue(Coord( 5,-10,-20), 0.6f); + tree.setValue(Coord( 5, 10,-20), 0.3f); + // Verify that the tree has the expected numbers of active voxels and leaf nodes. + CPPUNIT_ASSERT_EQUAL(Index64(8), tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index32(8), tree.leafCount()); + + // Verify that prune() has no effect, since the values are all different. + openvdb::tools::prune(tree); + CPPUNIT_ASSERT_EQUAL(Index64(8), tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index32(8), tree.leafCount()); + // Verify that pruneInactive() has no effect, since the values are active. + openvdb::tools::pruneInactive(tree); + CPPUNIT_ASSERT_EQUAL(Index64(8), tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index32(8), tree.leafCount()); + + // Make some of the active values inactive, without changing their values. + tree.setValueOff(Coord(-5, 10, 20)); + tree.setValueOff(Coord(-5,-10, 20)); + tree.setValueOff(Coord(-5, 10,-20)); + tree.setValueOff(Coord(-5,-10,-20)); + CPPUNIT_ASSERT_EQUAL(Index64(4), tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index32(8), tree.leafCount()); + // Verify that prune() has no effect, since the values are still different. + openvdb::tools::prune(tree); + CPPUNIT_ASSERT_EQUAL(Index64(4), tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index32(8), tree.leafCount()); + // Verify that pruneInactive() prunes the nodes containing only inactive voxels. + openvdb::tools::pruneInactive(tree); + CPPUNIT_ASSERT_EQUAL(Index64(4), tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index32(4), tree.leafCount()); + + // Make all of the active values inactive, without changing their values. + tree.setValueOff(Coord( 5, 10, 20)); + tree.setValueOff(Coord( 5,-10, 20)); + tree.setValueOff(Coord( 5,-10,-20)); + tree.setValueOff(Coord( 5, 10,-20)); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index32(4), tree.leafCount()); + // Verify that prune() has no effect, since the values are still different. + openvdb::tools::prune(tree); + CPPUNIT_ASSERT_EQUAL(Index64(0), tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Index32(4), tree.leafCount()); + // Verify that pruneInactive() prunes all of the remaining leaf nodes. + openvdb::tools::pruneInactive(tree); + CPPUNIT_ASSERT(tree.empty()); +} + +void +TestTree::testPruneLevelSet() +{ + const float background=10.0f, R=5.6f; + const openvdb::Vec3f C(12.3f, 15.5f, 10.0f); + const openvdb::Coord dim(32, 32, 32); + + openvdb::FloatGrid grid(background); + unittest_util::makeSphere(dim, C, R, grid, + 1.0f, unittest_util::SPHERE_SPARSE_NARROW_BAND); + openvdb::FloatTree& tree = grid.tree(); + + openvdb::Index64 count = 0; + openvdb::Coord xyz; + for (xyz[0]=0; xyz[0]beginValueOn(); vIter; ++vIter) { + if (fabs(*vIter)setValueOff(vIter.pos(), *vIter > 0.0f ? background : -background); + ++removed; + } + } + // The following version is slower since it employs + // FloatTree::ValueOnIter that visits both tiles and voxels and + // also uses random acceess to set the voxels off. + /* + for (openvdb::FloatTree::ValueOnIter i = tree.beginValueOn(); i; ++i) { + if (fabs(*i) 0.0f ? background : -background); + ++removed2; + } + */ + + CPPUNIT_ASSERT_EQUAL(leafCount, tree.leafCount()); + //std::cerr << "Leaf count=" << tree.leafCount() << std::endl; + CPPUNIT_ASSERT_EQUAL(tree.activeVoxelCount(), count-removed); + CPPUNIT_ASSERT_EQUAL(tree.activeLeafVoxelCount(), count-removed); + + openvdb::tools::pruneLevelSet(tree); + + CPPUNIT_ASSERT(tree.leafCount() < leafCount); + //std::cerr << "Leaf count=" << tree.leafCount() << std::endl; + CPPUNIT_ASSERT_EQUAL(tree.activeVoxelCount(), count-removed); + CPPUNIT_ASSERT_EQUAL(tree.activeLeafVoxelCount(), count-removed); + + openvdb::FloatTree::ValueOnCIter i = tree.cbeginValueOn(); + for (; i; ++i) CPPUNIT_ASSERT( *i < new_width); + + for (xyz[0]=0; xyz[0]getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree->leafCount())); + CPPUNIT_ASSERT(tree->touchLeaf(xyz) != nullptr); + CPPUNIT_ASSERT_EQUAL( 3, tree->getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree->leafCount())); + CPPUNIT_ASSERT(!tree->isValueOn(xyz)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree->getValue(xyz)); + } + {// test accessor + openvdb::FloatTree::Ptr tree(new openvdb::FloatTree(background)); + openvdb::tree::ValueAccessor acc(*tree); + CPPUNIT_ASSERT_EQUAL(-1, acc.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree->leafCount())); + CPPUNIT_ASSERT(acc.touchLeaf(xyz) != nullptr); + CPPUNIT_ASSERT_EQUAL( 3, tree->getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree->leafCount())); + CPPUNIT_ASSERT(!acc.isValueOn(xyz)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, acc.getValue(xyz)); + } +} + + +void +TestTree::testProbeLeaf() +{ + const float background=10.0f, value = 2.0f; + const openvdb::Coord xyz(-20,30,10); + {// test Tree::probeLeaf + openvdb::FloatTree::Ptr tree(new openvdb::FloatTree(background)); + CPPUNIT_ASSERT_EQUAL(-1, tree->getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree->leafCount())); + CPPUNIT_ASSERT(tree->probeLeaf(xyz) == nullptr); + CPPUNIT_ASSERT_EQUAL(-1, tree->getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree->leafCount())); + tree->setValue(xyz, value); + CPPUNIT_ASSERT_EQUAL( 3, tree->getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree->leafCount())); + CPPUNIT_ASSERT(tree->probeLeaf(xyz) != nullptr); + CPPUNIT_ASSERT_EQUAL( 3, tree->getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree->leafCount())); + CPPUNIT_ASSERT(tree->isValueOn(xyz)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree->getValue(xyz)); + } + {// test Tree::probeConstLeaf + const openvdb::FloatTree tree1(background); + CPPUNIT_ASSERT_EQUAL(-1, tree1.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree1.leafCount())); + CPPUNIT_ASSERT(tree1.probeConstLeaf(xyz) == nullptr); + CPPUNIT_ASSERT_EQUAL(-1, tree1.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree1.leafCount())); + openvdb::FloatTree tmp(tree1); + tmp.setValue(xyz, value); + const openvdb::FloatTree tree2(tmp); + CPPUNIT_ASSERT_EQUAL( 3, tree2.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree2.leafCount())); + CPPUNIT_ASSERT(tree2.probeConstLeaf(xyz) != nullptr); + CPPUNIT_ASSERT_EQUAL( 3, tree2.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree2.leafCount())); + CPPUNIT_ASSERT(tree2.isValueOn(xyz)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree2.getValue(xyz)); + } + {// test ValueAccessor::probeLeaf + openvdb::FloatTree::Ptr tree(new openvdb::FloatTree(background)); + openvdb::tree::ValueAccessor acc(*tree); + CPPUNIT_ASSERT_EQUAL(-1, acc.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree->leafCount())); + CPPUNIT_ASSERT(acc.probeLeaf(xyz) == nullptr); + CPPUNIT_ASSERT_EQUAL(-1, acc.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree->leafCount())); + acc.setValue(xyz, value); + CPPUNIT_ASSERT_EQUAL( 3, acc.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree->leafCount())); + CPPUNIT_ASSERT(acc.probeLeaf(xyz) != nullptr); + CPPUNIT_ASSERT_EQUAL( 3, acc.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree->leafCount())); + CPPUNIT_ASSERT(acc.isValueOn(xyz)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, acc.getValue(xyz)); + } + {// test ValueAccessor::probeConstLeaf + const openvdb::FloatTree tree1(background); + openvdb::tree::ValueAccessor acc1(tree1); + CPPUNIT_ASSERT_EQUAL(-1, acc1.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree1.leafCount())); + CPPUNIT_ASSERT(acc1.probeConstLeaf(xyz) == nullptr); + CPPUNIT_ASSERT_EQUAL(-1, acc1.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 0, int(tree1.leafCount())); + openvdb::FloatTree tmp(tree1); + tmp.setValue(xyz, value); + const openvdb::FloatTree tree2(tmp); + openvdb::tree::ValueAccessor acc2(tree2); + CPPUNIT_ASSERT_EQUAL( 3, acc2.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree2.leafCount())); + CPPUNIT_ASSERT(acc2.probeConstLeaf(xyz) != nullptr); + CPPUNIT_ASSERT_EQUAL( 3, acc2.getValueDepth(xyz)); + CPPUNIT_ASSERT_EQUAL( 1, int(tree2.leafCount())); + CPPUNIT_ASSERT(acc2.isValueOn(xyz)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, acc2.getValue(xyz)); + } +} + + +void +TestTree::testAddLeaf() +{ + using namespace openvdb; + + using LeafT = FloatTree::LeafNodeType; + + const Coord ijk(100); + FloatGrid grid; + FloatTree& tree = grid.tree(); + + tree.setValue(ijk, 5.0); + const LeafT* oldLeaf = tree.probeLeaf(ijk); + CPPUNIT_ASSERT(oldLeaf != nullptr); + ASSERT_DOUBLES_EXACTLY_EQUAL(5.0, oldLeaf->getValue(ijk)); + + LeafT* newLeaf = new LeafT; + newLeaf->setOrigin(oldLeaf->origin()); + newLeaf->fill(3.0); + + tree.addLeaf(newLeaf); + CPPUNIT_ASSERT_EQUAL(newLeaf, tree.probeLeaf(ijk)); + ASSERT_DOUBLES_EXACTLY_EQUAL(3.0, tree.getValue(ijk)); +} + + +void +TestTree::testAddTile() +{ + using namespace openvdb; + + const Coord ijk(100); + FloatGrid grid; + FloatTree& tree = grid.tree(); + + tree.setValue(ijk, 5.0); + CPPUNIT_ASSERT(tree.probeLeaf(ijk) != nullptr); + + const Index lvl = FloatTree::DEPTH >> 1; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (lvl > 0) tree.addTile(lvl,ijk, 3.0, /*active=*/true); + else tree.addTile(1,ijk, 3.0, /*active=*/true); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + + CPPUNIT_ASSERT(tree.probeLeaf(ijk) == nullptr); + ASSERT_DOUBLES_EXACTLY_EQUAL(3.0, tree.getValue(ijk)); +} + + +struct BBoxOp +{ + std::vector bbox; + std::vector level; + + // This method is required by Tree::visitActiveBBox + // Since it will return false if LEVEL==0 it will never descent to + // the active voxels. In other words the smallest BBoxes + // correspond to LeafNodes or active tiles at LEVEL=1 + template + inline bool descent() { return LEVEL>0; } + + // This method is required by Tree::visitActiveBBox + template + inline void operator()(const openvdb::CoordBBox &_bbox) { + bbox.push_back(_bbox); + level.push_back(LEVEL); + } +}; + +void +TestTree::testProcessBBox() +{ + using openvdb::Coord; + using openvdb::CoordBBox; + //check two leaf nodes and two tiles at each level 1, 2 and 3 + const int size[4]={1<<3, 1<<3, 1<<(3+4), 1<<(3+4+5)}; + for (int level=0; level<=3; ++level) { + openvdb::FloatTree tree; + const int n = size[level]; + const CoordBBox bbox[]={CoordBBox::createCube(Coord(-n,-n,-n), n), + CoordBBox::createCube(Coord( 0, 0, 0), n)}; + if (level==0) { + tree.setValue(Coord(-1,-2,-3), 1.0f); + tree.setValue(Coord( 1, 2, 3), 1.0f); + } else { + tree.fill(bbox[0], 1.0f, true); + tree.fill(bbox[1], 1.0f, true); + } + BBoxOp op; + tree.visitActiveBBox(op); + CPPUNIT_ASSERT_EQUAL(2, int(op.bbox.size())); + + for (int i=0; i<2; ++i) { + //std::cerr <<"\nLevel="< and Tree::getNodes()"); + tree.getNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(leafCount, size_t(tree.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::getNodes() with std::vector + std::vector array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::vector and Tree::getNodes()"); + tree.getNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(leafCount, size_t(tree.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::getNodes() const with std::vector + std::vector array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::vector and Tree::getNodes() const"); + const FloatTree& tmp = tree; + tmp.getNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(leafCount, size_t(tree.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::getNodes() with std::vector and std::vector::reserve + std::vector array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::vector, std::vector::reserve and Tree::getNodes"); + array.reserve(tree.leafCount()); + tree.getNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(leafCount, size_t(tree.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::getNodes() with std::deque + std::deque array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::deque and Tree::getNodes"); + tree.getNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(leafCount, size_t(tree.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::getNodes() with std::deque + std::deque array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::deque and Tree::getNodes"); + tree.getNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(size_t(1), array.size()); + CPPUNIT_ASSERT_EQUAL(leafCount, size_t(tree.leafCount())); + } + {//testing Tree::getNodes() with std::deque + std::deque array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::deque and Tree::getNodes"); + tree.getNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(size_t(1), array.size()); + CPPUNIT_ASSERT_EQUAL(leafCount, size_t(tree.leafCount())); + } + /* + {//testing Tree::getNodes() with std::deque where T is not part of the tree configuration + using NodeT = openvdb::tree::LeafNode; + std::deque array; + tree.getNodes(array);//should NOT compile since NodeT is not part of the FloatTree configuration + } + {//testing Tree::getNodes() const with std::deque where T is not part of the tree configuration + using NodeT = openvdb::tree::LeafNode; + std::deque array; + const FloatTree& tmp = tree; + tmp.getNodes(array);//should NOT compile since NodeT is not part of the FloatTree configuration + } + */ +}// testGetNodes + +void +TestTree::testStealNodes() +{ + //openvdb::util::CpuTimer timer; + using openvdb::CoordBBox; + using openvdb::Coord; + using openvdb::Vec3f; + using openvdb::FloatGrid; + using openvdb::FloatTree; + + const Vec3f center(0.35f, 0.35f, 0.35f); + const float radius = 0.15f; + const int dim = 128, half_width = 5; + const float voxel_size = 1.0f/dim; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/half_width*voxel_size); + const FloatTree& tree = grid->tree(); + grid->setTransform(openvdb::math::Transform::createLinearTransform(/*voxel size=*/voxel_size)); + + unittest_util::makeSphere( + Coord(dim), center, radius, *grid, unittest_util::SPHERE_SPARSE_NARROW_BAND); + const size_t leafCount = tree.leafCount(); + const size_t voxelCount = tree.activeVoxelCount(); + + {//testing Tree::stealNodes() with std::vector + FloatTree tree2 = tree; + std::vector array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::vector and Tree::stealNodes()"); + tree2.stealNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(size_t(0), size_t(tree2.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::stealNodes() with std::vector + FloatTree tree2 = tree; + std::vector array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::vector and Tree::stealNodes()"); + tree2.stealNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(size_t(0), size_t(tree2.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::stealNodes() const with std::vector + FloatTree tree2 = tree; + std::vector array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::vector and Tree::stealNodes() const"); + tree2.stealNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(size_t(0), size_t(tree2.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::stealNodes() with std::vector and std::vector::reserve + FloatTree tree2 = tree; + std::vector array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::vector, std::vector::reserve and Tree::stealNodes"); + array.reserve(tree2.leafCount()); + tree2.stealNodes(array, 0.0f, false); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(size_t(0), size_t(tree2.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::getNodes() with std::deque + FloatTree tree2 = tree; + std::deque array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::deque and Tree::stealNodes"); + tree2.stealNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(leafCount, array.size()); + CPPUNIT_ASSERT_EQUAL(size_t(0), size_t(tree2.leafCount())); + size_t sum = 0; + for (size_t i=0; ionVoxelCount(); + CPPUNIT_ASSERT_EQUAL(voxelCount, sum); + } + {//testing Tree::getNodes() with std::deque + FloatTree tree2 = tree; + std::deque array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::deque and Tree::stealNodes"); + tree2.stealNodes(array, 0.0f, true); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(size_t(1), array.size()); + CPPUNIT_ASSERT_EQUAL(size_t(0), size_t(tree2.leafCount())); + } + {//testing Tree::getNodes() with std::deque + FloatTree tree2 = tree; + std::deque array; + CPPUNIT_ASSERT_EQUAL(size_t(0), array.size()); + //timer.start("\nstd::deque and Tree::stealNodes"); + tree2.stealNodes(array); + //timer.stop(); + CPPUNIT_ASSERT_EQUAL(size_t(1), array.size()); + CPPUNIT_ASSERT_EQUAL(size_t(0), size_t(tree2.leafCount())); + } + /* + {//testing Tree::stealNodes() with std::deque where T is not part of the tree configuration + FloatTree tree2 = tree; + using NodeT = openvdb::tree::LeafNode; + std::deque array; + //should NOT compile since NodeT is not part of the FloatTree configuration + tree2.stealNodes(array, 0.0f, true); + } + */ +}// testStealNodes + +void +TestTree::testStealNode() +{ + using openvdb::Index; + using openvdb::FloatTree; + + const float background=0.0f, value = 5.6f, epsilon=0.000001f; + const openvdb::Coord xyz(-23,42,70); + + {// stal a LeafNode + using NodeT = FloatTree::LeafNodeType; + CPPUNIT_ASSERT_EQUAL(Index(0), NodeT::getLevel()); + + FloatTree tree(background); + CPPUNIT_ASSERT_EQUAL(Index(0), tree.leafCount()); + CPPUNIT_ASSERT(!tree.isValueOn(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, tree.getValue(xyz), epsilon); + CPPUNIT_ASSERT(tree.root().stealNode(xyz, value, false) == nullptr); + + tree.setValue(xyz, value); + CPPUNIT_ASSERT_EQUAL(Index(1), tree.leafCount()); + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(value, tree.getValue(xyz), epsilon); + + NodeT* node = tree.root().stealNode(xyz, background, false); + CPPUNIT_ASSERT(node != nullptr); + CPPUNIT_ASSERT_EQUAL(Index(0), tree.leafCount()); + CPPUNIT_ASSERT(!tree.isValueOn(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, tree.getValue(xyz), epsilon); + CPPUNIT_ASSERT(tree.root().stealNode(xyz, value, false) == nullptr); + CPPUNIT_ASSERT_DOUBLES_EQUAL(value, node->getValue(xyz), epsilon); + CPPUNIT_ASSERT(node->isValueOn(xyz)); + delete node; + } + {// steal a bottom InternalNode + using NodeT = FloatTree::RootNodeType::ChildNodeType::ChildNodeType; + CPPUNIT_ASSERT_EQUAL(Index(1), NodeT::getLevel()); + + FloatTree tree(background); + CPPUNIT_ASSERT_EQUAL(Index(0), tree.leafCount()); + CPPUNIT_ASSERT(!tree.isValueOn(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, tree.getValue(xyz), epsilon); + CPPUNIT_ASSERT(tree.root().stealNode(xyz, value, false) == nullptr); + + tree.setValue(xyz, value); + CPPUNIT_ASSERT_EQUAL(Index(1), tree.leafCount()); + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(value, tree.getValue(xyz), epsilon); + + NodeT* node = tree.root().stealNode(xyz, background, false); + CPPUNIT_ASSERT(node != nullptr); + CPPUNIT_ASSERT_EQUAL(Index(0), tree.leafCount()); + CPPUNIT_ASSERT(!tree.isValueOn(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, tree.getValue(xyz), epsilon); + CPPUNIT_ASSERT(tree.root().stealNode(xyz, value, false) == nullptr); + CPPUNIT_ASSERT_DOUBLES_EQUAL(value, node->getValue(xyz), epsilon); + CPPUNIT_ASSERT(node->isValueOn(xyz)); + delete node; + } + {// steal a top InternalNode + using NodeT = FloatTree::RootNodeType::ChildNodeType; + CPPUNIT_ASSERT_EQUAL(Index(2), NodeT::getLevel()); + + FloatTree tree(background); + CPPUNIT_ASSERT_EQUAL(Index(0), tree.leafCount()); + CPPUNIT_ASSERT(!tree.isValueOn(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, tree.getValue(xyz), epsilon); + CPPUNIT_ASSERT(tree.root().stealNode(xyz, value, false) == nullptr); + + tree.setValue(xyz, value); + CPPUNIT_ASSERT_EQUAL(Index(1), tree.leafCount()); + CPPUNIT_ASSERT(tree.isValueOn(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(value, tree.getValue(xyz), epsilon); + + NodeT* node = tree.root().stealNode(xyz, background, false); + CPPUNIT_ASSERT(node != nullptr); + CPPUNIT_ASSERT_EQUAL(Index(0), tree.leafCount()); + CPPUNIT_ASSERT(!tree.isValueOn(xyz)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(background, tree.getValue(xyz), epsilon); + CPPUNIT_ASSERT(tree.root().stealNode(xyz, value, false) == nullptr); + CPPUNIT_ASSERT_DOUBLES_EQUAL(value, node->getValue(xyz), epsilon); + CPPUNIT_ASSERT(node->isValueOn(xyz)); + delete node; + } +} + +#if OPENVDB_ABI_VERSION_NUMBER >= 7 +void +TestTree::testNodeCount() +{ + //openvdb::util::CpuTimer timer;// use for benchmark test + + const openvdb::Vec3f center(0.0f, 0.0f, 0.0f); + const float radius = 1.0f; + //const int dim = 4096, halfWidth = 3;// use for benchmark test + const int dim = 512, halfWidth = 3;// use for unit test + //timer.start("\nGenerate level set sphere");// use for benchmark test + auto grid = openvdb::tools::createLevelSetSphere(radius, center, radius/dim, halfWidth); + //timer.stop();// use for benchmark test + auto& tree = grid->tree(); + + std::vector dims; + tree.getNodeLog2Dims(dims); + std::vector nodeCount1(dims.size()); + //timer.start("Old technique");// use for benchmark test + for (auto it = tree.cbeginNode(); it; ++it) ++(nodeCount1[dims.size()-1-it.getDepth()]); + //timer.restart("New technique");// use for benchmark test + const auto nodeCount2 = tree.nodeCount(); + //timer.stop();// use for benchmark test + CPPUNIT_ASSERT_EQUAL(nodeCount1.size(), nodeCount2.size()); + //for (size_t i=0; i children; + root.stealNodes(children); + CPPUNIT_ASSERT(root.empty()); + + // insert the root node children directly + for (ChildType* child : children) { + root.addChild(child); + } + CPPUNIT_ASSERT_EQUAL(openvdb::Index(2), root.getTableSize()); + + { // verify the coordinates of the root node children + auto rootIter = root.cbeginChildOn(); + CPPUNIT_ASSERT_EQUAL(c0, rootIter.getCoord()); + ++rootIter; + CPPUNIT_ASSERT_EQUAL(c1, rootIter.getCoord()); + } + } + + { // test inserting tiles and replacing them with child nodes + RootNodeType root(0.0f); + CPPUNIT_ASSERT(root.empty()); + + // no-op + root.addChild(nullptr); + + // populate the root node by inserting tiles + root.addTile(c0, /*value=*/1.0f, /*state=*/true); + root.addTile(c1, /*value=*/2.0f, /*state=*/true); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(2), root.getTableSize()); + CPPUNIT_ASSERT(root.hasActiveTiles()); + ASSERT_DOUBLES_EXACTLY_EQUAL(1.0f, root.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(2.0f, root.getValue(c1)); + + // insert child nodes with the same coordinates + root.addChild(new ChildType(c0, 3.0f)); + root.addChild(new ChildType(c1, 4.0f)); + + // insert a new child at c0 + root.addChild(new ChildType(c0, 5.0f)); + + // verify active tiles have been replaced by child nodes + CPPUNIT_ASSERT_EQUAL(openvdb::Index(2), root.getTableSize()); + CPPUNIT_ASSERT(!root.hasActiveTiles()); + + { // verify the coordinates of the root node children + auto rootIter = root.cbeginChildOn(); + CPPUNIT_ASSERT_EQUAL(c0, rootIter.getCoord()); + ASSERT_DOUBLES_EXACTLY_EQUAL(5.0f, root.getValue(c0)); + ++rootIter; + CPPUNIT_ASSERT_EQUAL(c1, rootIter.getCoord()); + } + } +} + +void +TestTree::testInternalNode() +{ + const openvdb::Coord c0(1000, 1000, 1000); + const openvdb::Coord c1(896, 896, 896); + + using InternalNodeType = InternalNodeType1; + using ChildType = LeafNodeType; + + { // test inserting child nodes directly and indirectly + openvdb::Coord c2 = c1.offsetBy(8,0,0); + openvdb::Coord c3 = c1.offsetBy(16,16,16); + + InternalNodeType internalNode(c1, 0.0f); + internalNode.touchLeaf(c2); + internalNode.touchLeaf(c3); + + CPPUNIT_ASSERT_EQUAL(openvdb::Index(2), internalNode.leafCount()); + CPPUNIT_ASSERT(!internalNode.hasActiveTiles()); + + { // verify c0 and c1 are the root node coordinates + auto childIter = internalNode.cbeginChildOn(); + CPPUNIT_ASSERT_EQUAL(c2, childIter.getCoord()); + ++childIter; + CPPUNIT_ASSERT_EQUAL(c3, childIter.getCoord()); + } + + // copy the internal node + InternalNodeType internalNodeCopy(internalNode); + + // steal the internal node children leaving it empty again + std::vector children; + internalNode.stealNodes(children, 0.0f, false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(0), internalNode.leafCount()); + + // insert the root node children directly + for (ChildType* child : children) { + internalNode.addChild(child); + } + CPPUNIT_ASSERT_EQUAL(openvdb::Index(2), internalNode.leafCount()); + + { // verify the coordinates of the root node children + auto childIter = internalNode.cbeginChildOn(); + CPPUNIT_ASSERT_EQUAL(c2, childIter.getCoord()); + ++childIter; + CPPUNIT_ASSERT_EQUAL(c3, childIter.getCoord()); + } + } + + { // test inserting a tile and replacing with a child node + InternalNodeType internalNode(c1, 0.0f); + CPPUNIT_ASSERT(!internalNode.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(0), internalNode.leafCount()); + + // add a tile + internalNode.addTile(openvdb::Index(0), /*value=*/1.0f, /*state=*/true); + CPPUNIT_ASSERT(internalNode.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(0), internalNode.leafCount()); + + // replace the tile with a child node + CPPUNIT_ASSERT(internalNode.addChild(new ChildType(c1, 2.0f))); + CPPUNIT_ASSERT(!internalNode.hasActiveTiles()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index(1), internalNode.leafCount()); + CPPUNIT_ASSERT_EQUAL(c1, internalNode.cbeginChildOn().getCoord()); + ASSERT_DOUBLES_EXACTLY_EQUAL(2.0f, internalNode.cbeginChildOn()->getValue(0)); + + // replace the child node with another child node + CPPUNIT_ASSERT(internalNode.addChild(new ChildType(c1, 3.0f))); + ASSERT_DOUBLES_EXACTLY_EQUAL(3.0f, internalNode.cbeginChildOn()->getValue(0)); + } + + { // test inserting child nodes that do and do not belong to the internal node + InternalNodeType internalNode(c1, 0.0f); + + // succeed if child belongs to this internal node + CPPUNIT_ASSERT(internalNode.addChild(new ChildType(c0.offsetBy(8,0,0)))); + CPPUNIT_ASSERT(internalNode.probeLeaf(c0.offsetBy(8,0,0))); + openvdb::Index index1 = internalNode.coordToOffset(c0); + openvdb::Index index2 = internalNode.coordToOffset(c0.offsetBy(8,0,0)); + CPPUNIT_ASSERT(!internalNode.isChildMaskOn(index1)); + CPPUNIT_ASSERT(internalNode.isChildMaskOn(index2)); + + // fail otherwise + CPPUNIT_ASSERT(!internalNode.addChild(new ChildType(c0.offsetBy(8000,0,0)))); + } +} + +// Copyright (c) DreamWorks Animation LLC +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/openvdb/unittest/TestTreeCombine.cc b/openvdb/unittest/TestTreeCombine.cc new file mode 100644 index 00000000..5eef312c --- /dev/null +++ b/openvdb/unittest/TestTreeCombine.cc @@ -0,0 +1,1044 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include "util.h" // for unittest_util::makeSphere() +#include // for std::max() and std::min() +#include // for std::isnan() and std::isinf() +#include // for std::numeric_limits +#include +#include +#include + +#define TEST_CSG_VERBOSE 0 + +#if TEST_CSG_VERBOSE +#include +#include +#endif + +namespace { +using Float433Tree = openvdb::tree::Tree4::Type; +using Float433Grid = openvdb::Grid; +} + + +class TestTreeCombine: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); Float433Grid::registerGrid(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestTreeCombine); + CPPUNIT_TEST(testCombine); + CPPUNIT_TEST(testCombine2); + CPPUNIT_TEST(testCompMax); + CPPUNIT_TEST(testCompMin); + CPPUNIT_TEST(testCompSum); + CPPUNIT_TEST(testCompProd); + CPPUNIT_TEST(testCompDiv); + CPPUNIT_TEST(testCompDivByZero); + CPPUNIT_TEST(testCompReplace); + CPPUNIT_TEST(testBoolTree); +#ifdef DWA_OPENVDB + CPPUNIT_TEST(testCsg); +#endif + CPPUNIT_TEST(testCsgCopy); + CPPUNIT_TEST(testCompActiveLeafVoxels); + CPPUNIT_TEST_SUITE_END(); + + void testCombine(); + void testCombine2(); + void testCompMax(); + void testCompMin(); + void testCompSum(); + void testCompProd(); + void testCompDiv(); + void testCompDivByZero(); + void testCompReplace(); + void testBoolTree(); + void testCsg(); + void testCsgCopy(); + void testCompActiveLeafVoxels(); + +private: + template + void testComp(const TreeComp&, const ValueComp&); + + template + void testCompRepl(); + + template + typename TreeT::Ptr + visitCsg(const TreeT& a, const TreeT& b, const TreeT& ref, const VisitorT&); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestTreeCombine); + + +//////////////////////////////////////// + + +namespace { +namespace Local { + +template +struct OrderDependentCombineOp { + OrderDependentCombineOp() {} + void operator()(const ValueT& a, const ValueT& b, ValueT& result) const { + result = a + ValueT(100) * b; // result is order-dependent on A and B + } +}; + +/// Test Tree::combine(), which takes a functor that accepts three arguments +/// (the a, b and result values). +template +void combine(TreeT& a, TreeT& b) +{ + a.combine(b, OrderDependentCombineOp()); +} + +/// Test Tree::combineExtended(), which takes a functor that accepts a single +/// CombineArgs argument, in which the functor can return a computed active state +/// for the output value. +template +void extendedCombine(TreeT& a, TreeT& b) +{ + using ValueT = typename TreeT::ValueType; + struct ArgsOp { + static void order(openvdb::CombineArgs& args) { + // The result is order-dependent on A and B. + args.setResult(args.a() + ValueT(100) * args.b()); + args.setResultIsActive(args.aIsActive() || args.bIsActive()); + } + }; + a.combineExtended(b, ArgsOp::order); +} + +template void compMax(TreeT& a, TreeT& b) { openvdb::tools::compMax(a, b); } +template void compMin(TreeT& a, TreeT& b) { openvdb::tools::compMin(a, b); } +template void compSum(TreeT& a, TreeT& b) { openvdb::tools::compSum(a, b); } +template void compMul(TreeT& a, TreeT& b) { openvdb::tools::compMul(a, b); }\ +template void compDiv(TreeT& a, TreeT& b) { openvdb::tools::compDiv(a, b); }\ + +inline float orderf(float a, float b) { return a + 100.0f * b; } +inline float maxf(float a, float b) { return std::max(a, b); } +inline float minf(float a, float b) { return std::min(a, b); } +inline float sumf(float a, float b) { return a + b; } +inline float mulf(float a, float b) { return a * b; } +inline float divf(float a, float b) { return a / b; } + +inline openvdb::Vec3f orderv(const openvdb::Vec3f& a, const openvdb::Vec3f& b) { return a+100.0f*b; } +inline openvdb::Vec3f maxv(const openvdb::Vec3f& a, const openvdb::Vec3f& b) { + const float aMag = a.lengthSqr(), bMag = b.lengthSqr(); + return (aMag > bMag ? a : (bMag > aMag ? b : std::max(a, b))); +} +inline openvdb::Vec3f minv(const openvdb::Vec3f& a, const openvdb::Vec3f& b) { + const float aMag = a.lengthSqr(), bMag = b.lengthSqr(); + return (aMag < bMag ? a : (bMag < aMag ? b : std::min(a, b))); +} +inline openvdb::Vec3f sumv(const openvdb::Vec3f& a, const openvdb::Vec3f& b) { return a + b; } +inline openvdb::Vec3f mulv(const openvdb::Vec3f& a, const openvdb::Vec3f& b) { return a * b; } +inline openvdb::Vec3f divv(const openvdb::Vec3f& a, const openvdb::Vec3f& b) { return a / b; } + +} // namespace Local +} // unnamed namespace + + +void +TestTreeCombine::testCombine() +{ + testComp(Local::combine, Local::orderf); + testComp(Local::combine, Local::orderv); + + testComp(Local::extendedCombine, Local::orderf); + testComp(Local::extendedCombine, Local::orderv); +} + + +void +TestTreeCombine::testCompMax() +{ + testComp(Local::compMax, Local::maxf); + testComp(Local::compMax, Local::maxv); +} + + +void +TestTreeCombine::testCompMin() +{ + testComp(Local::compMin, Local::minf); + testComp(Local::compMin, Local::minv); +} + + +void +TestTreeCombine::testCompSum() +{ + testComp(Local::compSum, Local::sumf); + testComp(Local::compSum, Local::sumv); +} + + +void +TestTreeCombine::testCompProd() +{ + testComp(Local::compMul, Local::mulf); + testComp(Local::compMul, Local::mulv); +} + + +void +TestTreeCombine::testCompDiv() +{ + testComp(Local::compDiv, Local::divf); + testComp(Local::compDiv, Local::divv); +} + + +void +TestTreeCombine::testCompDivByZero() +{ + const openvdb::Coord c0(0), c1(1), c2(2), c3(3), c4(4); + + // Verify that integer-valued grids behave well w.r.t. division by zero. + { + const openvdb::Int32 inf = std::numeric_limits::max(); + + openvdb::Int32Tree a(/*background=*/1), b(0); + + a.setValueOn(c0); + a.setValueOn(c1); + a.setValueOn(c2, -1); + a.setValueOn(c3, -1); + a.setValueOn(c4, 0); + b.setValueOn(c1); + b.setValueOn(c3); + + openvdb::tools::compDiv(a, b); + + CPPUNIT_ASSERT_EQUAL( inf, a.getValue(c0)); // 1 / 0 + CPPUNIT_ASSERT_EQUAL( inf, a.getValue(c1)); // 1 / 0 + CPPUNIT_ASSERT_EQUAL(-inf, a.getValue(c2)); // -1 / 0 + CPPUNIT_ASSERT_EQUAL(-inf, a.getValue(c3)); // -1 / 0 + CPPUNIT_ASSERT_EQUAL( 0, a.getValue(c4)); // 0 / 0 + } + { + const openvdb::Index32 zero(0), inf = std::numeric_limits::max(); + + openvdb::UInt32Tree a(/*background=*/1), b(0); + + a.setValueOn(c0); + a.setValueOn(c1); + a.setValueOn(c2, zero); + b.setValueOn(c1); + + openvdb::tools::compDiv(a, b); + + CPPUNIT_ASSERT_EQUAL( inf, a.getValue(c0)); // 1 / 0 + CPPUNIT_ASSERT_EQUAL( inf, a.getValue(c1)); // 1 / 0 + CPPUNIT_ASSERT_EQUAL(zero, a.getValue(c2)); // 0 / 0 + } + + // Verify that non-integer-valued grids don't use integer division semantics. + { + openvdb::FloatTree a(/*background=*/1.0), b(0.0); + + a.setValueOn(c0); + a.setValueOn(c1); + a.setValueOn(c2, -1.0); + a.setValueOn(c3, -1.0); + a.setValueOn(c4, 0.0); + b.setValueOn(c1); + b.setValueOn(c3); + + openvdb::tools::compDiv(a, b); + + CPPUNIT_ASSERT(std::isinf(a.getValue(c0))); // 1 / 0 + CPPUNIT_ASSERT(std::isinf(a.getValue(c1))); // 1 / 0 + CPPUNIT_ASSERT(std::isinf(a.getValue(c2))); // -1 / 0 + CPPUNIT_ASSERT(std::isinf(a.getValue(c3))); // -1 / 0 + CPPUNIT_ASSERT(std::isnan(a.getValue(c4))); // 0 / 0 + } +} + + +void +TestTreeCombine::testCompReplace() +{ + testCompRepl(); + testCompRepl(); +} + + +template +void +TestTreeCombine::testComp(const TreeComp& comp, const ValueComp& op) +{ + using ValueT = typename TreeT::ValueType; + + const ValueT + zero = openvdb::zeroVal(), + minusOne = zero + (-1), + minusTwo = zero + (-2), + one = zero + 1, + three = zero + 3, + four = zero + 4, + five = zero + 5; + + { + TreeT aTree(/*background=*/one); + aTree.setValueOn(openvdb::Coord(0, 0, 0), three); + aTree.setValueOn(openvdb::Coord(0, 0, 1), three); + aTree.setValueOn(openvdb::Coord(0, 0, 2), aTree.background()); + aTree.setValueOn(openvdb::Coord(0, 1, 2), aTree.background()); + aTree.setValueOff(openvdb::Coord(1, 0, 0), three); + aTree.setValueOff(openvdb::Coord(1, 0, 1), three); + + TreeT bTree(five); + bTree.setValueOn(openvdb::Coord(0, 0, 0), minusOne); + bTree.setValueOn(openvdb::Coord(0, 1, 0), four); + bTree.setValueOn(openvdb::Coord(0, 1, 2), minusTwo); + bTree.setValueOff(openvdb::Coord(1, 0, 0), minusOne); + bTree.setValueOff(openvdb::Coord(1, 1, 0), four); + + // Call aTree.compMax(bTree), aTree.compSum(bTree), etc. + comp(aTree, bTree); + + // a = 3 (On), b = -1 (On) + CPPUNIT_ASSERT_EQUAL(op(three, minusOne), aTree.getValue(openvdb::Coord(0, 0, 0))); + + // a = 3 (On), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(op(three, five), aTree.getValue(openvdb::Coord(0, 0, 1))); + CPPUNIT_ASSERT(aTree.isValueOn(openvdb::Coord(0, 0, 1))); + + // a = 1 (On, = bg), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(op(one, five), aTree.getValue(openvdb::Coord(0, 0, 2))); + CPPUNIT_ASSERT(aTree.isValueOn(openvdb::Coord(0, 0, 2))); + + // a = 1 (On, = bg), b = -2 (On) + CPPUNIT_ASSERT_EQUAL(op(one, minusTwo), aTree.getValue(openvdb::Coord(0, 1, 2))); + CPPUNIT_ASSERT(aTree.isValueOn(openvdb::Coord(0, 1, 2))); + + // a = 1 (bg), b = 4 (On) + CPPUNIT_ASSERT_EQUAL(op(one, four), aTree.getValue(openvdb::Coord(0, 1, 0))); + CPPUNIT_ASSERT(aTree.isValueOn(openvdb::Coord(0, 1, 0))); + + // a = 3 (Off), b = -1 (Off) + CPPUNIT_ASSERT_EQUAL(op(three, minusOne), aTree.getValue(openvdb::Coord(1, 0, 0))); + CPPUNIT_ASSERT(aTree.isValueOff(openvdb::Coord(1, 0, 0))); + + // a = 3 (Off), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(op(three, five), aTree.getValue(openvdb::Coord(1, 0, 1))); + CPPUNIT_ASSERT(aTree.isValueOff(openvdb::Coord(1, 0, 1))); + + // a = 1 (bg), b = 4 (Off) + CPPUNIT_ASSERT_EQUAL(op(one, four), aTree.getValue(openvdb::Coord(1, 1, 0))); + CPPUNIT_ASSERT(aTree.isValueOff(openvdb::Coord(1, 1, 0))); + + // a = 1 (bg), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(op(one, five), aTree.getValue(openvdb::Coord(1000, 1, 2))); + CPPUNIT_ASSERT(aTree.isValueOff(openvdb::Coord(1000, 1, 2))); + } + + // As above, but combining the A grid into the B grid + { + TreeT aTree(/*bg=*/one); + aTree.setValueOn(openvdb::Coord(0, 0, 0), three); + aTree.setValueOn(openvdb::Coord(0, 0, 1), three); + aTree.setValueOn(openvdb::Coord(0, 0, 2), aTree.background()); + aTree.setValueOn(openvdb::Coord(0, 1, 2), aTree.background()); + aTree.setValueOff(openvdb::Coord(1, 0, 0), three); + aTree.setValueOff(openvdb::Coord(1, 0, 1), three); + + TreeT bTree(five); + bTree.setValueOn(openvdb::Coord(0, 0, 0), minusOne); + bTree.setValueOn(openvdb::Coord(0, 1, 0), four); + bTree.setValueOn(openvdb::Coord(0, 1, 2), minusTwo); + bTree.setValueOff(openvdb::Coord(1, 0, 0), minusOne); + bTree.setValueOff(openvdb::Coord(1, 1, 0), four); + + // Call bTree.compMax(aTree), bTree.compSum(aTree), etc. + comp(bTree, aTree); + + // a = 3 (On), b = -1 (On) + CPPUNIT_ASSERT_EQUAL(op(minusOne, three), bTree.getValue(openvdb::Coord(0, 0, 0))); + + // a = 3 (On), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(op(five, three), bTree.getValue(openvdb::Coord(0, 0, 1))); + CPPUNIT_ASSERT(bTree.isValueOn(openvdb::Coord(0, 0, 1))); + + // a = 1 (On, = bg), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(op(five, one), bTree.getValue(openvdb::Coord(0, 0, 2))); + CPPUNIT_ASSERT(bTree.isValueOn(openvdb::Coord(0, 0, 2))); + + // a = 1 (On, = bg), b = -2 (On) + CPPUNIT_ASSERT_EQUAL(op(minusTwo, one), bTree.getValue(openvdb::Coord(0, 1, 2))); + CPPUNIT_ASSERT(bTree.isValueOn(openvdb::Coord(0, 1, 2))); + + // a = 1 (bg), b = 4 (On) + CPPUNIT_ASSERT_EQUAL(op(four, one), bTree.getValue(openvdb::Coord(0, 1, 0))); + CPPUNIT_ASSERT(bTree.isValueOn(openvdb::Coord(0, 1, 0))); + + // a = 3 (Off), b = -1 (Off) + CPPUNIT_ASSERT_EQUAL(op(minusOne, three), bTree.getValue(openvdb::Coord(1, 0, 0))); + CPPUNIT_ASSERT(bTree.isValueOff(openvdb::Coord(1, 0, 0))); + + // a = 3 (Off), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(op(five, three), bTree.getValue(openvdb::Coord(1, 0, 1))); + CPPUNIT_ASSERT(bTree.isValueOff(openvdb::Coord(1, 0, 1))); + + // a = 1 (bg), b = 4 (Off) + CPPUNIT_ASSERT_EQUAL(op(four, one), bTree.getValue(openvdb::Coord(1, 1, 0))); + CPPUNIT_ASSERT(bTree.isValueOff(openvdb::Coord(1, 1, 0))); + + // a = 1 (bg), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(op(five, one), bTree.getValue(openvdb::Coord(1000, 1, 2))); + CPPUNIT_ASSERT(bTree.isValueOff(openvdb::Coord(1000, 1, 2))); + } +} + + +//////////////////////////////////////// + + +void +TestTreeCombine::testCombine2() +{ + using openvdb::Coord; + using openvdb::Vec3d; + + struct Local { + static void floatAverage(const float& a, const float& b, float& result) + { result = 0.5f * (a + b); } + static void vec3dAverage(const Vec3d& a, const Vec3d& b, Vec3d& result) + { result = 0.5 * (a + b); } + static void vec3dFloatMultiply(const Vec3d& a, const float& b, Vec3d& result) + { result = a * b; } + static void vec3dBoolMultiply(const Vec3d& a, const bool& b, Vec3d& result) + { result = a * b; } + }; + + const Coord c0(0, 0, 0), c1(0, 0, 1), c2(0, 1, 0), c3(1, 0, 0), c4(1000, 1, 2); + + openvdb::FloatTree aFloatTree(/*bg=*/1.0), bFloatTree(5.0), outFloatTree(1.0); + aFloatTree.setValue(c0, 3.0); + aFloatTree.setValue(c1, 3.0); + bFloatTree.setValue(c0, -1.0); + bFloatTree.setValue(c2, 4.0); + outFloatTree.combine2(aFloatTree, bFloatTree, Local::floatAverage); + + const float tolerance = 0.0; + // Average of set value 3 and set value -1 + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, outFloatTree.getValue(c0), tolerance); + // Average of set value 3 and bg value 5 + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, outFloatTree.getValue(c1), tolerance); + // Average of bg value 1 and set value 4 + CPPUNIT_ASSERT_DOUBLES_EQUAL(2.5, outFloatTree.getValue(c2), tolerance); + // Average of bg value 1 and bg value 5 + CPPUNIT_ASSERT(outFloatTree.isValueOff(c3)); + CPPUNIT_ASSERT(outFloatTree.isValueOff(c4)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, outFloatTree.getValue(c3), tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, outFloatTree.getValue(c4), tolerance); + + // As above, but combining vector grids: + const Vec3d zero(0), one(1), two(2), three(3), four(4), five(5); + openvdb::Vec3DTree aVecTree(/*bg=*/one), bVecTree(five), outVecTree(one); + aVecTree.setValue(c0, three); + aVecTree.setValue(c1, three); + bVecTree.setValue(c0, -1.0 * one); + bVecTree.setValue(c2, four); + outVecTree.combine2(aVecTree, bVecTree, Local::vec3dAverage); + + // Average of set value 3 and set value -1 + CPPUNIT_ASSERT_EQUAL(one, outVecTree.getValue(c0)); + // Average of set value 3 and bg value 5 + CPPUNIT_ASSERT_EQUAL(four, outVecTree.getValue(c1)); + // Average of bg value 1 and set value 4 + CPPUNIT_ASSERT_EQUAL(2.5 * one, outVecTree.getValue(c2)); + // Average of bg value 1 and bg value 5 + CPPUNIT_ASSERT(outVecTree.isValueOff(c3)); + CPPUNIT_ASSERT(outVecTree.isValueOff(c4)); + CPPUNIT_ASSERT_EQUAL(three, outVecTree.getValue(c3)); + CPPUNIT_ASSERT_EQUAL(three, outVecTree.getValue(c4)); + + // Multiply the vector tree by the scalar tree. + { + openvdb::Vec3DTree vecTree(one); + vecTree.combine2(outVecTree, outFloatTree, Local::vec3dFloatMultiply); + + // Product of set value (1, 1, 1) and set value 1 + CPPUNIT_ASSERT(vecTree.isValueOn(c0)); + CPPUNIT_ASSERT_EQUAL(one, vecTree.getValue(c0)); + // Product of set value (4, 4, 4) and set value 4 + CPPUNIT_ASSERT(vecTree.isValueOn(c1)); + CPPUNIT_ASSERT_EQUAL(4 * 4 * one, vecTree.getValue(c1)); + // Product of set value (2.5, 2.5, 2.5) and set value 2.5 + CPPUNIT_ASSERT(vecTree.isValueOn(c2)); + CPPUNIT_ASSERT_EQUAL(2.5 * 2.5 * one, vecTree.getValue(c2)); + // Product of bg value (3, 3, 3) and bg value 3 + CPPUNIT_ASSERT(vecTree.isValueOff(c3)); + CPPUNIT_ASSERT(vecTree.isValueOff(c4)); + CPPUNIT_ASSERT_EQUAL(3 * 3 * one, vecTree.getValue(c3)); + CPPUNIT_ASSERT_EQUAL(3 * 3 * one, vecTree.getValue(c4)); + } + + // Multiply the vector tree by a boolean tree. + { + openvdb::BoolTree boolTree(0); + boolTree.setValue(c0, true); + boolTree.setValue(c1, false); + boolTree.setValue(c2, true); + + openvdb::Vec3DTree vecTree(one); + vecTree.combine2(outVecTree, boolTree, Local::vec3dBoolMultiply); + + // Product of set value (1, 1, 1) and set value 1 + CPPUNIT_ASSERT(vecTree.isValueOn(c0)); + CPPUNIT_ASSERT_EQUAL(one, vecTree.getValue(c0)); + // Product of set value (4, 4, 4) and set value 0 + CPPUNIT_ASSERT(vecTree.isValueOn(c1)); + CPPUNIT_ASSERT_EQUAL(zero, vecTree.getValue(c1)); + // Product of set value (2.5, 2.5, 2.5) and set value 1 + CPPUNIT_ASSERT(vecTree.isValueOn(c2)); + CPPUNIT_ASSERT_EQUAL(2.5 * one, vecTree.getValue(c2)); + // Product of bg value (3, 3, 3) and bg value 0 + CPPUNIT_ASSERT(vecTree.isValueOff(c3)); + CPPUNIT_ASSERT(vecTree.isValueOff(c4)); + CPPUNIT_ASSERT_EQUAL(zero, vecTree.getValue(c3)); + CPPUNIT_ASSERT_EQUAL(zero, vecTree.getValue(c4)); + } + + // Verify that a vector tree can't be combined into a scalar tree + // (although the reverse is allowed). + { + struct Local2 { + static void f(const float& a, const Vec3d&, float& result) { result = a; } + }; + openvdb::FloatTree floatTree(5.0), outTree; + openvdb::Vec3DTree vecTree(one); + CPPUNIT_ASSERT_THROW(outTree.combine2(floatTree, vecTree, Local2::f), openvdb::TypeError); + } +} + + +//////////////////////////////////////// + + +void +TestTreeCombine::testBoolTree() +{ + openvdb::BoolGrid::Ptr sphere = openvdb::BoolGrid::create(); + + unittest_util::makeSphere(/*dim=*/openvdb::Coord(32), + /*ctr=*/openvdb::Vec3f(0), + /*radius=*/20.0, *sphere, + unittest_util::SPHERE_SPARSE_NARROW_BAND); + + openvdb::BoolGrid::Ptr + aGrid = sphere->copy(), + bGrid = sphere->copy(); + + // CSG operations work only on level sets with a nonzero inside and outside values. + CPPUNIT_ASSERT_THROW(openvdb::tools::csgUnion(aGrid->tree(), bGrid->tree()), + openvdb::ValueError); + CPPUNIT_ASSERT_THROW(openvdb::tools::csgIntersection(aGrid->tree(), bGrid->tree()), + openvdb::ValueError); + CPPUNIT_ASSERT_THROW(openvdb::tools::csgDifference(aGrid->tree(), bGrid->tree()), + openvdb::ValueError); + + openvdb::tools::compSum(aGrid->tree(), bGrid->tree()); + + bGrid = sphere->copy(); + openvdb::tools::compMax(aGrid->tree(), bGrid->tree()); + + int mismatches = 0; + openvdb::BoolGrid::ConstAccessor acc = sphere->getConstAccessor(); + for (openvdb::BoolGrid::ValueAllCIter it = aGrid->cbeginValueAll(); it; ++it) { + if (*it != acc.getValue(it.getCoord())) ++mismatches; + } + CPPUNIT_ASSERT_EQUAL(0, mismatches); +} + + +//////////////////////////////////////// + + +template +void +TestTreeCombine::testCompRepl() +{ + using ValueT = typename TreeT::ValueType; + + const ValueT + zero = openvdb::zeroVal(), + minusOne = zero + (-1), + one = zero + 1, + three = zero + 3, + four = zero + 4, + five = zero + 5; + + { + TreeT aTree(/*bg=*/one); + aTree.setValueOn(openvdb::Coord(0, 0, 0), three); + aTree.setValueOn(openvdb::Coord(0, 0, 1), three); + aTree.setValueOn(openvdb::Coord(0, 0, 2), aTree.background()); + aTree.setValueOn(openvdb::Coord(0, 1, 2), aTree.background()); + aTree.setValueOff(openvdb::Coord(1, 0, 0), three); + aTree.setValueOff(openvdb::Coord(1, 0, 1), three); + + TreeT bTree(five); + bTree.setValueOn(openvdb::Coord(0, 0, 0), minusOne); + bTree.setValueOn(openvdb::Coord(0, 1, 0), four); + bTree.setValueOn(openvdb::Coord(0, 1, 2), minusOne); + bTree.setValueOff(openvdb::Coord(1, 0, 0), minusOne); + bTree.setValueOff(openvdb::Coord(1, 1, 0), four); + + // Copy active voxels of bTree into aTree. + openvdb::tools::compReplace(aTree, bTree); + + // a = 3 (On), b = -1 (On) + CPPUNIT_ASSERT_EQUAL(minusOne, aTree.getValue(openvdb::Coord(0, 0, 0))); + + // a = 3 (On), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(three, aTree.getValue(openvdb::Coord(0, 0, 1))); + CPPUNIT_ASSERT(aTree.isValueOn(openvdb::Coord(0, 0, 1))); + + // a = 1 (On, = bg), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(one, aTree.getValue(openvdb::Coord(0, 0, 2))); + CPPUNIT_ASSERT(aTree.isValueOn(openvdb::Coord(0, 0, 2))); + + // a = 1 (On, = bg), b = -1 (On) + CPPUNIT_ASSERT_EQUAL(minusOne, aTree.getValue(openvdb::Coord(0, 1, 2))); + CPPUNIT_ASSERT(aTree.isValueOn(openvdb::Coord(0, 1, 2))); + + // a = 1 (bg), b = 4 (On) + CPPUNIT_ASSERT_EQUAL(four, aTree.getValue(openvdb::Coord(0, 1, 0))); + CPPUNIT_ASSERT(aTree.isValueOn(openvdb::Coord(0, 1, 0))); + + // a = 3 (Off), b = -1 (Off) + CPPUNIT_ASSERT_EQUAL(three, aTree.getValue(openvdb::Coord(1, 0, 0))); + CPPUNIT_ASSERT(aTree.isValueOff(openvdb::Coord(1, 0, 0))); + + // a = 3 (Off), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(three, aTree.getValue(openvdb::Coord(1, 0, 1))); + CPPUNIT_ASSERT(aTree.isValueOff(openvdb::Coord(1, 0, 1))); + + // a = 1 (bg), b = 4 (Off) + CPPUNIT_ASSERT_EQUAL(one, aTree.getValue(openvdb::Coord(1, 1, 0))); + CPPUNIT_ASSERT(aTree.isValueOff(openvdb::Coord(1, 1, 0))); + + // a = 1 (bg), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(one, aTree.getValue(openvdb::Coord(1000, 1, 2))); + CPPUNIT_ASSERT(aTree.isValueOff(openvdb::Coord(1000, 1, 2))); + } + + // As above, but combining the A grid into the B grid + { + TreeT aTree(/*background=*/one); + aTree.setValueOn(openvdb::Coord(0, 0, 0), three); + aTree.setValueOn(openvdb::Coord(0, 0, 1), three); + aTree.setValueOn(openvdb::Coord(0, 0, 2), aTree.background()); + aTree.setValueOn(openvdb::Coord(0, 1, 2), aTree.background()); + aTree.setValueOff(openvdb::Coord(1, 0, 0), three); + aTree.setValueOff(openvdb::Coord(1, 0, 1), three); + + TreeT bTree(five); + bTree.setValueOn(openvdb::Coord(0, 0, 0), minusOne); + bTree.setValueOn(openvdb::Coord(0, 1, 0), four); + bTree.setValueOn(openvdb::Coord(0, 1, 2), minusOne); + bTree.setValueOff(openvdb::Coord(1, 0, 0), minusOne); + bTree.setValueOff(openvdb::Coord(1, 1, 0), four); + + // Copy active voxels of aTree into bTree. + openvdb::tools::compReplace(bTree, aTree); + + // a = 3 (On), b = -1 (On) + CPPUNIT_ASSERT_EQUAL(three, bTree.getValue(openvdb::Coord(0, 0, 0))); + + // a = 3 (On), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(three, bTree.getValue(openvdb::Coord(0, 0, 1))); + CPPUNIT_ASSERT(bTree.isValueOn(openvdb::Coord(0, 0, 1))); + + // a = 1 (On, = bg), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(one, bTree.getValue(openvdb::Coord(0, 0, 2))); + CPPUNIT_ASSERT(bTree.isValueOn(openvdb::Coord(0, 0, 2))); + + // a = 1 (On, = bg), b = -1 (On) + CPPUNIT_ASSERT_EQUAL(one, bTree.getValue(openvdb::Coord(0, 1, 2))); + CPPUNIT_ASSERT(bTree.isValueOn(openvdb::Coord(0, 1, 2))); + + // a = 1 (bg), b = 4 (On) + CPPUNIT_ASSERT_EQUAL(four, bTree.getValue(openvdb::Coord(0, 1, 0))); + CPPUNIT_ASSERT(bTree.isValueOn(openvdb::Coord(0, 1, 0))); + + // a = 3 (Off), b = -1 (Off) + CPPUNIT_ASSERT_EQUAL(minusOne, bTree.getValue(openvdb::Coord(1, 0, 0))); + CPPUNIT_ASSERT(bTree.isValueOff(openvdb::Coord(1, 0, 0))); + + // a = 3 (Off), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(five, bTree.getValue(openvdb::Coord(1, 0, 1))); + CPPUNIT_ASSERT(bTree.isValueOff(openvdb::Coord(1, 0, 1))); + + // a = 1 (bg), b = 4 (Off) + CPPUNIT_ASSERT_EQUAL(four, bTree.getValue(openvdb::Coord(1, 1, 0))); + CPPUNIT_ASSERT(bTree.isValueOff(openvdb::Coord(1, 1, 0))); + + // a = 1 (bg), b = 5 (bg) + CPPUNIT_ASSERT_EQUAL(five, bTree.getValue(openvdb::Coord(1000, 1, 2))); + CPPUNIT_ASSERT(bTree.isValueOff(openvdb::Coord(1000, 1, 2))); + } +} + + +//////////////////////////////////////// + + +void +TestTreeCombine::testCsg() +{ + using TreeT = openvdb::FloatTree; + using TreePtr = TreeT::Ptr; + using GridT = openvdb::Grid; + + struct Local { + static TreePtr readFile(const std::string& fname) { + std::string filename(fname), gridName("LevelSet"); + size_t space = filename.find_last_of(' '); + if (space != std::string::npos) { + gridName = filename.substr(space + 1); + filename.erase(space); + } + + TreePtr tree; + openvdb::io::File file(filename); + file.open(); + if (openvdb::GridBase::Ptr basePtr = file.readGrid(gridName)) { + if (GridT::Ptr gridPtr = openvdb::gridPtrCast(basePtr)) { + tree = gridPtr->treePtr(); + } + } + file.close(); + return tree; + } + + //static void writeFile(TreePtr tree, const std::string& filename) { + // openvdb::io::File file(filename); + // openvdb::GridPtrVec grids; + // GridT::Ptr grid = openvdb::createGrid(tree); + // grid->setName("LevelSet"); + // grids.push_back(grid); + // file.write(grids); + //} + + static void visitorUnion(TreeT& a, TreeT& b) { openvdb::tools::csgUnion(a, b); } + static void visitorIntersect(TreeT& a, TreeT& b) { openvdb::tools::csgIntersection(a, b); } + static void visitorDiff(TreeT& a, TreeT& b) { openvdb::tools::csgDifference(a, b); } + }; + + TreePtr smallTree1, smallTree2, largeTree1, largeTree2, refTree, outTree; + +#if TEST_CSG_VERBOSE + openvdb::util::CpuTimer timer; + timer.start(); +#endif + + const std::string testDir("/work/rd/fx_tools/vdb_unittest/TestGridCombine::testCsg/"); + smallTree1 = Local::readFile(testDir + "small1.vdb2 LevelSet"); + CPPUNIT_ASSERT(smallTree1.get() != nullptr); + smallTree2 = Local::readFile(testDir + "small2.vdb2 Cylinder"); + CPPUNIT_ASSERT(smallTree2.get() != nullptr); + largeTree1 = Local::readFile(testDir + "large1.vdb2 LevelSet"); + CPPUNIT_ASSERT(largeTree1.get() != nullptr); + largeTree2 = Local::readFile(testDir + "large2.vdb2 LevelSet"); + CPPUNIT_ASSERT(largeTree2.get() != nullptr); + +#if TEST_CSG_VERBOSE + std::cerr << "file read: " << timer.milliseconds() << " msec\n"; +#endif + +#if TEST_CSG_VERBOSE + std::cerr << "\n\n"; +#endif + refTree = Local::readFile(testDir + "small_union.vdb2"); + outTree = visitCsg(*smallTree1, *smallTree2, *refTree, Local::visitorUnion); + //Local::writeFile(outTree, "small_union_out.vdb2"); + refTree = Local::readFile(testDir + "large_union.vdb2"); + outTree = visitCsg(*largeTree1, *largeTree2, *refTree, Local::visitorUnion); + //Local::writeFile(outTree, "large_union_out.vdb2"); + +#if TEST_CSG_VERBOSE + std::cerr << "\n\n"; +#endif + refTree = Local::readFile(testDir + "small_intersection.vdb2"); + outTree = visitCsg(*smallTree1, *smallTree2, *refTree, Local::visitorIntersect); + //Local::writeFile(outTree, "small_intersection_out.vdb2"); + refTree = Local::readFile(testDir + "large_intersection.vdb2"); + outTree = visitCsg(*largeTree1, *largeTree2, *refTree, Local::visitorIntersect); + //Local::writeFile(outTree, "large_intersection_out.vdb2"); + +#if TEST_CSG_VERBOSE + std::cerr << "\n\n"; +#endif + refTree = Local::readFile(testDir + "small_difference.vdb2"); + outTree = visitCsg(*smallTree1, *smallTree2, *refTree, Local::visitorDiff); + //Local::writeFile(outTree, "small_difference_out.vdb2"); + refTree = Local::readFile(testDir + "large_difference.vdb2"); + outTree = visitCsg(*largeTree1, *largeTree2, *refTree, Local::visitorDiff); + //Local::writeFile(outTree, "large_difference_out.vdb2"); +} + + +template +typename TreeT::Ptr +TestTreeCombine::visitCsg(const TreeT& aInputTree, const TreeT& bInputTree, + const TreeT& refTree, const VisitorT& visitor) +{ + using TreePtr = typename TreeT::Ptr; + +#if TEST_CSG_VERBOSE + openvdb::util::CpuTimer timer; + timer.start(); +#endif + TreePtr aTree(new TreeT(aInputTree)); + TreeT bTree(bInputTree); +#if TEST_CSG_VERBOSE + std::cerr << "deep copy: " << timer.milliseconds() << " msec\n"; +#endif + +#if (TEST_CSG_VERBOSE > 1) + std::cerr << "\nA grid:\n"; + aTree->print(std::cerr, /*verbose=*/3); + std::cerr << "\nB grid:\n"; + bTree.print(std::cerr, /*verbose=*/3); + std::cerr << "\nExpected:\n"; + refTree.print(std::cerr, /*verbose=*/3); + std::cerr << "\n"; +#endif + + // Compute the CSG combination of the two grids. +#if TEST_CSG_VERBOSE + timer.start(); +#endif + visitor(*aTree, bTree); +#if TEST_CSG_VERBOSE + std::cerr << "combine: " << timer.milliseconds() << " msec\n"; +#endif +#if (TEST_CSG_VERBOSE > 1) + std::cerr << "\nActual:\n"; + aTree->print(std::cerr, /*verbose=*/3); +#endif + + std::ostringstream aInfo, refInfo; + aTree->print(aInfo, /*verbose=*/2); + refTree.print(refInfo, /*verbose=*/2); + + CPPUNIT_ASSERT_EQUAL(refInfo.str(), aInfo.str()); + + CPPUNIT_ASSERT(aTree->hasSameTopology(refTree)); + + return aTree; +} + + +//////////////////////////////////////// + + +void +TestTreeCombine::testCsgCopy() +{ + const float voxelSize = 0.2f; + const float radius = 3.0f; + openvdb::Vec3f center(0.0f, 0.0f, 0.0f); + + openvdb::FloatGrid::Ptr gridA = + openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + + openvdb::Coord ijkA = gridA->transform().worldToIndexNodeCentered(center); + CPPUNIT_ASSERT(gridA->tree().getValue(ijkA) < 0.0f); // center is inside + + center.x() += 3.5f; + + openvdb::FloatGrid::Ptr gridB = + openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + + openvdb::Coord ijkB = gridA->transform().worldToIndexNodeCentered(center); + CPPUNIT_ASSERT(gridB->tree().getValue(ijkB) < 0.0f); // center is inside + + openvdb::FloatGrid::Ptr unionGrid = openvdb::tools::csgUnionCopy(*gridA, *gridB); + openvdb::FloatGrid::Ptr intersectionGrid = openvdb::tools::csgIntersectionCopy(*gridA, *gridB); + openvdb::FloatGrid::Ptr differenceGrid = openvdb::tools::csgDifferenceCopy(*gridA, *gridB); + + CPPUNIT_ASSERT(unionGrid.get() != nullptr); + CPPUNIT_ASSERT(intersectionGrid.get() != nullptr); + CPPUNIT_ASSERT(differenceGrid.get() != nullptr); + + CPPUNIT_ASSERT(!unionGrid->empty()); + CPPUNIT_ASSERT(!intersectionGrid->empty()); + CPPUNIT_ASSERT(!differenceGrid->empty()); + + // test inside / outside sign + + CPPUNIT_ASSERT(unionGrid->tree().getValue(ijkA) < 0.0f); + CPPUNIT_ASSERT(unionGrid->tree().getValue(ijkB) < 0.0f); + + CPPUNIT_ASSERT(!(intersectionGrid->tree().getValue(ijkA) < 0.0f)); + CPPUNIT_ASSERT(!(intersectionGrid->tree().getValue(ijkB) < 0.0f)); + + CPPUNIT_ASSERT(differenceGrid->tree().getValue(ijkA) < 0.0f); + CPPUNIT_ASSERT(!(differenceGrid->tree().getValue(ijkB) < 0.0f)); +} + + +//////////////////////////////////////// + +void +TestTreeCombine::testCompActiveLeafVoxels() +{ + {//replace float tree (default argument) + openvdb::FloatTree srcTree(0.0f), dstTree(0.0f); + + dstTree.setValue(openvdb::Coord(1,1,1), 1.0f); + srcTree.setValue(openvdb::Coord(1,1,1), 2.0f); + srcTree.setValue(openvdb::Coord(8,8,8), 3.0f); + + CPPUNIT_ASSERT_EQUAL(1, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(2, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(1.0f, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(0.0f, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(!dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + + openvdb::tools::compActiveLeafVoxels(srcTree, dstTree); + + CPPUNIT_ASSERT_EQUAL(2, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(0, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(2.0f, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(3.0f, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + } + {//replace float tree (lambda expression) + openvdb::FloatTree srcTree(0.0f), dstTree(0.0f); + + dstTree.setValue(openvdb::Coord(1,1,1), 1.0f); + srcTree.setValue(openvdb::Coord(1,1,1), 2.0f); + srcTree.setValue(openvdb::Coord(8,8,8), 3.0f); + + CPPUNIT_ASSERT_EQUAL(1, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(2, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(1.0f, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(0.0f, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(!dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + + openvdb::tools::compActiveLeafVoxels(srcTree, dstTree, [](float &d, float s){d=s;}); + + CPPUNIT_ASSERT_EQUAL(2, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(0, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(2.0f, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(3.0f, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + } + {//add float tree + openvdb::FloatTree srcTree(0.0f), dstTree(0.0f); + + dstTree.setValue(openvdb::Coord(1,1,1), 1.0f); + srcTree.setValue(openvdb::Coord(1,1,1), 2.0f); + srcTree.setValue(openvdb::Coord(8,8,8), 3.0f); + + CPPUNIT_ASSERT_EQUAL(1, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(2, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(1.0f, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(0.0f, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(!dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + + openvdb::tools::compActiveLeafVoxels(srcTree, dstTree, [](float &d, float s){d+=s;}); + + CPPUNIT_ASSERT_EQUAL(2, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(0, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(3.0f, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(3.0f, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + } + { + using BufferT = openvdb::FloatTree::LeafNodeType::Buffer; + CPPUNIT_ASSERT((std::is_same::value)); + } + { + using BufferT = openvdb::Vec3fTree::LeafNodeType::Buffer; + CPPUNIT_ASSERT((std::is_same::value)); + } + { + using BufferT = openvdb::BoolTree::LeafNodeType::Buffer; + CPPUNIT_ASSERT(!(std::is_same::value)); + } + { + using BufferT = openvdb::MaskTree::LeafNodeType::Buffer; + CPPUNIT_ASSERT(!(std::is_same::value)); + } + {//replace bool tree + openvdb::BoolTree srcTree(false), dstTree(false); + + dstTree.setValue(openvdb::Coord(1,1,1), true); + srcTree.setValue(openvdb::Coord(1,1,1), false); + srcTree.setValue(openvdb::Coord(8,8,8), true); + //(9,8,8) is inactive but true so it should have no effect + srcTree.setValueOnly(openvdb::Coord(9,8,8), true); + + CPPUNIT_ASSERT_EQUAL(1, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(2, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(true, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(false, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(!dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT_EQUAL(true, srcTree.getValue(openvdb::Coord(9, 8, 8))); + CPPUNIT_ASSERT(!srcTree.isValueOn(openvdb::Coord(9, 8, 8))); + + using Word = openvdb::BoolTree::LeafNodeType::Buffer::WordType; + openvdb::tools::compActiveLeafVoxels(srcTree, dstTree, [](Word &d, Word s){d=s;}); + + CPPUNIT_ASSERT_EQUAL(2, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(0, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(false, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(true, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + } + {// mask tree + openvdb::MaskTree srcTree(false), dstTree(false); + + dstTree.setValueOn(openvdb::Coord(1,1,1)); + srcTree.setValueOn(openvdb::Coord(1,1,1)); + srcTree.setValueOn(openvdb::Coord(8,8,8)); + + CPPUNIT_ASSERT_EQUAL(1, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(2, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(true, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(false, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(!dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + + openvdb::tools::compActiveLeafVoxels(srcTree, dstTree); + + CPPUNIT_ASSERT_EQUAL(2, int(dstTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(0, int(srcTree.leafCount())); + CPPUNIT_ASSERT_EQUAL(true, dstTree.getValue(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(true, dstTree.getValue(openvdb::Coord(8, 8, 8))); + CPPUNIT_ASSERT(dstTree.isValueOn(openvdb::Coord(8, 8, 8))); + } +} + + +//////////////////////////////////////// + diff --git a/openvdb/unittest/TestTreeGetSetValues.cc b/openvdb/unittest/TestTreeGetSetValues.cc new file mode 100644 index 00000000..b9bf87af --- /dev/null +++ b/openvdb/unittest/TestTreeGetSetValues.cc @@ -0,0 +1,436 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include // for tools::setValueOnMin() et al. +#include + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + + +class TestTreeGetSetValues: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestTreeGetSetValues); + CPPUNIT_TEST(testGetValues); + CPPUNIT_TEST(testSetValues); + CPPUNIT_TEST(testUnsetValues); + CPPUNIT_TEST(testFill); + CPPUNIT_TEST(testSetActiveStates); + CPPUNIT_TEST(testHasActiveTiles); + CPPUNIT_TEST_SUITE_END(); + + void testGetBackground(); + void testGetValues(); + void testSetValues(); + void testUnsetValues(); + void testFill(); + void testSetActiveStates(); + void testHasActiveTiles(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestTreeGetSetValues); + +namespace { +typedef openvdb::tree::Tree4::Type Tree323f; // 8^3 x 4^3 x 8^3 +} + + +void +TestTreeGetSetValues::testGetBackground() +{ + const float background = 256.0f; + Tree323f tree(background); + + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.background()); +} + + +void +TestTreeGetSetValues::testGetValues() +{ + Tree323f tree(/*background=*/256.0f); + + tree.setValue(openvdb::Coord(0, 0, 0), 1.0); + tree.setValue(openvdb::Coord(1, 0, 0), 1.5); + tree.setValue(openvdb::Coord(0, 0, 8), 2.0); + tree.setValue(openvdb::Coord(1, 0, 8), 2.5); + tree.setValue(openvdb::Coord(0, 0, 16), 3.0); + tree.setValue(openvdb::Coord(1, 0, 16), 3.5); + tree.setValue(openvdb::Coord(0, 0, 24), 4.0); + tree.setValue(openvdb::Coord(1, 0, 24), 4.5); + + ASSERT_DOUBLES_EXACTLY_EQUAL(1.0, tree.getValue(openvdb::Coord(0, 0, 0))); + ASSERT_DOUBLES_EXACTLY_EQUAL(1.5, tree.getValue(openvdb::Coord(1, 0, 0))); + ASSERT_DOUBLES_EXACTLY_EQUAL(2.0, tree.getValue(openvdb::Coord(0, 0, 8))); + ASSERT_DOUBLES_EXACTLY_EQUAL(2.5, tree.getValue(openvdb::Coord(1, 0, 8))); + ASSERT_DOUBLES_EXACTLY_EQUAL(3.0, tree.getValue(openvdb::Coord(0, 0, 16))); + ASSERT_DOUBLES_EXACTLY_EQUAL(3.5, tree.getValue(openvdb::Coord(1, 0, 16))); + ASSERT_DOUBLES_EXACTLY_EQUAL(4.0, tree.getValue(openvdb::Coord(0, 0, 24))); + ASSERT_DOUBLES_EXACTLY_EQUAL(4.5, tree.getValue(openvdb::Coord(1, 0, 24))); +} + + +void +TestTreeGetSetValues::testSetValues() +{ + using namespace openvdb; + + const float background = 256.0; + Tree323f tree(background); + + for (int activeTile = 0; activeTile < 2; ++activeTile) { + if (activeTile) tree.fill(CoordBBox(Coord(0), Coord(31)), background, /*active=*/true); + + tree.setValue(openvdb::Coord(0, 0, 0), 1.0); + tree.setValue(openvdb::Coord(1, 0, 0), 1.5); + tree.setValue(openvdb::Coord(0, 0, 8), 2.0); + tree.setValue(openvdb::Coord(1, 0, 8), 2.5); + tree.setValue(openvdb::Coord(0, 0, 16), 3.0); + tree.setValue(openvdb::Coord(1, 0, 16), 3.5); + tree.setValue(openvdb::Coord(0, 0, 24), 4.0); + tree.setValue(openvdb::Coord(1, 0, 24), 4.5); + + const int expectedActiveCount = (!activeTile ? 8 : 32 * 32 * 32); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount, int(tree.activeVoxelCount())); + + float val = 1.f; + for (Tree323f::LeafCIter iter = tree.cbeginLeaf(); iter; ++iter) { + ASSERT_DOUBLES_EXACTLY_EQUAL(val, iter->getValue(openvdb::Coord(0, 0, 0))); + ASSERT_DOUBLES_EXACTLY_EQUAL(val+0.5, iter->getValue(openvdb::Coord(1, 0, 0))); + val = val + 1.f; + } + } +} + + +void +TestTreeGetSetValues::testUnsetValues() +{ + using namespace openvdb; + + const float background = 256.0; + Tree323f tree(background); + + for (int activeTile = 0; activeTile < 2; ++activeTile) { + if (activeTile) tree.fill(CoordBBox(Coord(0), Coord(31)), background, /*active=*/true); + + Coord setCoords[8] = { + Coord(0, 0, 0), + Coord(1, 0, 0), + Coord(0, 0, 8), + Coord(1, 0, 8), + Coord(0, 0, 16), + Coord(1, 0, 16), + Coord(0, 0, 24), + Coord(1, 0, 24) + }; + + for (int i = 0; i < 8; ++i) { + tree.setValue(setCoords[i], 1.0); + } + const int expectedActiveCount = (!activeTile ? 8 : 32 * 32 * 32); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount, int(tree.activeVoxelCount())); + + // Unset some voxels. + for (int i = 0; i < 8; i += 2) { + tree.setValueOff(setCoords[i]); + } + CPPUNIT_ASSERT_EQUAL(expectedActiveCount - 4, int(tree.activeVoxelCount())); + + // Unset some voxels, but change their values. + for (int i = 0; i < 8; i += 2) { + tree.setValueOff(setCoords[i], background); + } + CPPUNIT_ASSERT_EQUAL(expectedActiveCount - 4, int(tree.activeVoxelCount())); + for (int i = 0; i < 8; i += 2) { + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(setCoords[i])); + } + } +} + + +void +TestTreeGetSetValues::testFill() +{ + using openvdb::CoordBBox; + using openvdb::Coord; + + const float background = 256.0; + Tree323f tree(background); + + // Fill from (-2,-2,-2) to (2,2,2) with active value 2. + tree.fill(CoordBBox(Coord(-2), Coord(2)), 2.0); + Coord xyz, xyzMin = Coord::max(), xyzMax = Coord::min(); + for (Tree323f::ValueOnCIter iter = tree.cbeginValueOn(); iter; ++iter) { + xyz = iter.getCoord(); + xyzMin = std::min(xyzMin, xyz); + xyzMax = std::max(xyz, xyzMax); + ASSERT_DOUBLES_EXACTLY_EQUAL(2.0, *iter); + } + CPPUNIT_ASSERT_EQUAL(openvdb::Index64(5*5*5), tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Coord(-2), xyzMin); + CPPUNIT_ASSERT_EQUAL(Coord( 2), xyzMax); + + // Fill from (1,1,1) to (3,3,3) with active value 3. + tree.fill(CoordBBox(Coord(1), Coord(3)), 3.0); + xyzMin = Coord::max(); xyzMax = Coord::min(); + for (Tree323f::ValueOnCIter iter = tree.cbeginValueOn(); iter; ++iter) { + xyz = iter.getCoord(); + xyzMin = std::min(xyzMin, xyz); + xyzMax = std::max(xyz, xyzMax); + const float expectedValue = (xyz[0] >= 1 && xyz[1] >= 1 && xyz[2] >= 1 + && xyz[0] <= 3 && xyz[1] <= 3 && xyz[2] <= 3) ? 3.0 : 2.0; + ASSERT_DOUBLES_EXACTLY_EQUAL(expectedValue, *iter); + } + openvdb::Index64 expectedCount = + 5*5*5 // (-2,-2,-2) to (2,2,2) + + 3*3*3 // (1,1,1) to (3,3,3) + - 2*2*2; // (1,1,1) to (2,2,2) overlap + CPPUNIT_ASSERT_EQUAL(expectedCount, tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Coord(-2), xyzMin); + CPPUNIT_ASSERT_EQUAL(Coord( 3), xyzMax); + + // Fill from (10,10,10) to (20,20,20) with active value 10. + tree.fill(CoordBBox(Coord(10), Coord(20)), 10.0); + xyzMin = Coord::max(); xyzMax = Coord::min(); + for (Tree323f::ValueOnCIter iter = tree.cbeginValueOn(); iter; ++iter) { + xyz = iter.getCoord(); + xyzMin = std::min(xyzMin, xyz); + xyzMax = std::max(xyz, xyzMax); + float expectedValue = 2.0; + if (xyz[0] >= 1 && xyz[1] >= 1 && xyz[2] >= 1 + && xyz[0] <= 3 && xyz[1] <= 3 && xyz[2] <= 3) + { + expectedValue = 3.0; + } else if (xyz[0] >= 10 && xyz[1] >= 10 && xyz[2] >= 10 + && xyz[0] <= 20 && xyz[1] <= 20 && xyz[2] <= 20) + { + expectedValue = 10.0; + } + ASSERT_DOUBLES_EXACTLY_EQUAL(expectedValue, *iter); + } + expectedCount = + 5*5*5 // (-2,-2,-2) to (2,2,2) + + 3*3*3 // (1,1,1) to (3,3,3) + - 2*2*2 // (1,1,1) to (2,2,2) overlap + + 11*11*11; // (10,10,10) to (20,20,20) + CPPUNIT_ASSERT_EQUAL(expectedCount, tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Coord(-2), xyzMin); + CPPUNIT_ASSERT_EQUAL(Coord(20), xyzMax); + + // "Undo" previous fill from (10,10,10) to (20,20,20). + tree.fill(CoordBBox(Coord(10), Coord(20)), background, /*active=*/false); + xyzMin = Coord::max(); xyzMax = Coord::min(); + for (Tree323f::ValueOnCIter iter = tree.cbeginValueOn(); iter; ++iter) { + xyz = iter.getCoord(); + xyzMin = std::min(xyzMin, xyz); + xyzMax = std::max(xyz, xyzMax); + const float expectedValue = (xyz[0] >= 1 && xyz[1] >= 1 && xyz[2] >= 1 + && xyz[0] <= 3 && xyz[1] <= 3 && xyz[2] <= 3) ? 3.0 : 2.0; + ASSERT_DOUBLES_EXACTLY_EQUAL(expectedValue, *iter); + } + expectedCount = + 5*5*5 // (-2,-2,-2) to (2,2,2) + + 3*3*3 // (1,1,1) to (3,3,3) + - 2*2*2; // (1,1,1) to (2,2,2) overlap + CPPUNIT_ASSERT_EQUAL(expectedCount, tree.activeVoxelCount()); + CPPUNIT_ASSERT_EQUAL(Coord(-2), xyzMin); + CPPUNIT_ASSERT_EQUAL(Coord( 3), xyzMax); + + // The following tests assume a [3,2,3] tree configuration. + + tree.clear(); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.nonLeafCount()); // root node + + // Partially fill a single leaf node. + tree.fill(CoordBBox(Coord(8), Coord(14)), 0.0); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree.nonLeafCount()); + + // Completely fill the leaf node, replacing it with a tile. + tree.fill(CoordBBox(Coord(8), Coord(15)), 0.0); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree.nonLeafCount()); + + { + const int activeVoxelCount = int(tree.activeVoxelCount()); + + // Fill a single voxel of the tile with a different (active) value. + tree.fill(CoordBBox(Coord(10), Coord(10)), 1.0); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree.nonLeafCount()); + CPPUNIT_ASSERT_EQUAL(activeVoxelCount, int(tree.activeVoxelCount())); + // Fill the voxel with an inactive value. + tree.fill(CoordBBox(Coord(10), Coord(10)), 1.0, /*active=*/false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree.nonLeafCount()); + CPPUNIT_ASSERT_EQUAL(activeVoxelCount - 1, int(tree.activeVoxelCount())); + + // Completely fill the leaf node, replacing it with a tile again. + tree.fill(CoordBBox(Coord(8), Coord(15)), 0.0); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree.nonLeafCount()); + } + + // Expand by one voxel, creating seven neighboring leaf nodes. + tree.fill(CoordBBox(Coord(8), Coord(16)), 0.0); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(7), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree.nonLeafCount()); + + // Completely fill the internal node containing the tile, replacing it with + // a tile at the next level of the tree. + tree.fill(CoordBBox(Coord(0), Coord(31)), 0.0); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(2), tree.nonLeafCount()); + + // Expand by one voxel, creating a layer of leaf nodes on three faces. + tree.fill(CoordBBox(Coord(0), Coord(32)), 0.0); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(5*5 + 4*5 + 4*4), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(2 + 7), tree.nonLeafCount()); // +7 internal nodes + + // Completely fill the second-level internal node, replacing it with a root-level tile. + tree.fill(CoordBBox(Coord(0), Coord(255)), 0.0); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.nonLeafCount()); + + // Repeat, filling with an inactive value. + + tree.clear(); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.nonLeafCount()); // root node + + // Partially fill a single leaf node. + tree.fill(CoordBBox(Coord(8), Coord(14)), 0.0, /*active=*/false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree.nonLeafCount()); + + // Completely fill the leaf node, replacing it with a tile. + tree.fill(CoordBBox(Coord(8), Coord(15)), 0.0, /*active=*/false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree.nonLeafCount()); + + // Expand by one voxel, creating seven neighboring leaf nodes. + tree.fill(CoordBBox(Coord(8), Coord(16)), 0.0, /*active=*/false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(7), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(3), tree.nonLeafCount()); + + // Completely fill the internal node containing the tile, replacing it with + // a tile at the next level of the tree. + tree.fill(CoordBBox(Coord(0), Coord(31)), 0.0, /*active=*/false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(2), tree.nonLeafCount()); + + // Expand by one voxel, creating a layer of leaf nodes on three faces. + tree.fill(CoordBBox(Coord(0), Coord(32)), 0.0, /*active=*/false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(5*5 + 4*5 + 4*4), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(2 + 7), tree.nonLeafCount()); // +7 internal nodes + + // Completely fill the second-level internal node, replacing it with a root-level tile. + tree.fill(CoordBBox(Coord(0), Coord(255)), 0.0, /*active=*/false); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.nonLeafCount()); + + tree.clear(); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.nonLeafCount()); // root node + CPPUNIT_ASSERT(tree.empty()); + + // Partially fill a region with inactive background values. + tree.fill(CoordBBox(Coord(27), Coord(254)), background, /*active=*/false); + // Confirm that after pruning, the tree is empty. + openvdb::tools::prune(tree); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.nonLeafCount()); // root node + CPPUNIT_ASSERT(tree.empty()); +} + + +// Verify that setting voxels inside active tiles works correctly. +// In particular, it should preserve the active states of surrounding voxels. +void +TestTreeGetSetValues::testSetActiveStates() +{ + using namespace openvdb; + + const float background = 256.0; + Tree323f tree(background); + + const Coord xyz(10); + const float val = 42.0; + const int expectedActiveCount = 32 * 32 * 32; + +#define RESET_TREE() \ + tree.fill(CoordBBox(Coord(0), Coord(31)), background, /*active=*/true) // create an active tile + + RESET_TREE(); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount, int(tree.activeVoxelCount())); + + tree.setValueOff(xyz); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount - 1, int(tree.activeVoxelCount())); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(xyz)); + + RESET_TREE(); + tree.setValueOn(xyz); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount, int(tree.activeVoxelCount())); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(xyz)); + + RESET_TREE(); + tree.setValueOff(xyz, val); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount - 1, int(tree.activeVoxelCount())); + ASSERT_DOUBLES_EXACTLY_EQUAL(val, tree.getValue(xyz)); + + RESET_TREE(); + tree.setActiveState(xyz, true); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount, int(tree.activeVoxelCount())); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(xyz)); + + RESET_TREE(); + tree.setActiveState(xyz, false); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount - 1, int(tree.activeVoxelCount())); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(xyz)); + + RESET_TREE(); + tree.setValueOn(xyz, val); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount, int(tree.activeVoxelCount())); + ASSERT_DOUBLES_EXACTLY_EQUAL(val, tree.getValue(xyz)); + + RESET_TREE(); + tools::setValueOnMin(tree, xyz, val); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount, int(tree.activeVoxelCount())); + ASSERT_DOUBLES_EXACTLY_EQUAL(std::min(val, background), tree.getValue(xyz)); + + RESET_TREE(); + tools::setValueOnMax(tree, xyz, val); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount, int(tree.activeVoxelCount())); + ASSERT_DOUBLES_EXACTLY_EQUAL(std::max(val, background), tree.getValue(xyz)); + + RESET_TREE(); + tools::setValueOnSum(tree, xyz, val); + CPPUNIT_ASSERT_EQUAL(expectedActiveCount, int(tree.activeVoxelCount())); + ASSERT_DOUBLES_EXACTLY_EQUAL(val + background, tree.getValue(xyz)); + +#undef RESET_TREE +} + + +void +TestTreeGetSetValues::testHasActiveTiles() +{ + Tree323f tree(/*background=*/256.0f); + + CPPUNIT_ASSERT(!tree.hasActiveTiles()); + + // Fill from (-2,-2,-2) to (2,2,2) with active value 2. + tree.fill(openvdb::CoordBBox(openvdb::Coord(-2), openvdb::Coord(2)), 2.0f); + CPPUNIT_ASSERT(!tree.hasActiveTiles()); + + // Fill from (-200,-200,-200) to (-4,-4,-4) with active value 3. + tree.fill(openvdb::CoordBBox(openvdb::Coord(-200), openvdb::Coord(-4)), 3.0f); + CPPUNIT_ASSERT(tree.hasActiveTiles()); +} diff --git a/openvdb/unittest/TestTreeIterators.cc b/openvdb/unittest/TestTreeIterators.cc new file mode 100644 index 00000000..6fd1e5a9 --- /dev/null +++ b/openvdb/unittest/TestTreeIterators.cc @@ -0,0 +1,582 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include // for tools::createLevelSetSphere() + + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0) + + +class TestTreeIterators: public CppUnit::TestCase +{ +public: + virtual void setUp() { openvdb::initialize(); } + virtual void tearDown() { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestTreeIterators); + CPPUNIT_TEST(testLeafIterator); + CPPUNIT_TEST(testEmptyLeafIterator); + CPPUNIT_TEST(testOnlyNegative); + CPPUNIT_TEST(testValueAllIterator); + CPPUNIT_TEST(testValueOnIterator); + CPPUNIT_TEST(testValueOffIterator); + CPPUNIT_TEST(testModifyValue); + CPPUNIT_TEST(testDepthBounds); + CPPUNIT_TEST_SUITE_END(); + + void testLeafIterator(); + void testEmptyLeafIterator(); + void testOnlyNegative(); + void testValueAllIterator(); + void testValueOnIterator(); + void testValueOffIterator(); + void testModifyValue(); + void testDepthBounds(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestTreeIterators); + + +typedef openvdb::FloatTree TreeType; + + +void +TestTreeIterators::testLeafIterator() +{ + const float fillValue = 256.0f; + + TreeType tree(fillValue); + + tree.setValue(openvdb::Coord(0, 0, 0), 1.0); + tree.setValue(openvdb::Coord(1, 0, 0), 1.5); + tree.setValue(openvdb::Coord(0, 0, 8), 2.0); + tree.setValue(openvdb::Coord(1, 0, 8), 2.5); + tree.setValue(openvdb::Coord(0, 0, 16), 3.0); + tree.setValue(openvdb::Coord(1, 0, 16), 3.5); + tree.setValue(openvdb::Coord(0, 0, 24), 4.0); + tree.setValue(openvdb::Coord(1, 0, 24), 4.5); + + float val = 1.f; + for (TreeType::LeafCIter iter = tree.cbeginLeaf(); iter; ++iter) { + const TreeType::LeafNodeType* leaf = iter.getLeaf(); + CPPUNIT_ASSERT(leaf != NULL); + ASSERT_DOUBLES_EXACTLY_EQUAL(val, leaf->getValue(openvdb::Coord(0, 0, 0))); + ASSERT_DOUBLES_EXACTLY_EQUAL(val + 0.5, iter->getValue(openvdb::Coord(1, 0, 0))); + ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue, iter->getValue(openvdb::Coord(1, 1, 1))); + val = val + 1.f; + } +} + + +// Test the leaf iterator over a tree without any leaf nodes. +void +TestTreeIterators::testEmptyLeafIterator() +{ + using namespace openvdb; + + TreeType tree(/*fillValue=*/256.0); + + std::vector dims; + tree.getNodeLog2Dims(dims); + CPPUNIT_ASSERT_EQUAL(4, int(dims.size())); + + // Start with an iterator over an empty tree. + TreeType::LeafCIter iter = tree.cbeginLeaf(); + CPPUNIT_ASSERT(!iter); + + // Using sparse fill, add internal nodes but no leaf nodes to the tree. + + // Fill the region subsumed by a level-2 internal node (assuming a four-level tree). + Index log2Sum = dims[1] + dims[2] + dims[3]; + CoordBBox bbox(Coord(0), Coord((1 << log2Sum) - 1)); + tree.fill(bbox, /*value=*/1.0); + iter = tree.cbeginLeaf(); + CPPUNIT_ASSERT(!iter); + + // Fill the region subsumed by a level-1 internal node. + log2Sum = dims[2] + dims[3]; + bbox.reset(Coord(0), Coord((1 << log2Sum) - 1)); + tree.fill(bbox, /*value=*/2.0); + iter = tree.cbeginLeaf(); + CPPUNIT_ASSERT(!iter); +} + + +void +TestTreeIterators::testOnlyNegative() +{ + using openvdb::Index64; + + const float fillValue = 5.0f; + + TreeType tree(fillValue); + + CPPUNIT_ASSERT(tree.empty()); + ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue, tree.getValue(openvdb::Coord(5, -10, 20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue, tree.getValue(openvdb::Coord(-500, 200, 300))); + + tree.setValue(openvdb::Coord(-5, 10, 20), 0.1f); + tree.setValue(openvdb::Coord( 5, -10, 20), 0.2f); + tree.setValue(openvdb::Coord( 5, 10, -20), 0.3f); + tree.setValue(openvdb::Coord(-5, -10, 20), 0.4f); + tree.setValue(openvdb::Coord(-5, 10, -20), 0.5f); + tree.setValue(openvdb::Coord( 5, -10, -20), 0.6f); + tree.setValue(openvdb::Coord(-5, -10, -20), 0.7f); + tree.setValue(openvdb::Coord(-500, 200, -300), 4.5678f); + tree.setValue(openvdb::Coord( 500, -200, -300), 4.5678f); + tree.setValue(openvdb::Coord(-500, -200, 300), 4.5678f); + + ASSERT_DOUBLES_EXACTLY_EQUAL(0.1f, tree.getValue(openvdb::Coord(-5, 10, 20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.2f, tree.getValue(openvdb::Coord( 5, -10, 20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.3f, tree.getValue(openvdb::Coord( 5, 10, -20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.4f, tree.getValue(openvdb::Coord(-5, -10, 20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.5f, tree.getValue(openvdb::Coord(-5, 10, -20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.6f, tree.getValue(openvdb::Coord( 5, -10, -20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.7f, tree.getValue(openvdb::Coord(-5, -10, -20))); + ASSERT_DOUBLES_EXACTLY_EQUAL(4.5678f, tree.getValue(openvdb::Coord(-500, 200, -300))); + ASSERT_DOUBLES_EXACTLY_EQUAL(4.5678f, tree.getValue(openvdb::Coord( 500, -200, -300))); + ASSERT_DOUBLES_EXACTLY_EQUAL(4.5678f, tree.getValue(openvdb::Coord(-500, -200, 300))); + + int count = 0; + for (int i = -25; i < 25; ++i) { + for (int j = -25; j < 25; ++j) { + for (int k = -25; k < 25; ++k) { + if (tree.getValue(openvdb::Coord(i, j, k)) < 1.0f) { + //fprintf(stderr, "(%i, %i, %i) = %f\n", + // i, j, k, tree.getValue(openvdb::Coord(i, j, k))); + ++count; + } + } + } + } + CPPUNIT_ASSERT_EQUAL(7, count); + + openvdb::Coord xyz; + int count2 = 0; + for (TreeType::ValueOnCIter iter = tree.cbeginValueOn();iter; ++iter) { + ++count2; + xyz = iter.getCoord(); + //std::cerr << xyz << " = " << *iter << "\n"; + } + CPPUNIT_ASSERT_EQUAL(10, count2); + CPPUNIT_ASSERT_EQUAL(Index64(10), tree.activeVoxelCount()); +} + + +void +TestTreeIterators::testValueAllIterator() +{ + const openvdb::Index DIM0 = 3, DIM1 = 2, DIM2 = 3; + + typedef openvdb::tree::Tree4::Type Tree323f; + + typedef Tree323f::RootNodeType RootT; + typedef RootT::ChildNodeType Int1T; + typedef Int1T::ChildNodeType Int2T; + typedef Int2T::ChildNodeType LeafT; + + Tree323f tree(/*fillValue=*/256.0f); + tree.setValue(openvdb::Coord(4), 0.0f); + tree.setValue(openvdb::Coord(-4), -1.0f); + + const size_t expectedNumOff = + 2 * ((1 << (3 * DIM2)) - 1) // 2 8x8x8 InternalNodes - 1 child pointer each + + 2 * ((1 << (3 * DIM1)) - 1) // 2 4x4x4 InternalNodes - 1 child pointer each + + 2 * ((1 << (3 * DIM0)) - 1); // 2 8x8x8 LeafNodes - 1 active value each + + { + Tree323f::ValueAllIter iter = tree.beginValueAll(); + CPPUNIT_ASSERT(iter.test()); + + // Read all tile and voxel values through a non-const value iterator. + size_t numOn = 0, numOff = 0; + for ( ; iter; ++iter) { + CPPUNIT_ASSERT(iter.getLevel() <= 3); + const openvdb::Index iterLevel = iter.getLevel(); + for (openvdb::Index lvl = 0; lvl <= 3; ++lvl) { + RootT* root; Int1T* int1; Int2T* int2; LeafT* leaf; + iter.getNode(root); CPPUNIT_ASSERT(root != NULL); + iter.getNode(int1); CPPUNIT_ASSERT(iterLevel < 3 ? int1 != NULL: int1 == NULL); + iter.getNode(int2); CPPUNIT_ASSERT(iterLevel < 2 ? int2 != NULL: int2 == NULL); + iter.getNode(leaf); CPPUNIT_ASSERT(iterLevel < 1 ? leaf != NULL: leaf == NULL); + } + + if (iter.isValueOn()) { + ++numOn; + const float f = iter.getValue(); + if (openvdb::math::isZero(f)) { + CPPUNIT_ASSERT(iter.getCoord() == openvdb::Coord(4)); + CPPUNIT_ASSERT(iter.isVoxelValue()); + } else { + ASSERT_DOUBLES_EXACTLY_EQUAL(-1.0f, f); + CPPUNIT_ASSERT(iter.getCoord() == openvdb::Coord(-4)); + CPPUNIT_ASSERT(iter.isVoxelValue()); + } + } else { + ++numOff; + + // For every tenth inactive value, check that the size of + // the tile or voxel is as expected. + if (numOff % 10 == 0) { + const int dim[4] = { + 1, 1 << DIM0, 1 << (DIM1 + DIM0), 1 << (DIM2 + DIM1 + DIM0) + }; + const int lvl = iter.getLevel(); + CPPUNIT_ASSERT(lvl < 4); + openvdb::CoordBBox bbox; + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL( + bbox.extents(), openvdb::Coord(dim[lvl], dim[lvl], dim[lvl])); + } + } + } + CPPUNIT_ASSERT_EQUAL(2, int(numOn)); + CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff); + } + { + Tree323f::ValueAllCIter iter = tree.cbeginValueAll(); + CPPUNIT_ASSERT(iter.test()); + + // Read all tile and voxel values through a const value iterator. + size_t numOn = 0, numOff = 0; + for ( ; iter.test(); iter.next()) { + if (iter.isValueOn()) ++numOn; else ++numOff; + } + CPPUNIT_ASSERT_EQUAL(2, int(numOn)); + CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff); + } + { + Tree323f::ValueAllIter iter = tree.beginValueAll(); + CPPUNIT_ASSERT(iter.test()); + + // Read all tile and voxel values through a non-const value iterator + // and overwrite all active values. + size_t numOn = 0, numOff = 0; + for ( ; iter; ++iter) { + if (iter.isValueOn()) { + iter.setValue(iter.getValue() - 5); + ++numOn; + } else { + ++numOff; + } + } + CPPUNIT_ASSERT_EQUAL(2, int(numOn)); + CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff); + } +} + + +void +TestTreeIterators::testValueOnIterator() +{ + typedef openvdb::tree::Tree4::Type Tree323f; + + Tree323f tree(/*fillValue=*/256.0f); + + { + Tree323f::ValueOnIter iter = tree.beginValueOn(); + CPPUNIT_ASSERT(!iter.test()); // empty tree + } + + const int STEP = 8/*100*/, NUM_STEPS = 10; + for (int i = 0; i < NUM_STEPS; ++i) { + tree.setValue(openvdb::Coord(STEP * i), 0.0f); + } + + { + Tree323f::ValueOnIter iter = tree.beginValueOn(); + CPPUNIT_ASSERT(iter.test()); + + // Read all active tile and voxel values through a non-const value iterator. + int numOn = 0; + for ( ; iter; ++iter) { + CPPUNIT_ASSERT(iter.isVoxelValue()); + CPPUNIT_ASSERT(iter.isValueOn()); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.0f, iter.getValue()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(STEP * numOn), iter.getCoord()); + ++numOn; + } + CPPUNIT_ASSERT_EQUAL(NUM_STEPS, numOn); + } + { + Tree323f::ValueOnCIter iter = tree.cbeginValueOn(); + CPPUNIT_ASSERT(iter.test()); + + // Read all active tile and voxel values through a const value iterator. + int numOn = 0; + for ( ; iter.test(); iter.next()) { + CPPUNIT_ASSERT(iter.isVoxelValue()); + CPPUNIT_ASSERT(iter.isValueOn()); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.0f, iter.getValue()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(STEP * numOn), iter.getCoord()); + ++numOn; + } + CPPUNIT_ASSERT_EQUAL(NUM_STEPS, numOn); + } + { + Tree323f::ValueOnIter iter = tree.beginValueOn(); + CPPUNIT_ASSERT(iter.test()); + + // Read all active tile and voxel values through a non-const value iterator + // and overwrite the values. + int numOn = 0; + for ( ; iter; ++iter) { + CPPUNIT_ASSERT(iter.isVoxelValue()); + CPPUNIT_ASSERT(iter.isValueOn()); + ASSERT_DOUBLES_EXACTLY_EQUAL(0.0f, iter.getValue()); + iter.setValue(5.0f); + ASSERT_DOUBLES_EXACTLY_EQUAL(5.0f, iter.getValue()); + CPPUNIT_ASSERT_EQUAL(openvdb::Coord(STEP * numOn), iter.getCoord()); + ++numOn; + } + CPPUNIT_ASSERT_EQUAL(NUM_STEPS, numOn); + } +} + + +void +TestTreeIterators::testValueOffIterator() +{ + const openvdb::Index DIM0 = 3, DIM1 = 2, DIM2 = 3; + + typedef openvdb::tree::Tree4::Type Tree323f; + + Tree323f tree(/*fillValue=*/256.0f); + tree.setValue(openvdb::Coord(4), 0.0f); + tree.setValue(openvdb::Coord(-4), -1.0f); + + const size_t expectedNumOff = + 2 * ((1 << (3 * DIM2)) - 1) // 2 8x8x8 InternalNodes - 1 child pointer each + + 2 * ((1 << (3 * DIM1)) - 1) // 2 4x4x4 InternalNodes - 1 child pointer each + + 2 * ((1 << (3 * DIM0)) - 1); // 2 8x8x8 LeafNodes - 1 active value each + + { + Tree323f::ValueOffIter iter = tree.beginValueOff(); + CPPUNIT_ASSERT(iter.test()); + + // Read all inactive tile and voxel values through a non-const value iterator. + size_t numOff = 0; + for ( ; iter; ++iter) { + CPPUNIT_ASSERT(!iter.isValueOn()); + ++numOff; + // For every tenth inactive value, check that the size of + // the tile or voxel is as expected. + if (numOff % 10 == 0) { + const int dim[4] = { + 1, 1 << DIM0, 1 << (DIM1 + DIM0), 1 << (DIM2 + DIM1 + DIM0) + }; + const int lvl = iter.getLevel(); + CPPUNIT_ASSERT(lvl < 4); + openvdb::CoordBBox bbox; + iter.getBoundingBox(bbox); + CPPUNIT_ASSERT_EQUAL(bbox.extents(), openvdb::Coord(dim[lvl], dim[lvl], dim[lvl])); + } + } + CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff); + } + { + Tree323f::ValueOffCIter iter = tree.cbeginValueOff(); + CPPUNIT_ASSERT(iter.test()); + + // Read all inactive tile and voxel values through a const value iterator. + size_t numOff = 0; + for ( ; iter.test(); iter.next(), ++numOff) { + CPPUNIT_ASSERT(!iter.isValueOn()); + } + CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff); + } + { + Tree323f::ValueOffIter iter = tree.beginValueOff(); + CPPUNIT_ASSERT(iter.test()); + + // Read all inactive tile and voxel values through a non-const value iterator + // and overwrite the values. + size_t numOff = 0; + for ( ; iter; ++iter, ++numOff) { + iter.setValue(iter.getValue() - 5); + iter.setValueOff(); + } + for (iter = tree.beginValueOff(); iter; ++iter, --numOff); + CPPUNIT_ASSERT_EQUAL(size_t(0), numOff); + } +} + + +void +TestTreeIterators::testModifyValue() +{ + using openvdb::Coord; + + const openvdb::Index DIM0 = 3, DIM1 = 2, DIM2 = 3; + { + typedef openvdb::tree::Tree4::Type IntTree323f; + + IntTree323f tree(/*background=*/256); + tree.addTile(/*level=*/3, Coord(-1), /*value=*/ 4, /*active=*/true); + tree.addTile(/*level=*/2, Coord(1 << (DIM0 + DIM1)), /*value=*/-3, /*active=*/true); + tree.addTile(/*level=*/1, Coord(1 << DIM0), /*value=*/ 2, /*active=*/true); + tree.addTile(/*level=*/0, Coord(0), /*value=*/-1, /*active=*/true); + + struct Local { static inline void negate(int32_t& n) { n = -n; } }; + + for (IntTree323f::ValueAllIter iter = tree.beginValueAll(); iter; ++iter) { + iter.modifyValue(Local::negate); + } + + for (IntTree323f::ValueAllCIter iter = tree.cbeginValueAll(); iter; ++iter) { + const int32_t val = *iter; + if (val < 0) CPPUNIT_ASSERT((-val) % 2 == 0); // negative values are even + else CPPUNIT_ASSERT(val % 2 == 1); // positive values are odd + } + + // Because modifying values through a const iterator is not allowed, + // uncommenting the following line should result in a static assertion failure: + //tree.cbeginValueOn().modifyValue(Local::negate); + } + { + typedef openvdb::tree::Tree4::Type BoolTree323f; + + BoolTree323f tree; + tree.addTile(/*level=*/3, Coord(-1), /*value=*/false, /*active=*/true); + tree.addTile(/*level=*/2, Coord(1 << (DIM0 + DIM1)), /*value=*/ true, /*active=*/true); + tree.addTile(/*level=*/1, Coord(1 << DIM0), /*value=*/false, /*active=*/true); + tree.addTile(/*level=*/0, Coord(0), /*value=*/ true, /*active=*/true); + + struct Local { static inline void negate(bool& b) { b = !b; } }; + + for (BoolTree323f::ValueAllIter iter = tree.beginValueAll(); iter; ++iter) { + iter.modifyValue(Local::negate); + } + + CPPUNIT_ASSERT(!tree.getValue(Coord(0))); + CPPUNIT_ASSERT( tree.getValue(Coord(1 << DIM0))); + CPPUNIT_ASSERT(!tree.getValue(Coord(1 << (DIM0 + DIM1)))); + CPPUNIT_ASSERT( tree.getValue(Coord(-1))); + + // Because modifying values through a const iterator is not allowed, + // uncommenting the following line should result in a static assertion failure: + //tree.cbeginValueOn().modifyValue(Local::negate); + } + { + typedef openvdb::tree::Tree4::Type StringTree323f; + + StringTree323f tree(/*background=*/""); + tree.addTile(/*level=*/3, Coord(-1), /*value=*/"abc", /*active=*/true); + tree.addTile(/*level=*/2, Coord(1 << (DIM0 + DIM1)), /*value=*/"abc", /*active=*/true); + tree.addTile(/*level=*/1, Coord(1 << DIM0), /*value=*/"abc", /*active=*/true); + tree.addTile(/*level=*/0, Coord(0), /*value=*/"abc", /*active=*/true); + + struct Local { static inline void op(std::string& s) { s.append("def"); } }; + + for (StringTree323f::ValueOnIter iter = tree.beginValueOn(); iter; ++iter) { + iter.modifyValue(Local::op); + } + + const std::string expectedVal("abcdef"); + for (StringTree323f::ValueOnCIter iter = tree.cbeginValueOn(); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL(expectedVal, *iter); + } + for (StringTree323f::ValueOffCIter iter = tree.cbeginValueOff(); iter; ++iter) { + CPPUNIT_ASSERT((*iter).empty()); + } + } +} + + +void +TestTreeIterators::testDepthBounds() +{ + const openvdb::Index DIM0 = 3, DIM1 = 2, DIM2 = 3; + + typedef openvdb::tree::Tree4::Type Tree323f; + + Tree323f tree(/*fillValue=*/256.0f); + tree.setValue(openvdb::Coord(4), 0.0f); + tree.setValue(openvdb::Coord(-4), -1.0f); + + const size_t + numDepth1 = 2 * ((1 << (3 * DIM2)) - 1), // 2 8x8x8 InternalNodes - 1 child pointer each + numDepth2 = 2 * ((1 << (3 * DIM1)) - 1), // 2 4x4x4 InternalNodes - 1 child pointer each + numDepth3 = 2 * ((1 << (3 * DIM0)) - 1), // 2 8x8x8 LeafNodes - 1 active value each + expectedNumOff = numDepth1 + numDepth2 + numDepth3; + + { + Tree323f::ValueOffCIter iter = tree.cbeginValueOff(); + CPPUNIT_ASSERT(iter.test()); + + // Read all inactive tile and voxel values through a non-const value iterator. + size_t numOff = 0; + for ( ; iter; ++iter) { + CPPUNIT_ASSERT(!iter.isValueOn()); + ++numOff; + } + CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff); + } + { + // Repeat, setting the minimum iterator depth to 2. + Tree323f::ValueOffCIter iter = tree.cbeginValueOff(); + CPPUNIT_ASSERT(iter.test()); + + iter.setMinDepth(2); + CPPUNIT_ASSERT(iter.test()); + + size_t numOff = 0; + for ( ; iter; ++iter) { + CPPUNIT_ASSERT(!iter.isValueOn()); + ++numOff; + const int depth = iter.getDepth(); + CPPUNIT_ASSERT(depth > 1); + } + CPPUNIT_ASSERT_EQUAL(expectedNumOff - numDepth1, numOff); + } + { + // Repeat, setting the minimum and maximum depths to 2. + Tree323f::ValueOffCIter iter = tree.cbeginValueOff(); + CPPUNIT_ASSERT(iter.test()); + + iter.setMinDepth(2); + CPPUNIT_ASSERT(iter.test()); + + iter.setMaxDepth(2); + CPPUNIT_ASSERT(iter.test()); + + size_t numOff = 0; + for ( ; iter; ++iter) { + CPPUNIT_ASSERT(!iter.isValueOn()); + ++numOff; + const int depth = iter.getDepth(); + CPPUNIT_ASSERT_EQUAL(2, depth); + } + CPPUNIT_ASSERT_EQUAL(expectedNumOff - numDepth1 - numDepth3, numOff); + } + { + // FX-7884 regression test + using namespace openvdb; + + const float radius = 4.3f, voxelSize = 0.1f, width = 2.0f; + const Vec3f center(15.8f, 13.2f, 16.7f); + FloatGrid::Ptr sphereGrid = tools::createLevelSetSphere( + radius, center, voxelSize, width); + const FloatTree& sphereTree = sphereGrid->tree(); + + FloatGrid::ValueOffIter iter = sphereGrid->beginValueOff(); + iter.setMaxDepth(2); + for ( ; iter; ++iter) { + const Coord ijk = iter.getCoord(); + ASSERT_DOUBLES_EXACTLY_EQUAL(sphereTree.getValue(ijk), *iter); + } + } + { + // FX-10221 regression test + // This code generated an infinite loop in OpenVDB 5.1.0 and earlier: + openvdb::FloatTree emptyTree; + auto iter = emptyTree.cbeginValueAll(); + iter.setMinDepth(2); + CPPUNIT_ASSERT(!iter); + } +} diff --git a/openvdb/unittest/TestTreeVisitor.cc b/openvdb/unittest/TestTreeVisitor.cc new file mode 100644 index 00000000..b9a8af27 --- /dev/null +++ b/openvdb/unittest/TestTreeVisitor.cc @@ -0,0 +1,344 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file TestTreeVisitor.h +/// +/// @author Peter Cucka + +#include +#include +#include +#include +#include +#include +#include + + +class TestTreeVisitor: public CppUnit::TestCase +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestTreeVisitor); + CPPUNIT_TEST(testVisitTreeBool); + CPPUNIT_TEST(testVisitTreeInt32); + CPPUNIT_TEST(testVisitTreeFloat); + CPPUNIT_TEST(testVisitTreeVec2I); + CPPUNIT_TEST(testVisitTreeVec3S); + CPPUNIT_TEST(testVisit2Trees); + CPPUNIT_TEST_SUITE_END(); + + void testVisitTreeBool() { visitTree(); } + void testVisitTreeInt32() { visitTree(); } + void testVisitTreeFloat() { visitTree(); } + void testVisitTreeVec2I() { visitTree(); } + void testVisitTreeVec3S() { visitTree(); } + void testVisit2Trees(); + +private: + template TreeT createTestTree() const; + template void visitTree(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestTreeVisitor); + + +//////////////////////////////////////// + + +template +TreeT +TestTreeVisitor::createTestTree() const +{ + using ValueT = typename TreeT::ValueType; + const ValueT zero = openvdb::zeroVal(), one = zero + 1; + + // Create a sparse test tree comprising the eight corners of + // a 200 x 200 x 200 cube. + TreeT tree(/*background=*/one); + tree.setValue(openvdb::Coord( 0, 0, 0), /*value=*/zero); + tree.setValue(openvdb::Coord(200, 0, 0), zero); + tree.setValue(openvdb::Coord( 0, 200, 0), zero); + tree.setValue(openvdb::Coord( 0, 0, 200), zero); + tree.setValue(openvdb::Coord(200, 0, 200), zero); + tree.setValue(openvdb::Coord( 0, 200, 200), zero); + tree.setValue(openvdb::Coord(200, 200, 0), zero); + tree.setValue(openvdb::Coord(200, 200, 200), zero); + + // Verify that the bounding box of all On values is 200 x 200 x 200. + openvdb::CoordBBox bbox; + CPPUNIT_ASSERT(tree.evalActiveVoxelBoundingBox(bbox)); + CPPUNIT_ASSERT(bbox.min() == openvdb::Coord(0, 0, 0)); + CPPUNIT_ASSERT(bbox.max() == openvdb::Coord(200, 200, 200)); + + return tree; +} + + +//////////////////////////////////////// + + +namespace { + +/// Single-tree visitor that accumulates node counts +class Visitor +{ +public: + using NodeMap = std::map >; + + Visitor(): mSkipLeafNodes(false) { reset(); } + + void reset() + { + mSkipLeafNodes = false; + mNodes.clear(); + mNonConstIterUseCount = mConstIterUseCount = 0; + } + + void setSkipLeafNodes(bool b) { mSkipLeafNodes = b; } + + template + bool operator()(IterT& iter) + { + incrementIterUseCount(std::is_const::value); + CPPUNIT_ASSERT(iter.getParentNode() != nullptr); + + if (mSkipLeafNodes && iter.parent().getLevel() == 1) return true; + + using ValueT = typename IterT::NonConstValueType; + using ChildT = typename IterT::ChildNodeType; + ValueT value; + if (const ChildT* child = iter.probeChild(value)) { + insertChild(child); + } + return false; + } + + openvdb::Index leafCount() const + { + NodeMap::const_iterator it = mNodes.find(0); + return openvdb::Index((it != mNodes.end()) ? it->second.size() : 0); + } + openvdb::Index nonLeafCount() const + { + openvdb::Index count = 1; // root node + for (NodeMap::const_iterator i = mNodes.begin(), e = mNodes.end(); i != e; ++i) { + if (i->first != 0) count = openvdb::Index(count + i->second.size()); + } + return count; + } + + bool usedOnlyConstIterators() const + { + return (mConstIterUseCount > 0 && mNonConstIterUseCount == 0); + } + bool usedOnlyNonConstIterators() const + { + return (mConstIterUseCount == 0 && mNonConstIterUseCount > 0); + } + +private: + template + void insertChild(const ChildT* child) + { + if (child != nullptr) { + const openvdb::Index level = child->getLevel(); + if (!mSkipLeafNodes || level > 0) { + mNodes[level].insert(child); + } + } + } + + void incrementIterUseCount(bool isConst) + { + if (isConst) ++mConstIterUseCount; else ++mNonConstIterUseCount; + } + + bool mSkipLeafNodes; + NodeMap mNodes; + int mNonConstIterUseCount, mConstIterUseCount; +}; + +/// Specialization for LeafNode iterators, whose ChildNodeType is void +/// (therefore can't call child->getLevel()) +template<> inline void Visitor::insertChild(const void*) {} + +} // unnamed namespace + + +template +void +TestTreeVisitor::visitTree() +{ + TreeT tree = createTestTree(); + { + // Traverse the tree, accumulating node counts. + Visitor visitor; + const_cast(tree).visit(visitor); + + CPPUNIT_ASSERT(visitor.usedOnlyConstIterators()); + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), visitor.leafCount()); + CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.nonLeafCount()); + } + { + // Traverse the tree, accumulating node counts as above, + // but using non-const iterators. + Visitor visitor; + tree.visit(visitor); + + CPPUNIT_ASSERT(visitor.usedOnlyNonConstIterators()); + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), visitor.leafCount()); + CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.nonLeafCount()); + } + { + // Traverse the tree, accumulating counts of non-leaf nodes only. + Visitor visitor; + visitor.setSkipLeafNodes(true); + const_cast(tree).visit(visitor); + + CPPUNIT_ASSERT(visitor.usedOnlyConstIterators()); + CPPUNIT_ASSERT_EQUAL(0U, visitor.leafCount()); // leaf nodes were skipped + CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.nonLeafCount()); + } +} + + +//////////////////////////////////////// + + +namespace { + +/// Two-tree visitor that accumulates node counts +class Visitor2 +{ +public: + using NodeMap = std::map >; + + Visitor2() { reset(); } + + void reset() + { + mSkipALeafNodes = mSkipBLeafNodes = false; + mANodeCount.clear(); + mBNodeCount.clear(); + } + + void setSkipALeafNodes(bool b) { mSkipALeafNodes = b; } + void setSkipBLeafNodes(bool b) { mSkipBLeafNodes = b; } + + openvdb::Index aLeafCount() const { return leafCount(/*useA=*/true); } + openvdb::Index bLeafCount() const { return leafCount(/*useA=*/false); } + openvdb::Index aNonLeafCount() const { return nonLeafCount(/*useA=*/true); } + openvdb::Index bNonLeafCount() const { return nonLeafCount(/*useA=*/false); } + + template + int operator()(AIterT& aIter, BIterT& bIter) + { + CPPUNIT_ASSERT(aIter.getParentNode() != nullptr); + CPPUNIT_ASSERT(bIter.getParentNode() != nullptr); + + typename AIterT::NodeType& aNode = aIter.parent(); + typename BIterT::NodeType& bNode = bIter.parent(); + + const openvdb::Index aLevel = aNode.getLevel(), bLevel = bNode.getLevel(); + mANodeCount[aLevel].insert(&aNode); + mBNodeCount[bLevel].insert(&bNode); + + int skipBranch = 0; + if (aLevel == 1 && mSkipALeafNodes) skipBranch = (skipBranch | 1); + if (bLevel == 1 && mSkipBLeafNodes) skipBranch = (skipBranch | 2); + return skipBranch; + } + +private: + openvdb::Index leafCount(bool useA) const + { + const NodeMap& theMap = (useA ? mANodeCount : mBNodeCount); + NodeMap::const_iterator it = theMap.find(0); + if (it != theMap.end()) return openvdb::Index(it->second.size()); + return 0; + } + openvdb::Index nonLeafCount(bool useA) const + { + openvdb::Index count = 0; + const NodeMap& theMap = (useA ? mANodeCount : mBNodeCount); + for (NodeMap::const_iterator i = theMap.begin(), e = theMap.end(); i != e; ++i) { + if (i->first != 0) count = openvdb::Index(count + i->second.size()); + } + return count; + } + + bool mSkipALeafNodes, mSkipBLeafNodes; + NodeMap mANodeCount, mBNodeCount; +}; + +} // unnamed namespace + + +void +TestTreeVisitor::testVisit2Trees() +{ + using TreeT = openvdb::FloatTree; + using Tree2T = openvdb::VectorTree; + using ValueT = TreeT::ValueType; + + // Create a test tree. + TreeT tree = createTestTree(); + // Create another test tree of a different type but with the same topology. + Tree2T tree2 = createTestTree(); + + // Traverse both trees. + Visitor2 visitor; + tree.visit2(tree2, visitor); + + //CPPUNIT_ASSERT(visitor.usedOnlyConstIterators()); + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), visitor.aLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree2.leafCount(), visitor.bLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.aNonLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree2.nonLeafCount(), visitor.bNonLeafCount()); + + visitor.reset(); + + // Change the topology of the first tree. + tree.setValue(openvdb::Coord(-200, -200, -200), openvdb::zeroVal()); + + // Traverse both trees. + tree.visit2(tree2, visitor); + + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), visitor.aLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree2.leafCount(), visitor.bLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.aNonLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree2.nonLeafCount(), visitor.bNonLeafCount()); + + visitor.reset(); + + // Traverse the two trees in the opposite order. + tree2.visit2(tree, visitor); + + CPPUNIT_ASSERT_EQUAL(tree2.leafCount(), visitor.aLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), visitor.bLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree2.nonLeafCount(), visitor.aNonLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.bNonLeafCount()); + + // Repeat, skipping leaf nodes of tree2. + visitor.reset(); + visitor.setSkipALeafNodes(true); + tree2.visit2(tree, visitor); + + CPPUNIT_ASSERT_EQUAL(0U, visitor.aLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree.leafCount(), visitor.bLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree2.nonLeafCount(), visitor.aNonLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.bNonLeafCount()); + + // Repeat, skipping leaf nodes of tree. + visitor.reset(); + visitor.setSkipBLeafNodes(true); + tree2.visit2(tree, visitor); + + CPPUNIT_ASSERT_EQUAL(tree2.leafCount(), visitor.aLeafCount()); + CPPUNIT_ASSERT_EQUAL(0U, visitor.bLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree2.nonLeafCount(), visitor.aNonLeafCount()); + CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.bNonLeafCount()); +} diff --git a/openvdb/unittest/TestTypes.cc b/openvdb/unittest/TestTypes.cc new file mode 100644 index 00000000..f8bc271c --- /dev/null +++ b/openvdb/unittest/TestTypes.cc @@ -0,0 +1,468 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include // for std::ref() +#include + + +using namespace openvdb; + +class TestTypes: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestTypes); + CPPUNIT_TEST(testVecTraits); + CPPUNIT_TEST(testQuatTraits); + CPPUNIT_TEST(testMatTraits); + CPPUNIT_TEST(testValueTraits); + CPPUNIT_TEST(testTypeList); + CPPUNIT_TEST_SUITE_END(); + + void testVecTraits(); + void testQuatTraits(); + void testMatTraits(); + void testValueTraits(); + void testTypeList(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestTypes); + + +namespace { struct Dummy {}; } + + +void +TestTypes::testVecTraits() +{ + { // VecTraits - IsVec + + // standard types (Vec3s, etc) + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + + // some less common types (Vec3U16, etc) + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + CPPUNIT_ASSERT(VecTraits::IsVec); + + // some non-vector types + CPPUNIT_ASSERT(!VecTraits::IsVec); + CPPUNIT_ASSERT(!VecTraits::IsVec); + CPPUNIT_ASSERT(!VecTraits::IsVec); + CPPUNIT_ASSERT(!VecTraits::IsVec); + CPPUNIT_ASSERT(!VecTraits::IsVec); + CPPUNIT_ASSERT(!VecTraits::IsVec); + CPPUNIT_ASSERT(!VecTraits::IsVec); + CPPUNIT_ASSERT(!VecTraits::IsVec); + } + + { // VecTraits - Size + + // standard types (Vec3s, etc) + CPPUNIT_ASSERT(VecTraits::Size == 3); + CPPUNIT_ASSERT(VecTraits::Size == 3); + CPPUNIT_ASSERT(VecTraits::Size == 3); + CPPUNIT_ASSERT(VecTraits::Size == 2); + CPPUNIT_ASSERT(VecTraits::Size == 2); + CPPUNIT_ASSERT(VecTraits::Size == 2); + CPPUNIT_ASSERT(VecTraits::Size == 4); + CPPUNIT_ASSERT(VecTraits::Size == 4); + CPPUNIT_ASSERT(VecTraits::Size == 4); + + // some less common types (Vec3U16, etc) + CPPUNIT_ASSERT(VecTraits::Size == 2); + CPPUNIT_ASSERT(VecTraits::Size == 3); + CPPUNIT_ASSERT(VecTraits::Size == 4); + + // some non-vector types + CPPUNIT_ASSERT(VecTraits::Size == 1); + CPPUNIT_ASSERT(VecTraits::Size == 1); + CPPUNIT_ASSERT(VecTraits::Size == 1); + CPPUNIT_ASSERT(VecTraits::Size == 1); + CPPUNIT_ASSERT(VecTraits::Size == 1); + CPPUNIT_ASSERT(VecTraits::Size == 1); + CPPUNIT_ASSERT(VecTraits::Size == 1); + CPPUNIT_ASSERT(VecTraits::Size == 1); + } + + { // VecTraits - ElementType + + // standard types (Vec3s, etc) + CPPUNIT_ASSERT(bool(std::is_same::ElementType, float>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, int>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, int>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, float>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, int>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, float>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + + // some less common types (Vec3U16, etc) + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, uint16_t>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, half>::value)); + + // some non-vector types + CPPUNIT_ASSERT(bool(std::is_same::ElementType, int>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, bool>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Quats>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Mat4d>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, ValueMask>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Dummy>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Byte>::value)); + } +} + + +void +TestTypes::testQuatTraits() +{ + { // QuatTraits - IsQuat + + // standard types (Quats, etc) + CPPUNIT_ASSERT(QuatTraits::IsQuat); + CPPUNIT_ASSERT(QuatTraits::IsQuat); + + // some non-quaternion types + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + CPPUNIT_ASSERT(!QuatTraits::IsQuat); + } + + { // QuatTraits - Size + + // standard types (Quats, etc) + CPPUNIT_ASSERT(QuatTraits::Size == 4); + CPPUNIT_ASSERT(QuatTraits::Size == 4); + + // some non-quaternion types + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + CPPUNIT_ASSERT(QuatTraits::Size == 1); + } + + { // QuatTraits - ElementType + + // standard types (Quats, etc) + CPPUNIT_ASSERT(bool(std::is_same::ElementType, float>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + + // some non-matrix types + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Vec3s>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Vec4d>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Vec2i>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Vec3U16>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, int>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, bool>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Mat4s>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, ValueMask>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Dummy>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Byte>::value)); + } +} + + +void +TestTypes::testMatTraits() +{ + { // MatTraits - IsMat + + // standard types (Mat4d, etc) + CPPUNIT_ASSERT(MatTraits::IsMat); + CPPUNIT_ASSERT(MatTraits::IsMat); + CPPUNIT_ASSERT(MatTraits::IsMat); + CPPUNIT_ASSERT(MatTraits::IsMat); + + // some non-matrix types + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + CPPUNIT_ASSERT(!MatTraits::IsMat); + } + + { // MatTraits - Size + + // standard types (Mat4d, etc) + CPPUNIT_ASSERT(MatTraits::Size == 3); + CPPUNIT_ASSERT(MatTraits::Size == 3); + CPPUNIT_ASSERT(MatTraits::Size == 4); + CPPUNIT_ASSERT(MatTraits::Size == 4); + + // some non-matrix types + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + CPPUNIT_ASSERT(MatTraits::Size == 1); + } + + { // MatTraits - ElementType + + // standard types (Mat4d, etc) + CPPUNIT_ASSERT(bool(std::is_same::ElementType, float>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, float>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + + // some non-matrix types + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Vec3s>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Vec4d>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Vec2i>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Vec3U16>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, int>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, bool>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Quats>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, ValueMask>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Dummy>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Byte>::value)); + } +} + + +void +TestTypes::testValueTraits() +{ + { // ValueTraits - IsVec, IsQuat, IsMat, IsScalar + + // vector types + CPPUNIT_ASSERT(ValueTraits::IsVec); + CPPUNIT_ASSERT(!ValueTraits::IsQuat); + CPPUNIT_ASSERT(!ValueTraits::IsMat); + CPPUNIT_ASSERT(!ValueTraits::IsScalar); + CPPUNIT_ASSERT(ValueTraits::IsVec); + CPPUNIT_ASSERT(!ValueTraits::IsQuat); + CPPUNIT_ASSERT(!ValueTraits::IsMat); + CPPUNIT_ASSERT(!ValueTraits::IsScalar); + CPPUNIT_ASSERT(ValueTraits::IsVec); + CPPUNIT_ASSERT(!ValueTraits::IsQuat); + CPPUNIT_ASSERT(!ValueTraits::IsMat); + CPPUNIT_ASSERT(!ValueTraits::IsScalar); + + // quaternion types + CPPUNIT_ASSERT(!ValueTraits::IsVec); + CPPUNIT_ASSERT(ValueTraits::IsQuat); + CPPUNIT_ASSERT(!ValueTraits::IsMat); + CPPUNIT_ASSERT(!ValueTraits::IsScalar); + CPPUNIT_ASSERT(!ValueTraits::IsVec); + CPPUNIT_ASSERT(ValueTraits::IsQuat); + CPPUNIT_ASSERT(!ValueTraits::IsMat); + CPPUNIT_ASSERT(!ValueTraits::IsScalar); + + // matrix types + CPPUNIT_ASSERT(!ValueTraits::IsVec); + CPPUNIT_ASSERT(!ValueTraits::IsQuat); + CPPUNIT_ASSERT(ValueTraits::IsMat); + CPPUNIT_ASSERT(!ValueTraits::IsScalar); + CPPUNIT_ASSERT(!ValueTraits::IsVec); + CPPUNIT_ASSERT(!ValueTraits::IsQuat); + CPPUNIT_ASSERT(ValueTraits::IsMat); + CPPUNIT_ASSERT(!ValueTraits::IsScalar); + + // scalar types + CPPUNIT_ASSERT(!ValueTraits::IsVec); + CPPUNIT_ASSERT(!ValueTraits::IsQuat); + CPPUNIT_ASSERT(!ValueTraits::IsMat); + CPPUNIT_ASSERT(ValueTraits::IsScalar); + CPPUNIT_ASSERT(!ValueTraits::IsVec); + CPPUNIT_ASSERT(!ValueTraits::IsQuat); + CPPUNIT_ASSERT(!ValueTraits::IsMat); + CPPUNIT_ASSERT(ValueTraits::IsScalar); + CPPUNIT_ASSERT(!ValueTraits::IsVec); + CPPUNIT_ASSERT(!ValueTraits::IsQuat); + CPPUNIT_ASSERT(!ValueTraits::IsMat); + CPPUNIT_ASSERT(ValueTraits::IsScalar); + CPPUNIT_ASSERT(!ValueTraits::IsVec); + CPPUNIT_ASSERT(!ValueTraits::IsQuat); + CPPUNIT_ASSERT(!ValueTraits::IsMat); + CPPUNIT_ASSERT(ValueTraits::IsScalar); + } + + { // ValueTraits - Size + + // vector types + CPPUNIT_ASSERT(ValueTraits::Size == 3); + CPPUNIT_ASSERT(ValueTraits::Size == 4); + CPPUNIT_ASSERT(ValueTraits::Size == 3); + + // quaternion types + CPPUNIT_ASSERT(ValueTraits::Size == 4); + CPPUNIT_ASSERT(ValueTraits::Size == 4); + + // matrix types + CPPUNIT_ASSERT(ValueTraits::Size == 3); + CPPUNIT_ASSERT(ValueTraits::Size == 4); + + // scalar types + CPPUNIT_ASSERT(ValueTraits::Size == 1); + CPPUNIT_ASSERT(ValueTraits::Size == 1); + CPPUNIT_ASSERT(ValueTraits::Size == 1); + CPPUNIT_ASSERT(ValueTraits::Size == 1); + } + + { // ValueTraits - Elements + + // vector types + CPPUNIT_ASSERT(ValueTraits::Elements == 3); + CPPUNIT_ASSERT(ValueTraits::Elements == 4); + CPPUNIT_ASSERT(ValueTraits::Elements == 3); + + // quaternion types + CPPUNIT_ASSERT(ValueTraits::Elements == 4); + CPPUNIT_ASSERT(ValueTraits::Elements == 4); + + // matrix types + CPPUNIT_ASSERT(ValueTraits::Elements == 3*3); + CPPUNIT_ASSERT(ValueTraits::Elements == 4*4); + + // scalar types + CPPUNIT_ASSERT(ValueTraits::Elements == 1); + CPPUNIT_ASSERT(ValueTraits::Elements == 1); + CPPUNIT_ASSERT(ValueTraits::Elements == 1); + CPPUNIT_ASSERT(ValueTraits::Elements == 1); + } + + { // ValueTraits - ElementType + + // vector types + CPPUNIT_ASSERT(bool(std::is_same::ElementType, float>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, uint16_t>::value)); + + // quaternion types + CPPUNIT_ASSERT(bool(std::is_same::ElementType, float>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + + // matrix types + CPPUNIT_ASSERT(bool(std::is_same::ElementType, float>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + + // scalar types + CPPUNIT_ASSERT(bool(std::is_same::ElementType, double>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, bool>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, ValueMask>::value)); + CPPUNIT_ASSERT(bool(std::is_same::ElementType, Dummy>::value)); + } +} + + +//////////////////////////////////////// + + +namespace { + +template char typeCode() { return '.'; } +template<> char typeCode() { return 'b'; } +template<> char typeCode() { return 'c'; } +template<> char typeCode() { return 'd'; } +template<> char typeCode() { return 'f'; } +template<> char typeCode() { return 'i'; } +template<> char typeCode() { return 'l'; } + + +struct TypeCodeOp +{ + std::string codes; + template void operator()(const T&) { codes.push_back(typeCode()); } +}; + + +template +inline std::string +typeSetAsString() +{ + TypeCodeOp op; + TSet::foreach(std::ref(op)); + return op.codes; +} + +} // anonymous namespace + + +void +TestTypes::testTypeList() +{ + using T0 = TypeList<>; + CPPUNIT_ASSERT_EQUAL(std::string(), typeSetAsString()); + + using T1 = TypeList; + CPPUNIT_ASSERT_EQUAL(std::string("i"), typeSetAsString()); + + using T2 = TypeList; + CPPUNIT_ASSERT_EQUAL(std::string("f"), typeSetAsString()); + + using T3 = TypeList; + CPPUNIT_ASSERT_EQUAL(std::string("bd"), typeSetAsString()); + + using T4 = T1::Append; + CPPUNIT_ASSERT_EQUAL(std::string("if"), typeSetAsString()); + CPPUNIT_ASSERT_EQUAL(std::string("fi"), typeSetAsString>()); + + using T5 = T3::Append; + CPPUNIT_ASSERT_EQUAL(std::string("bdif"), typeSetAsString()); + + using T6 = T5::Append; + CPPUNIT_ASSERT_EQUAL(std::string("bdifbdif"), typeSetAsString()); + + using T7 = T5::Append; + CPPUNIT_ASSERT_EQUAL(std::string("bdifcl"), typeSetAsString()); + + using T8 = T5::Append::Append; + CPPUNIT_ASSERT_EQUAL(std::string("bdifcl"), typeSetAsString()); + + using T9 = T8::Remove>; + CPPUNIT_ASSERT_EQUAL(std::string("bdifcl"), typeSetAsString()); + + using T10 = T8::Remove; + CPPUNIT_ASSERT_EQUAL(std::string("bdifcl"), typeSetAsString()); + + using T11 = T8::Remove::Remove; + CPPUNIT_ASSERT_EQUAL(std::string("bdfl"), typeSetAsString()); + + using T12 = T8::Remove; + CPPUNIT_ASSERT_EQUAL(std::string("bdfl"), typeSetAsString()); + + using T13 = T8::Remove>; + CPPUNIT_ASSERT_EQUAL(std::string("bdfl"), typeSetAsString()); +} diff --git a/openvdb/unittest/TestUtil.cc b/openvdb/unittest/TestUtil.cc new file mode 100644 index 00000000..bb077a3b --- /dev/null +++ b/openvdb/unittest/TestUtil.cc @@ -0,0 +1,674 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +//#define BENCHMARK_PAGED_ARRAY + +// For benchmark comparisons +#ifdef BENCHMARK_PAGED_ARRAY +#include // for std::deque +#include // for std::vector +#include // for tbb::concurrent_vector +#endif + +class TestUtil: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestUtil); + CPPUNIT_TEST(testFormats); + CPPUNIT_TEST(testCpuTimer); + CPPUNIT_TEST(testPagedArray); + CPPUNIT_TEST(testPagedArrayPushBack); + CPPUNIT_TEST_SUITE_END(); + + void testCpuTimer(); + void testFormats(); + void testPagedArray(); + void testPagedArrayPushBack(); + + using RangeT = tbb::blocked_range; + + // Multi-threading ArrayT::push_back + template + struct ArrayPushBack { + ArrayPushBack(ArrayT& array) : mArray(&array) {} + void parallel(size_t size) {tbb::parallel_for(RangeT(size_t(0), size, mArray->pageSize()), *this);} + void serial(size_t size) { (*this)(RangeT(size_t(0), size)); } + void unsafe(size_t size) { for (size_t i=0; i!=size; ++i) mArray->push_back_unsafe(i); } + void operator()(const RangeT& r) const { + for (size_t i=r.begin(), n=r.end(); i!=n; ++i) mArray->push_back(i); + } + ArrayT* mArray; + }; + + // Multi-threading ArrayT::ValueBuffer::push_back + template + struct BufferPushBack { + BufferPushBack(ArrayT& array) : mBuffer(array) {} + void parallel(size_t size) { + tbb::parallel_for(RangeT(size_t(0), size, mBuffer.parent().pageSize()), *this); + } + void serial(size_t size) { (*this)(RangeT(size_t(0), size)); } + void operator()(const RangeT& r) const { + for (size_t i=r.begin(), n=r.end(); i!=n; ++i) mBuffer.push_back(i); + } + mutable typename ArrayT::ValueBuffer mBuffer;//local instance + }; + + // Thread Local Storage version of BufferPushBack + template + struct TLS_BufferPushBack { + using PoolT = tbb::enumerable_thread_specific; + TLS_BufferPushBack(ArrayT &array) : mArray(&array), mPool(nullptr) {} + void parallel(size_t size) { + typename ArrayT::ValueBuffer exemplar(*mArray);//dummy used for initialization + mPool = new PoolT(exemplar);//thread local storage pool of ValueBuffers + tbb::parallel_for(RangeT(size_t(0), size, mArray->pageSize()), *this); + for (auto i=mPool->begin(); i!=mPool->end(); ++i) i->flush(); + delete mPool; + } + void operator()(const RangeT& r) const { + typename PoolT::reference buffer = mPool->local(); + for (size_t i=r.begin(), n=r.end(); i!=n; ++i) buffer.push_back(i); + } + ArrayT *mArray; + PoolT *mPool; + }; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestUtil); + +void +TestUtil::testFormats() +{ + {// TODO: add unit tests for printBytes + } + {// TODO: add a unit tests for printNumber + } + {// test long format printTime + const int width = 4, precision = 1, verbose = 1; + const int days = 1; + const int hours = 3; + const int minutes = 59; + const int seconds = 12; + const double milliseconds = 347.6; + const double mseconds = milliseconds + (seconds + (minutes + (hours + days*24)*60)*60)*1000.0; + std::ostringstream ostr1, ostr2; + CPPUNIT_ASSERT_EQUAL(4, openvdb::util::printTime(ostr2, mseconds, "Completed in ", "", width, precision, verbose )); + ostr1 << std::setprecision(precision) << std::setiosflags(std::ios::fixed); + ostr1 << "Completed in " << days << " day, " << hours << " hours, " << minutes << " minutes, " + << seconds << " seconds and " << std::setw(width) << milliseconds << " milliseconds (" << mseconds << "ms)"; + //std::cerr << ostr2.str() << std::endl; + CPPUNIT_ASSERT_EQUAL(ostr1.str(), ostr2.str()); + } + {// test compact format printTime + const int width = 4, precision = 1, verbose = 0; + const int days = 1; + const int hours = 3; + const int minutes = 59; + const int seconds = 12; + const double milliseconds = 347.6; + const double mseconds = milliseconds + (seconds + (minutes + (hours + days*24)*60)*60)*1000.0; + std::ostringstream ostr1, ostr2; + CPPUNIT_ASSERT_EQUAL(4, openvdb::util::printTime(ostr2, mseconds, "Completed in ", "", width, precision, verbose )); + ostr1 << std::setprecision(precision) << std::setiosflags(std::ios::fixed); + ostr1 << "Completed in " << days << "d " << hours << "h " << minutes << "m " + << std::setw(width) << (seconds + milliseconds/1000.0) << "s"; + //std::cerr << ostr2.str() << std::endl; + CPPUNIT_ASSERT_EQUAL(ostr1.str(), ostr2.str()); + } +} + +void +TestUtil::testCpuTimer() +{ + const int expected = 159, tolerance = 20;//milliseconds + const tbb::tick_count::interval_t sec(expected/1000.0); + { + openvdb::util::CpuTimer timer; + tbb::this_tbb_thread::sleep(sec); + const int actual1 = static_cast(timer.milliseconds()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual1, tolerance); + tbb::this_tbb_thread::sleep(sec); + const int actual2 = static_cast(timer.milliseconds()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2*expected, actual2, tolerance); + } + { + openvdb::util::CpuTimer timer; + tbb::this_tbb_thread::sleep(sec); + auto t1 = timer.restart(); + tbb::this_tbb_thread::sleep(sec); + tbb::this_tbb_thread::sleep(sec); + auto t2 = timer.restart(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2*t1, t2, tolerance); + } +} + +void +TestUtil::testPagedArrayPushBack() +{ +#ifdef BENCHMARK_PAGED_ARRAY + const size_t problemSize = 256000; + openvdb::util::CpuTimer timer; + std::cerr << "\nProblem size for benchmark: " << problemSize << std::endl; +#else + const size_t problemSize = 256000; +#endif + {//parallel PagedArray::push_back + using ArrayT = openvdb::util::PagedArray; + ArrayT d; +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("3: Parallel PagedArray::push_back with default page size"); +#endif + {// for some reason this: + ArrayPushBack tmp(d); + tmp.parallel(problemSize); + }// is faster than: + //tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), + // [&d](const tbb::blocked_range &range){ + // for (size_t i=range.begin(); i!=range.end(); ++i) d.push_back(i);}); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + CPPUNIT_ASSERT_EQUAL(size_t(10), d.log2PageSize()); + CPPUNIT_ASSERT_EQUAL(size_t(1)<> log2PageSize + CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("parallel sort with a default page size"); +#endif + d.sort(); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + for (size_t i=0, n=d.size(); i> log2PageSize + CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + } +} + +void +TestUtil::testPagedArray() +{ +#ifdef BENCHMARK_PAGED_ARRAY + const size_t problemSize = 2560000; + openvdb::util::CpuTimer timer; + std::cerr << "\nProblem size for benchmark: " << problemSize << std::endl; +#else + const size_t problemSize = 256000; +#endif + + {//serial PagedArray::push_back (check return value) + openvdb::util::PagedArray d; + CPPUNIT_ASSERT(d.isEmpty()); + CPPUNIT_ASSERT_EQUAL(size_t(0), d.size()); + CPPUNIT_ASSERT_EQUAL(size_t(8), d.log2PageSize()); + CPPUNIT_ASSERT_EQUAL(size_t(1)<; + ArrayT d; +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("1: Serial PagedArray::push_back with default page size"); +#endif + {// for some reason this: + ArrayPushBack tmp(d); + tmp.serial(problemSize); + }// is faster than: + //for (size_t i=0; i> log2PageSize + CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + for (size_t i=0, n=d.size(); i; + ArrayT d; +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("2: Serial PagedArray::push_back_unsafe with default page size"); +#endif + {// for some reason this: + ArrayPushBack tmp(d); + tmp.unsafe(problemSize); + }// is faster than: + //openvdb::util::PagedArray d; + //for (size_t i=0; i; + ArrayT d; +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("3: Parallel PagedArray::push_back with default page size"); +#endif + //{// for some reason this: + // ArrayPushBack tmp(d); + // tmp.parallel(problemSize); + //}// is faster than: + tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), + [&d](const tbb::blocked_range &range){ + for (size_t i=range.begin(); i!=range.end(); ++i) d.push_back(i);}); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + CPPUNIT_ASSERT_EQUAL(size_t(10), d.log2PageSize()); + CPPUNIT_ASSERT_EQUAL(size_t(1)<> log2PageSize + CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("parallel sort with a default page size"); +#endif + d.sort(); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + for (size_t i=0, n=d.size(); i> log2PageSize + CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + + + } + {//parallel PagedArray::push_back with page size of only 8 + using ArrayT = openvdb::util::PagedArray; + ArrayT d; +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("4: Parallel PagedArray::push_back with page size of only 8"); +#endif + {// for some reason this: + ArrayPushBack tmp(d); + tmp.parallel(problemSize); + }// is faster than: + //tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), + // [&d](const tbb::blocked_range &range){ + // for (size_t i=range.begin(); i!=range.end(); ++i) d.push_back(i);}); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + CPPUNIT_ASSERT_EQUAL(size_t(3), d.log2PageSize()); + CPPUNIT_ASSERT_EQUAL(size_t(1)<> log2PageSize + CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("parallel sort with a page size of only 8"); +#endif + d.sort(); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + for (size_t i=0, n=d.size(); i> log2PageSize + CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + } +#ifdef BENCHMARK_PAGED_ARRAY + {//benchmark against a std::vector + timer.start("5: Serial std::vector::push_back"); + std::vector v; + for (size_t i=0; i d; + for (size_t i=0; i d2; + CPPUNIT_ASSERT_EQUAL(size_t(0), d2.size()); + d2.resize(1234); + CPPUNIT_ASSERT_EQUAL(size_t(1234), d2.size()); + } + {//benchmark against a tbb::concurrent_vector::push_back + timer.start("7: Serial tbb::concurrent_vector::push_back"); + tbb::concurrent_vector v; + for (size_t i=0; i; + tbb::parallel_for(tbb::blocked_range(0, problemSize, ArrayT::pageSize()), + [&v](const tbb::blocked_range &range){ + for (size_t i=range.begin(); i!=range.end(); ++i) v.push_back(i);}); + timer.stop(); + tbb::parallel_sort(v.begin(), v.end()); + for (size_t i=0; i; + ArrayT d; + + CPPUNIT_ASSERT_EQUAL(size_t(0), d.size()); + d.resize(problemSize); + CPPUNIT_ASSERT_EQUAL(problemSize, d.size()); + CPPUNIT_ASSERT_EQUAL(size_t(1)<> log2PageSize + CPPUNIT_ASSERT_EQUAL((problemSize-1)>>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + + d.clear(); + CPPUNIT_ASSERT_EQUAL(size_t(0), d.size()); +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("9: Serial PagedArray::ValueBuffer::push_back"); +#endif + {// for some reason this: + BufferPushBack tmp(d); + tmp.serial(problemSize); + // is faster than: + //ArrayT::ValueBuffer buffer(d); + //for (size_t i=0, n=problemSize; i>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + + + } + {//parallel PagedArray::ValueBuffer::push_back + using ArrayT = openvdb::util::PagedArray; + ArrayT d; +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("10: Parallel PagedArray::ValueBuffer::push_back"); +#endif + {// for some reason this: + BufferPushBack tmp(d); + tmp.parallel(problemSize); + }// is faster than: + //tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), + // [&d](const tbb::blocked_range &r){ + // typename ArrayT::ValueBuffer buffer(d); + // for (size_t i=r.begin(), n=r.end(); i!=n; ++i) buffer.push_back(i);}); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + + CPPUNIT_ASSERT_EQUAL(problemSize, d.size()); + CPPUNIT_ASSERT_EQUAL(size_t(1)<>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + + // Test sorting +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("parallel sort"); +#endif + d.sort(); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + for (size_t i=0; i> log2PageSize + CPPUNIT_ASSERT_EQUAL(size_t(1)+(problemSize>>d.log2PageSize()), d.pageCount()); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + + // test PagedArray::fill + const size_t v = 13; + d.fill(v); + for (size_t i=0, n=d.capacity(); i; + ArrayT d; + CPPUNIT_ASSERT_EQUAL(size_t(0), d.size()); + { + ArrayT::ValueBuffer vc(d); + vc.push_back(1); + vc.push_back(2); + CPPUNIT_ASSERT_EQUAL(size_t(0), d.size()); + vc.flush(); + CPPUNIT_ASSERT_EQUAL(size_t(2), d.size()); + CPPUNIT_ASSERT_EQUAL(size_t(1), d[0]); + CPPUNIT_ASSERT_EQUAL(size_t(2), d[1]); + } + CPPUNIT_ASSERT_EQUAL(size_t(2), d.size()); + CPPUNIT_ASSERT_EQUAL(size_t(1), d[0]); + CPPUNIT_ASSERT_EQUAL(size_t(2), d[1]); + } + {//thread-local-storage PagedArray::ValueBuffer::push_back followed by parallel sort + using ArrayT = openvdb::util::PagedArray; + ArrayT d; + +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("11: Parallel TLS PagedArray::ValueBuffer::push_back"); +#endif + {// for some reason this: + TLS_BufferPushBack tmp(d); + tmp.parallel(problemSize); + }// is faster than: + //ArrayT::ValueBuffer exemplar(d);//dummy used for initialization + ///tbb::enumerable_thread_specific pool(exemplar);//thread local storage pool of ValueBuffers + //tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), + // [&pool](const tbb::blocked_range &range){ + // ArrayT::ValueBuffer &buffer = pool.local(); + // for (size_t i=range.begin(); i!=range.end(); ++i) buffer.push_back(i);}); + //for (auto i=pool.begin(); i!=pool.end(); ++i) i->flush(); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + //std::cerr << "Number of threads for TLS = " << (buffer.end()-buffer.begin()) << std::endl; + //d.print(); + CPPUNIT_ASSERT_EQUAL(problemSize, d.size()); + CPPUNIT_ASSERT_EQUAL(size_t(1)<>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + + // Not guaranteed to pass + //size_t unsorted = 0; + //for (size_t i=0, n=d.size(); i 0 ); + +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("parallel sort"); +#endif + d.sort(); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + for (size_t i=0, n=d.size(); i; + ArrayT d, d2; + + tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), + [&d](const tbb::blocked_range &range){ + ArrayT::ValueBuffer buffer(d); + for (size_t i=range.begin(); i!=range.end(); ++i) buffer.push_back(i);}); + CPPUNIT_ASSERT_EQUAL(problemSize, d.size()); + CPPUNIT_ASSERT_EQUAL(size_t(1)<>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); + CPPUNIT_ASSERT(!d.isPartiallyFull()); + d.push_back(problemSize); + CPPUNIT_ASSERT(d.isPartiallyFull()); + + tbb::parallel_for(tbb::blocked_range(problemSize+1, 2*problemSize+1, d2.pageSize()), + [&d2](const tbb::blocked_range &range){ + ArrayT::ValueBuffer buffer(d2); + for (size_t i=range.begin(); i!=range.end(); ++i) buffer.push_back(i);}); + //for (size_t i=d.size(), n=i+problemSize; i>d2.log2PageSize(), d2.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(d2.pageCount()*d2.pageSize(), d2.capacity()); + + //d.print(); + //d2.print(); +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("parallel PagedArray::merge"); +#endif + d.merge(d2); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + CPPUNIT_ASSERT(d.isPartiallyFull()); + + //d.print(); + //d2.print(); + CPPUNIT_ASSERT_EQUAL(2*problemSize+1, d.size()); + CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); + CPPUNIT_ASSERT_EQUAL(size_t(0), d2.size()); + CPPUNIT_ASSERT_EQUAL(size_t(0), d2.pageCount()); + +#ifdef BENCHMARK_PAGED_ARRAY + timer.start("parallel sort of merged array"); +#endif + d.sort(); +#ifdef BENCHMARK_PAGED_ARRAY + timer.stop(); +#endif + for (size_t i=0, n=d.size(); i array; + for (int i=0; i<100000; ++i) array.push_back(i); + for (int i=0; i<100000; ++i) CPPUNIT_ASSERT_EQUAL(i, array[i]); + } + {//2A + openvdb::util::PagedArray array; + openvdb::util::PagedArray::ValueBuffer buffer(array); + for (int i=0; i<100000; ++i) buffer.push_back(i); + buffer.flush(); + for (int i=0; i<100000; ++i) CPPUNIT_ASSERT_EQUAL(i, array[i]); + } + {//2B + openvdb::util::PagedArray array; + {//local scope of a single thread + openvdb::util::PagedArray::ValueBuffer buffer(array); + for (int i=0; i<100000; ++i) buffer.push_back(i); + } + for (int i=0; i<100000; ++i) CPPUNIT_ASSERT_EQUAL(i, array[i]); + } + {//3A + openvdb::util::PagedArray array; + array.resize(100000); + for (int i=0; i<100000; ++i) array[i] = i; + for (int i=0; i<100000; ++i) CPPUNIT_ASSERT_EQUAL(i, array[i]); + } + {//3B + using ArrayT = openvdb::util::PagedArray; + ArrayT array; + array.resize(100000); + for (ArrayT::Iterator i=array.begin(); i!=array.end(); ++i) *i = int(i.pos()); + for (int i=0; i<100000; ++i) CPPUNIT_ASSERT_EQUAL(i, array[i]); + } + } +} diff --git a/openvdb/unittest/TestValueAccessor.cc b/openvdb/unittest/TestValueAccessor.cc new file mode 100644 index 00000000..032e1a39 --- /dev/null +++ b/openvdb/unittest/TestValueAccessor.cc @@ -0,0 +1,568 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0); + + +using ValueType = float; +using Tree2Type = openvdb::tree::Tree< + openvdb::tree::RootNode< + openvdb::tree::LeafNode > >; +using Tree3Type = openvdb::tree::Tree< + openvdb::tree::RootNode< + openvdb::tree::InternalNode< + openvdb::tree::LeafNode, 4> > >; +using Tree4Type = openvdb::tree::Tree4::Type; +using Tree5Type = openvdb::tree::Tree< + openvdb::tree::RootNode< + openvdb::tree::InternalNode< + openvdb::tree::InternalNode< + openvdb::tree::InternalNode< + openvdb::tree::LeafNode, 4>, 5>, 5> > >; +using TreeType = Tree4Type; + + +using namespace openvdb::tree; + +class TestValueAccessor: public CppUnit::TestFixture +{ +public: + void setUp() override { openvdb::initialize(); } + void tearDown() override { openvdb::uninitialize(); } + + CPPUNIT_TEST_SUITE(TestValueAccessor); + + CPPUNIT_TEST(testTree2Accessor); + CPPUNIT_TEST(testTree2AccessorRW); + CPPUNIT_TEST(testTree2ConstAccessor); + CPPUNIT_TEST(testTree2ConstAccessorRW); + + CPPUNIT_TEST(testTree3Accessor); + CPPUNIT_TEST(testTree3AccessorRW); + CPPUNIT_TEST(testTree3ConstAccessor); + CPPUNIT_TEST(testTree3ConstAccessorRW); + + CPPUNIT_TEST(testTree4Accessor); + CPPUNIT_TEST(testTree4AccessorRW); + CPPUNIT_TEST(testTree4ConstAccessor); + CPPUNIT_TEST(testTree4ConstAccessorRW); + + CPPUNIT_TEST(testTree5Accessor); + CPPUNIT_TEST(testTree5AccessorRW); + CPPUNIT_TEST(testTree5ConstAccessor); + CPPUNIT_TEST(testTree5ConstAccessorRW); + + CPPUNIT_TEST(testTree3Accessor2); + CPPUNIT_TEST(testTree3ConstAccessor2); + CPPUNIT_TEST(testTree4Accessor2); + CPPUNIT_TEST(testTree4ConstAccessor2); + CPPUNIT_TEST(testTree4Accessor1); + CPPUNIT_TEST(testTree4ConstAccessor1); + CPPUNIT_TEST(testTree4Accessor0); + CPPUNIT_TEST(testTree4ConstAccessor0); + CPPUNIT_TEST(testTree5Accessor2); + CPPUNIT_TEST(testTree5ConstAccessor2); + CPPUNIT_TEST(testTree4Accessor12);//cache node level 2 + CPPUNIT_TEST(testTree5Accessor213);//cache node level 1 and 3 + + CPPUNIT_TEST(testMultithreadedAccessor); + CPPUNIT_TEST(testAccessorRegistration); + CPPUNIT_TEST(testGetNode); + + CPPUNIT_TEST_SUITE_END(); + // cache all node levels + void testTree2Accessor() { accessorTest >(); } + void testTree2AccessorRW() { accessorTest >(); } + void testTree2ConstAccessor() { constAccessorTest >(); } + void testTree2ConstAccessorRW() { constAccessorTest >(); } + // cache all node levels + void testTree3Accessor() { accessorTest >(); } + void testTree3AccessorRW() { accessorTest >(); } + void testTree3ConstAccessor() { constAccessorTest >(); } + void testTree3ConstAccessorRW() { constAccessorTest >(); } + // cache all node levels + void testTree4Accessor() { accessorTest >(); } + void testTree4AccessorRW() { accessorTest >(); } + void testTree4ConstAccessor() { constAccessorTest >(); } + void testTree4ConstAccessorRW() { constAccessorTest >(); } + // cache all node levels + void testTree5Accessor() { accessorTest >(); } + void testTree5AccessorRW() { accessorTest >(); } + void testTree5ConstAccessor() { constAccessorTest >(); } + void testTree5ConstAccessorRW() { constAccessorTest >(); } + + // Test odd combinations of trees and ValueAccessors + // cache node level 0 and 1 + void testTree3Accessor2() + { + accessorTest >(); + accessorTest >(); + } + void testTree3ConstAccessor2() + { + constAccessorTest >(); + constAccessorTest >(); + } + void testTree4Accessor2() + { + accessorTest >(); + accessorTest >(); + } + void testTree4ConstAccessor2() + { + constAccessorTest >(); + constAccessorTest >(); + } + void testTree5Accessor2() + { + accessorTest >(); + accessorTest >(); + } + void testTree5ConstAccessor2() + { + constAccessorTest >(); + constAccessorTest >(); + } + // only cache leaf level + void testTree4Accessor1() + { + accessorTest >(); + accessorTest >(); + } + void testTree4ConstAccessor1() + { + constAccessorTest >(); + constAccessorTest >(); + } + // disable node caching + void testTree4Accessor0() + { + accessorTest >(); + accessorTest >(); + } + void testTree4ConstAccessor0() + { + constAccessorTest >(); + constAccessorTest >(); + } + //cache node level 2 + void testTree4Accessor12() + { + accessorTest >(); + accessorTest >(); + } + //cache node level 1 and 3 + void testTree5Accessor213() + { + accessorTest >(); + accessorTest >(); + } + + void testMultithreadedAccessor(); + void testAccessorRegistration(); + void testGetNode(); + +private: + template void accessorTest(); + template void constAccessorTest(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestValueAccessor); + + +//////////////////////////////////////// + + +namespace { + +struct Plus +{ + float addend; + Plus(float f): addend(f) {} + inline void operator()(float& f) const { f += addend; } + inline void operator()(float& f, bool& b) const { f += addend; b = false; } +}; + +} + + +template +void +TestValueAccessor::accessorTest() +{ + using TreeType = typename AccessorT::TreeType; + const int leafDepth = int(TreeType::DEPTH) - 1; + // subtract one because getValueDepth() returns 0 for values at the root + + const ValueType background = 5.0f, value = -9.345f; + const openvdb::Coord c0(5, 10, 20), c1(500000, 200000, 300000); + + { + TreeType tree(background); + CPPUNIT_ASSERT(!tree.isValueOn(c0)); + CPPUNIT_ASSERT(!tree.isValueOn(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(c1)); + tree.setValue(c0, value); + CPPUNIT_ASSERT(tree.isValueOn(c0)); + CPPUNIT_ASSERT(!tree.isValueOn(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(c1)); + } + { + TreeType tree(background); + AccessorT acc(tree); + ValueType v; + + CPPUNIT_ASSERT(!tree.isValueOn(c0)); + CPPUNIT_ASSERT(!tree.isValueOn(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(c1)); + CPPUNIT_ASSERT(!acc.isCached(c0)); + CPPUNIT_ASSERT(!acc.isCached(c1)); + CPPUNIT_ASSERT(!acc.probeValue(c0,v)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, v); + CPPUNIT_ASSERT(!acc.probeValue(c1,v)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, v); + CPPUNIT_ASSERT_EQUAL(-1, acc.getValueDepth(c0)); + CPPUNIT_ASSERT_EQUAL(-1, acc.getValueDepth(c1)); + CPPUNIT_ASSERT(!acc.isVoxel(c0)); + CPPUNIT_ASSERT(!acc.isVoxel(c1)); + + acc.setValue(c0, value); + + CPPUNIT_ASSERT(tree.isValueOn(c0)); + CPPUNIT_ASSERT(!tree.isValueOn(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(c1)); + CPPUNIT_ASSERT(acc.probeValue(c0,v)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, v); + CPPUNIT_ASSERT(!acc.probeValue(c1,v)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, v); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c0)); // leaf-level voxel value + CPPUNIT_ASSERT_EQUAL(-1, acc.getValueDepth(c1)); // background value + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(openvdb::Coord(7, 10, 20))); + const int depth = leafDepth == 1 ? -1 : leafDepth - 1; + CPPUNIT_ASSERT_EQUAL(depth, acc.getValueDepth(openvdb::Coord(8, 10, 20))); + CPPUNIT_ASSERT( acc.isVoxel(c0)); // leaf-level voxel value + CPPUNIT_ASSERT(!acc.isVoxel(c1)); + CPPUNIT_ASSERT( acc.isVoxel(openvdb::Coord(7, 10, 20))); + CPPUNIT_ASSERT(!acc.isVoxel(openvdb::Coord(8, 10, 20))); + + ASSERT_DOUBLES_EXACTLY_EQUAL(background, acc.getValue(c1)); + CPPUNIT_ASSERT(!acc.isCached(c1)); // uncached background value + CPPUNIT_ASSERT(!acc.isValueOn(c1)); // inactive background value + ASSERT_DOUBLES_EXACTLY_EQUAL(value, acc.getValue(c0)); + CPPUNIT_ASSERT( + (acc.numCacheLevels()>0) == acc.isCached(c0)); // active, leaf-level voxel value + CPPUNIT_ASSERT(acc.isValueOn(c0)); + + acc.setValue(c1, value); + + CPPUNIT_ASSERT(acc.isValueOn(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree.getValue(c1)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, acc.getValue(c1)); + CPPUNIT_ASSERT(!acc.isCached(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, acc.getValue(c0)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c0)); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c0)); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c1)); + CPPUNIT_ASSERT(acc.isVoxel(c0)); + CPPUNIT_ASSERT(acc.isVoxel(c1)); + + tree.setValueOff(c1); + + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree.getValue(c1)); + CPPUNIT_ASSERT(!acc.isCached(c0)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c1)); + CPPUNIT_ASSERT( acc.isValueOn(c0)); + CPPUNIT_ASSERT(!acc.isValueOn(c1)); + + acc.setValueOn(c1); + + CPPUNIT_ASSERT(!acc.isCached(c0)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c1)); + CPPUNIT_ASSERT( acc.isValueOn(c0)); + CPPUNIT_ASSERT( acc.isValueOn(c1)); + + acc.modifyValueAndActiveState(c1, Plus(-value)); // subtract value & mark inactive + CPPUNIT_ASSERT(!acc.isValueOn(c1)); + + acc.modifyValue(c1, Plus(-value)); // subtract value again & mark active + + CPPUNIT_ASSERT(acc.isValueOn(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(-value, tree.getValue(c1)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(-value, acc.getValue(c1)); + CPPUNIT_ASSERT(!acc.isCached(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, acc.getValue(c0)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c0)); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c0)); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c1)); + CPPUNIT_ASSERT(acc.isVoxel(c0)); + CPPUNIT_ASSERT(acc.isVoxel(c1)); + + acc.setValueOnly(c1, 3*value); + + CPPUNIT_ASSERT(acc.isValueOn(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, tree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(3*value, tree.getValue(c1)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(3*value, acc.getValue(c1)); + CPPUNIT_ASSERT(!acc.isCached(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, acc.getValue(c0)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c0)); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c0)); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c1)); + CPPUNIT_ASSERT(acc.isVoxel(c0)); + CPPUNIT_ASSERT(acc.isVoxel(c1)); + + acc.clear(); + CPPUNIT_ASSERT(!acc.isCached(c0)); + CPPUNIT_ASSERT(!acc.isCached(c1)); + } +} + + +template +void +TestValueAccessor::constAccessorTest() +{ + using TreeType = typename std::remove_const::type; + const int leafDepth = int(TreeType::DEPTH) - 1; + // subtract one because getValueDepth() returns 0 for values at the root + + const ValueType background = 5.0f, value = -9.345f; + const openvdb::Coord c0(5, 10, 20), c1(500000, 200000, 300000); + ValueType v; + + TreeType tree(background); + AccessorT acc(tree); + + CPPUNIT_ASSERT(!tree.isValueOn(c0)); + CPPUNIT_ASSERT(!tree.isValueOn(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, tree.getValue(c1)); + CPPUNIT_ASSERT(!acc.isCached(c0)); + CPPUNIT_ASSERT(!acc.isCached(c1)); + CPPUNIT_ASSERT(!acc.probeValue(c0,v)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, v); + CPPUNIT_ASSERT(!acc.probeValue(c1,v)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, v); + CPPUNIT_ASSERT_EQUAL(-1, acc.getValueDepth(c0)); + CPPUNIT_ASSERT_EQUAL(-1, acc.getValueDepth(c1)); + CPPUNIT_ASSERT(!acc.isVoxel(c0)); + CPPUNIT_ASSERT(!acc.isVoxel(c1)); + + tree.setValue(c0, value); + + CPPUNIT_ASSERT(tree.isValueOn(c0)); + CPPUNIT_ASSERT(!tree.isValueOn(c1)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, acc.getValue(c1)); + CPPUNIT_ASSERT(!acc.isCached(c1)); + CPPUNIT_ASSERT(!acc.isCached(c0)); + CPPUNIT_ASSERT(acc.isValueOn(c0)); + CPPUNIT_ASSERT(!acc.isValueOn(c1)); + CPPUNIT_ASSERT(acc.probeValue(c0,v)); + ASSERT_DOUBLES_EXACTLY_EQUAL(value, v); + CPPUNIT_ASSERT(!acc.probeValue(c1,v)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, v); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c0)); + CPPUNIT_ASSERT_EQUAL(-1, acc.getValueDepth(c1)); + CPPUNIT_ASSERT( acc.isVoxel(c0)); + CPPUNIT_ASSERT(!acc.isVoxel(c1)); + + ASSERT_DOUBLES_EXACTLY_EQUAL(value, acc.getValue(c0)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c0)); + ASSERT_DOUBLES_EXACTLY_EQUAL(background, acc.getValue(c1)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c0)); + CPPUNIT_ASSERT(!acc.isCached(c1)); + CPPUNIT_ASSERT(acc.isValueOn(c0)); + CPPUNIT_ASSERT(!acc.isValueOn(c1)); + + tree.setValue(c1, value); + + ASSERT_DOUBLES_EXACTLY_EQUAL(value, acc.getValue(c1)); + CPPUNIT_ASSERT(!acc.isCached(c0)); + CPPUNIT_ASSERT((acc.numCacheLevels()>0) == acc.isCached(c1)); + CPPUNIT_ASSERT(acc.isValueOn(c0)); + CPPUNIT_ASSERT(acc.isValueOn(c1)); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c0)); + CPPUNIT_ASSERT_EQUAL(leafDepth, acc.getValueDepth(c1)); + CPPUNIT_ASSERT(acc.isVoxel(c0)); + CPPUNIT_ASSERT(acc.isVoxel(c1)); + + // The next two lines should not compile, because the acc references a const tree: + //acc.setValue(c1, value); + //acc.setValueOff(c1); + + acc.clear(); + CPPUNIT_ASSERT(!acc.isCached(c0)); + CPPUNIT_ASSERT(!acc.isCached(c1)); +} + + +void +TestValueAccessor::testMultithreadedAccessor() +{ +#define MAX_COORD 5000 + + using AccessorT = openvdb::tree::ValueAccessorRW; + // Substituting the following alias typically results in assertion failures: + //using AccessorT = openvdb::tree::ValueAccessor; + + // Task to perform multiple reads through a shared accessor + struct ReadTask: public tbb::task { + AccessorT& acc; + ReadTask(AccessorT& c): acc(c) {} + tbb::task* execute() + { + for (int i = -MAX_COORD; i < MAX_COORD; ++i) { + ASSERT_DOUBLES_EXACTLY_EQUAL(double(i), acc.getValue(openvdb::Coord(i))); + } + return nullptr; + } + }; + // Task to perform multiple writes through a shared accessor + struct WriteTask: public tbb::task { + AccessorT& acc; + WriteTask(AccessorT& c): acc(c) {} + tbb::task* execute() + { + for (int i = -MAX_COORD; i < MAX_COORD; ++i) { + float f = acc.getValue(openvdb::Coord(i)); + ASSERT_DOUBLES_EXACTLY_EQUAL(float(i), f); + acc.setValue(openvdb::Coord(i), float(i)); + ASSERT_DOUBLES_EXACTLY_EQUAL(float(i), acc.getValue(openvdb::Coord(i))); + } + return nullptr; + } + }; + // Parent task to spawn multiple parallel read and write tasks + struct RootTask: public tbb::task { + AccessorT& acc; + RootTask(AccessorT& c): acc(c) {} + tbb::task* execute() + { + ReadTask* r[3]; WriteTask* w[3]; + for (int i = 0; i < 3; ++i) { + r[i] = new(allocate_child()) ReadTask(acc); + w[i] = new(allocate_child()) WriteTask(acc); + } + set_ref_count(6 /*children*/ + 1 /*wait*/); + for (int i = 0; i < 3; ++i) { + spawn(*r[i]); spawn(*w[i]); + } + wait_for_all(); + return nullptr; + } + }; + + Tree4Type tree(/*background=*/0.5); + AccessorT acc(tree); + // Populate the tree. + for (int i = -MAX_COORD; i < MAX_COORD; ++i) { + acc.setValue(openvdb::Coord(i), float(i)); + } + + // Run multiple read and write tasks in parallel. + RootTask& root = *new(tbb::task::allocate_root()) RootTask(acc); + tbb::task::spawn_root_and_wait(root); + +#undef MAX_COORD +} + + +void +TestValueAccessor::testAccessorRegistration() +{ + using openvdb::Index; + + const float background = 5.0f, value = -9.345f; + const openvdb::Coord c0(5, 10, 20); + + openvdb::FloatTree::Ptr tree(new openvdb::FloatTree(background)); + openvdb::tree::ValueAccessor acc(*tree); + + // Set a single leaf voxel via the accessor and verify that + // the cache is populated. + acc.setValue(c0, value); + CPPUNIT_ASSERT_EQUAL(Index(1), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(tree->root().getLevel(), tree->nonLeafCount()); + CPPUNIT_ASSERT(acc.getNode() != nullptr); + + // Reset the voxel to the background value and verify that no nodes + // have been deleted and that the cache is still populated. + tree->setValueOff(c0, background); + CPPUNIT_ASSERT_EQUAL(Index(1), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(tree->root().getLevel(), tree->nonLeafCount()); + CPPUNIT_ASSERT(acc.getNode() != nullptr); + + // Prune the tree and verify that only the root node remains and that + // the cache has been cleared. + openvdb::tools::prune(*tree); + //tree->prune(); + CPPUNIT_ASSERT_EQUAL(Index(0), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(Index(1), tree->nonLeafCount()); // root node only + CPPUNIT_ASSERT(acc.getNode() == nullptr); + + // Set the leaf voxel again and verify that the cache is repopulated. + acc.setValue(c0, value); + CPPUNIT_ASSERT_EQUAL(Index(1), tree->leafCount()); + CPPUNIT_ASSERT_EQUAL(tree->root().getLevel(), tree->nonLeafCount()); + CPPUNIT_ASSERT(acc.getNode() != nullptr); + + // Delete the tree and verify that the cache has been cleared. + tree.reset(); + CPPUNIT_ASSERT(acc.getTree() == nullptr); + CPPUNIT_ASSERT(acc.getNode() == nullptr); + CPPUNIT_ASSERT(acc.getNode() == nullptr); +} + + +void +TestValueAccessor::testGetNode() +{ + using LeafT = Tree4Type::LeafNodeType; + + const ValueType background = 5.0f, value = -9.345f; + const openvdb::Coord c0(5, 10, 20); + + Tree4Type tree(background); + tree.setValue(c0, value); + { + openvdb::tree::ValueAccessor acc(tree); + // Prime the cache. + acc.getValue(c0); + // Verify that the cache contains a leaf node. + LeafT* node = acc.getNode(); + CPPUNIT_ASSERT(node != nullptr); + + // Erase the leaf node from the cache and verify that it is gone. + acc.eraseNode(); + node = acc.getNode(); + CPPUNIT_ASSERT(node == nullptr); + } + { + // As above, but with a const tree. + openvdb::tree::ValueAccessor acc(tree); + acc.getValue(c0); + const LeafT* node = acc.getNode(); + CPPUNIT_ASSERT(node != nullptr); + + acc.eraseNode(); + node = acc.getNode(); + CPPUNIT_ASSERT(node == nullptr); + } +} diff --git a/openvdb/unittest/TestVec2Metadata.cc b/openvdb/unittest/TestVec2Metadata.cc new file mode 100644 index 00000000..285f7eda --- /dev/null +++ b/openvdb/unittest/TestVec2Metadata.cc @@ -0,0 +1,97 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + +class TestVec2Metadata : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestVec2Metadata); + CPPUNIT_TEST(testVec2i); + CPPUNIT_TEST(testVec2s); + CPPUNIT_TEST(testVec2d); + CPPUNIT_TEST_SUITE_END(); + + void testVec2i(); + void testVec2s(); + void testVec2d(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestVec2Metadata); + +void +TestVec2Metadata::testVec2i() +{ + using namespace openvdb; + + Metadata::Ptr m(new Vec2IMetadata(openvdb::Vec2i(1, 1))); + Metadata::Ptr m2 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0); + + CPPUNIT_ASSERT(m->typeName().compare("vec2i") == 0); + CPPUNIT_ASSERT(m2->typeName().compare("vec2i") == 0); + + Vec2IMetadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec2i(1, 1)); + s->value() = openvdb::Vec2i(2, 2); + CPPUNIT_ASSERT(s->value() == openvdb::Vec2i(2, 2)); + + m2->copy(*s); + + s = dynamic_cast(m2.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec2i(2, 2)); +} + +void +TestVec2Metadata::testVec2s() +{ + using namespace openvdb; + + Metadata::Ptr m(new Vec2SMetadata(openvdb::Vec2s(1, 1))); + Metadata::Ptr m2 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0); + + CPPUNIT_ASSERT(m->typeName().compare("vec2s") == 0); + CPPUNIT_ASSERT(m2->typeName().compare("vec2s") == 0); + + Vec2SMetadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec2s(1, 1)); + s->value() = openvdb::Vec2s(2, 2); + CPPUNIT_ASSERT(s->value() == openvdb::Vec2s(2, 2)); + + m2->copy(*s); + + s = dynamic_cast(m2.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec2s(2, 2)); +} + +void +TestVec2Metadata::testVec2d() +{ + using namespace openvdb; + + Metadata::Ptr m(new Vec2DMetadata(openvdb::Vec2d(1, 1))); + Metadata::Ptr m2 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0); + + CPPUNIT_ASSERT(m->typeName().compare("vec2d") == 0); + CPPUNIT_ASSERT(m2->typeName().compare("vec2d") == 0); + + Vec2DMetadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec2d(1, 1)); + s->value() = openvdb::Vec2d(2, 2); + CPPUNIT_ASSERT(s->value() == openvdb::Vec2d(2, 2)); + + m2->copy(*s); + + s = dynamic_cast(m2.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec2d(2, 2)); +} diff --git a/openvdb/unittest/TestVec3Metadata.cc b/openvdb/unittest/TestVec3Metadata.cc new file mode 100644 index 00000000..535e6bf4 --- /dev/null +++ b/openvdb/unittest/TestVec3Metadata.cc @@ -0,0 +1,97 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include + +class TestVec3Metadata : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestVec3Metadata); + CPPUNIT_TEST(testVec3i); + CPPUNIT_TEST(testVec3s); + CPPUNIT_TEST(testVec3d); + CPPUNIT_TEST_SUITE_END(); + + void testVec3i(); + void testVec3s(); + void testVec3d(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestVec3Metadata); + +void +TestVec3Metadata::testVec3i() +{ + using namespace openvdb; + + Metadata::Ptr m(new Vec3IMetadata(openvdb::Vec3i(1, 1, 1))); + Metadata::Ptr m3 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast( m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m3.get()) != 0); + + CPPUNIT_ASSERT( m->typeName().compare("vec3i") == 0); + CPPUNIT_ASSERT(m3->typeName().compare("vec3i") == 0); + + Vec3IMetadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec3i(1, 1, 1)); + s->value() = openvdb::Vec3i(3, 3, 3); + CPPUNIT_ASSERT(s->value() == openvdb::Vec3i(3, 3, 3)); + + m3->copy(*s); + + s = dynamic_cast(m3.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec3i(3, 3, 3)); +} + +void +TestVec3Metadata::testVec3s() +{ + using namespace openvdb; + + Metadata::Ptr m(new Vec3SMetadata(openvdb::Vec3s(1, 1, 1))); + Metadata::Ptr m3 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast( m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m3.get()) != 0); + + CPPUNIT_ASSERT( m->typeName().compare("vec3s") == 0); + CPPUNIT_ASSERT(m3->typeName().compare("vec3s") == 0); + + Vec3SMetadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec3s(1, 1, 1)); + s->value() = openvdb::Vec3s(3, 3, 3); + CPPUNIT_ASSERT(s->value() == openvdb::Vec3s(3, 3, 3)); + + m3->copy(*s); + + s = dynamic_cast(m3.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec3s(3, 3, 3)); +} + +void +TestVec3Metadata::testVec3d() +{ + using namespace openvdb; + + Metadata::Ptr m(new Vec3DMetadata(openvdb::Vec3d(1, 1, 1))); + Metadata::Ptr m3 = m->copy(); + + CPPUNIT_ASSERT(dynamic_cast( m.get()) != 0); + CPPUNIT_ASSERT(dynamic_cast(m3.get()) != 0); + + CPPUNIT_ASSERT( m->typeName().compare("vec3d") == 0); + CPPUNIT_ASSERT(m3->typeName().compare("vec3d") == 0); + + Vec3DMetadata *s = dynamic_cast(m.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec3d(1, 1, 1)); + s->value() = openvdb::Vec3d(3, 3, 3); + CPPUNIT_ASSERT(s->value() == openvdb::Vec3d(3, 3, 3)); + + m3->copy(*s); + + s = dynamic_cast(m3.get()); + CPPUNIT_ASSERT(s->value() == openvdb::Vec3d(3, 3, 3)); +} diff --git a/openvdb/unittest/TestVolumeRayIntersector.cc b/openvdb/unittest/TestVolumeRayIntersector.cc new file mode 100644 index 00000000..0cd9fbb2 --- /dev/null +++ b/openvdb/unittest/TestVolumeRayIntersector.cc @@ -0,0 +1,301 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file unittest/TestVolumeRayIntersector.cc +/// @author Ken Museth + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + + +#define ASSERT_DOUBLES_APPROX_EQUAL(expected, actual) \ + CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/1.e-6); + + +class TestVolumeRayIntersector : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestVolumeRayIntersector); + CPPUNIT_TEST(testAll); + CPPUNIT_TEST_SUITE_END(); + + void testAll(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestVolumeRayIntersector); + +void +TestVolumeRayIntersector::testAll() +{ + using namespace openvdb; + typedef math::Ray RayT; + typedef RayT::Vec3Type Vec3T; + + {//one single leaf node + FloatGrid grid(0.0f); + + grid.tree().setValue(Coord(0,0,0), 1.0f); + grid.tree().setValue(Coord(7,7,7), 1.0f); + + const Vec3T dir( 1.0, 0.0, 0.0); + const Vec3T eye(-1.0, 0.0, 0.0); + const RayT ray(eye, dir);//ray in index space + tools::VolumeRayIntersector inter(grid); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL( 1.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL( 9.0, t1); + CPPUNIT_ASSERT(!inter.march(t0, t1)); + } + {//same as above but with dilation + FloatGrid grid(0.0f); + + grid.tree().setValue(Coord(0,0,0), 1.0f); + grid.tree().setValue(Coord(7,7,7), 1.0f); + + const Vec3T dir( 1.0, 0.0, 0.0); + const Vec3T eye(-1.0, 0.0, 0.0); + const RayT ray(eye, dir);//ray in index space + tools::VolumeRayIntersector inter(grid, 1); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL( 0.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL(17.0, t1); + CPPUNIT_ASSERT(!inter.march(t0, t1)); + } + {//one single leaf node + FloatGrid grid(0.0f); + + grid.tree().setValue(Coord(1,1,1), 1.0f); + grid.tree().setValue(Coord(7,3,3), 1.0f); + + const Vec3T dir( 1.0, 0.0, 0.0); + const Vec3T eye(-1.0, 0.0, 0.0); + const RayT ray(eye, dir);//ray in index space + tools::VolumeRayIntersector inter(grid); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL( 1.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL( 9.0, t1); + CPPUNIT_ASSERT(!inter.march(t0, t1)); + } + {//same as above but with dilation + FloatGrid grid(0.0f); + + grid.tree().setValue(Coord(1,1,1), 1.0f); + grid.tree().setValue(Coord(7,3,3), 1.0f); + + const Vec3T dir( 1.0, 0.0, 0.0); + const Vec3T eye(-1.0, 0.0, 0.0); + const RayT ray(eye, dir);//ray in index space + tools::VolumeRayIntersector inter(grid, 1); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL( 1.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL(17.0, t1); + CPPUNIT_ASSERT(!inter.march(t0, t1)); + } + {//two adjacent leaf nodes + FloatGrid grid(0.0f); + + grid.tree().setValue(Coord(0,0,0), 1.0f); + grid.tree().setValue(Coord(8,0,0), 1.0f); + grid.tree().setValue(Coord(15,7,7), 1.0f); + + const Vec3T dir( 1.0, 0.0, 0.0); + const Vec3T eye(-1.0, 0.0, 0.0); + const RayT ray(eye, dir);//ray in index space + tools::VolumeRayIntersector inter(grid); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL( 1.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL(17.0, t1); + CPPUNIT_ASSERT(!inter.march(t0, t1)); + } + {//two adjacent leafs followed by a gab and leaf + FloatGrid grid(0.0f); + + grid.tree().setValue(Coord(0*8,0,0), 1.0f); + grid.tree().setValue(Coord(1*8,0,0), 1.0f); + grid.tree().setValue(Coord(3*8,0,0), 1.0f); + grid.tree().setValue(Coord(3*8+7,7,7), 1.0f); + + const Vec3T dir( 1.0, 0.0, 0.0); + const Vec3T eye(-1.0, 0.0, 0.0); + const RayT ray(eye, dir);//ray in index space + tools::VolumeRayIntersector inter(grid); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL( 1.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL(17.0, t1); + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL(25.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL(33.0, t1); + CPPUNIT_ASSERT(!inter.march(t0, t1)); + } + {//two adjacent leafs followed by a gab, a leaf and an active tile + FloatGrid grid(0.0f); + + grid.tree().setValue(Coord(0*8,0,0), 1.0f); + grid.tree().setValue(Coord(1*8,0,0), 1.0f); + grid.tree().setValue(Coord(3*8,0,0), 1.0f); + grid.fill(CoordBBox(Coord(4*8,0,0), Coord(4*8+7,7,7)), 2.0f, true); + + const Vec3T dir( 1.0, 0.0, 0.0); + const Vec3T eye(-1.0, 0.0, 0.0); + const RayT ray(eye, dir);//ray in index space + tools::VolumeRayIntersector inter(grid); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL( 1.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL(17.0, t1); + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL(25.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL(41.0, t1); + CPPUNIT_ASSERT(!inter.march(t0, t1)); + } + + {//two adjacent leafs followed by a gab, a leaf and an active tile + FloatGrid grid(0.0f); + + grid.tree().setValue(Coord(0*8,0,0), 1.0f); + grid.tree().setValue(Coord(1*8,0,0), 1.0f); + grid.tree().setValue(Coord(3*8,0,0), 1.0f); + grid.fill(CoordBBox(Coord(4*8,0,0), Coord(4*8+7,7,7)), 2.0f, true); + + const Vec3T dir( 1.0, 0.0, 0.0); + const Vec3T eye(-1.0, 0.0, 0.0); + const RayT ray(eye, dir);//ray in index space + tools::VolumeRayIntersector inter(grid); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + + std::vector list; + inter.hits(list); + CPPUNIT_ASSERT(list.size() == 2); + ASSERT_DOUBLES_APPROX_EQUAL( 1.0, list[0].t0); + ASSERT_DOUBLES_APPROX_EQUAL(17.0, list[0].t1); + ASSERT_DOUBLES_APPROX_EQUAL(25.0, list[1].t0); + ASSERT_DOUBLES_APPROX_EQUAL(41.0, list[1].t1); + } + + {//same as above but now with std::deque instead of std::vector + FloatGrid grid(0.0f); + + grid.tree().setValue(Coord(0*8,0,0), 1.0f); + grid.tree().setValue(Coord(1*8,0,0), 1.0f); + grid.tree().setValue(Coord(3*8,0,0), 1.0f); + grid.fill(CoordBBox(Coord(4*8,0,0), Coord(4*8+7,7,7)), 2.0f, true); + + const Vec3T dir( 1.0, 0.0, 0.0); + const Vec3T eye(-1.0, 0.0, 0.0); + const RayT ray(eye, dir);//ray in index space + tools::VolumeRayIntersector inter(grid); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + + std::deque list; + inter.hits(list); + CPPUNIT_ASSERT(list.size() == 2); + ASSERT_DOUBLES_APPROX_EQUAL( 1.0, list[0].t0); + ASSERT_DOUBLES_APPROX_EQUAL(17.0, list[0].t1); + ASSERT_DOUBLES_APPROX_EQUAL(25.0, list[1].t0); + ASSERT_DOUBLES_APPROX_EQUAL(41.0, list[1].t1); + } + + {// Test submitted by "Jan" @ GitHub + FloatGrid grid(0.0f); + grid.tree().setValue(Coord(0*8,0,0), 1.0f); + grid.tree().setValue(Coord(1*8,0,0), 1.0f); + grid.tree().setValue(Coord(3*8,0,0), 1.0f); + tools::VolumeRayIntersector inter(grid); + + const Vec3T dir(-1.0, 0.0, 0.0); + const Vec3T eye(50.0, 0.0, 0.0); + const RayT ray(eye, dir); + CPPUNIT_ASSERT(inter.setIndexRay(ray)); + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL(18.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL(26.0, t1); + CPPUNIT_ASSERT(inter.march(t0, t1)); + ASSERT_DOUBLES_APPROX_EQUAL(34.0, t0); + ASSERT_DOUBLES_APPROX_EQUAL(50.0, t1); + CPPUNIT_ASSERT(!inter.march(t0, t1)); + } + + {// Test submitted by "Trevor" @ GitHub + + FloatGrid::Ptr grid = createGrid(0.0f); + grid->tree().setValue(Coord(0,0,0), 1.0f); + tools::dilateVoxels(grid->tree()); + tools::VolumeRayIntersector inter(*grid); + + //std::cerr << "BBox = " << inter.bbox() << std::endl; + + const Vec3T eye(-0.25, -0.25, 10.0); + const Vec3T dir( 0.00, 0.00, -1.0); + const RayT ray(eye, dir); + CPPUNIT_ASSERT(inter.setIndexRay(ray));// hits bbox + + double t0=0, t1=0; + CPPUNIT_ASSERT(!inter.march(t0, t1));// misses leafs + } + + {// Test submitted by "Trevor" @ GitHub + + FloatGrid::Ptr grid = createGrid(0.0f); + grid->tree().setValue(Coord(0,0,0), 1.0f); + tools::dilateVoxels(grid->tree()); + tools::VolumeRayIntersector inter(*grid); + + //GridPtrVec grids; + //grids.push_back(grid); + //io::File vdbfile("trevor_v1.vdb"); + //vdbfile.write(grids); + + //std::cerr << "BBox = " << inter.bbox() << std::endl; + + const Vec3T eye(0.75, 0.75, 10.0); + const Vec3T dir( 0.00, 0.00, -1.0); + const RayT ray(eye, dir); + CPPUNIT_ASSERT(inter.setIndexRay(ray));// hits bbox + + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1));// misses leafs + //std::cerr << "t0=" << t0 << " t1=" << t1 << std::endl; + } + + {// Test derived from the test submitted by "Trevor" @ GitHub + + FloatGrid grid(0.0f); + grid.fill(math::CoordBBox(Coord(-1,-1,-1),Coord(1,1,1)), 1.0f); + tools::VolumeRayIntersector inter(grid); + //std::cerr << "BBox = " << inter.bbox() << std::endl; + + const Vec3T eye(-0.25, -0.25, 10.0); + const Vec3T dir( 0.00, 0.00, -1.0); + const RayT ray(eye, dir); + CPPUNIT_ASSERT(inter.setIndexRay(ray));// hits bbox + + double t0=0, t1=0; + CPPUNIT_ASSERT(inter.march(t0, t1));// hits leafs + //std::cerr << "t0=" << t0 << " t1=" << t1 << std::endl; + } +} diff --git a/openvdb/unittest/TestVolumeToMesh.cc b/openvdb/unittest/TestVolumeToMesh.cc new file mode 100644 index 00000000..5b7fa93e --- /dev/null +++ b/openvdb/unittest/TestVolumeToMesh.cc @@ -0,0 +1,126 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include + +#include + +class TestVolumeToMesh: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestVolumeToMesh); + CPPUNIT_TEST(testAuxiliaryDataCollection); + CPPUNIT_TEST(testUniformMeshing); + CPPUNIT_TEST_SUITE_END(); + + void testAuxiliaryDataCollection(); + void testUniformMeshing(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestVolumeToMesh); + + +//////////////////////////////////////// + + +void +TestVolumeToMesh::testAuxiliaryDataCollection() +{ + typedef openvdb::tree::Tree4::Type FloatTreeType; + typedef FloatTreeType::ValueConverter::Type BoolTreeType; + + const float iso = 0.0f; + const openvdb::Coord ijk(0,0,0); + + FloatTreeType inputTree(1.0f); + inputTree.setValue(ijk, -1.0f); + + BoolTreeType intersectionTree(false); + + openvdb::tools::volume_to_mesh_internal::identifySurfaceIntersectingVoxels( + intersectionTree, inputTree, iso); + + CPPUNIT_ASSERT_EQUAL(size_t(8), size_t(intersectionTree.activeVoxelCount())); + + typedef FloatTreeType::ValueConverter::Type Int16TreeType; + typedef FloatTreeType::ValueConverter::Type Index32TreeType; + + Int16TreeType signFlagsTree(0); + Index32TreeType pointIndexTree(99999); + + openvdb::tools::volume_to_mesh_internal::computeAuxiliaryData( + signFlagsTree, pointIndexTree, intersectionTree, inputTree, iso); + + const int flags = int(signFlagsTree.getValue(ijk)); + + CPPUNIT_ASSERT(bool(flags & openvdb::tools::volume_to_mesh_internal::INSIDE)); + CPPUNIT_ASSERT(bool(flags & openvdb::tools::volume_to_mesh_internal::EDGES)); + CPPUNIT_ASSERT(bool(flags & openvdb::tools::volume_to_mesh_internal::XEDGE)); + CPPUNIT_ASSERT(bool(flags & openvdb::tools::volume_to_mesh_internal::YEDGE)); + CPPUNIT_ASSERT(bool(flags & openvdb::tools::volume_to_mesh_internal::ZEDGE)); +} + + +void +TestVolumeToMesh::testUniformMeshing() +{ + typedef openvdb::tree::Tree4::Type FloatTreeType; + typedef openvdb::Grid FloatGridType; + + FloatGridType grid(1.0f); + + // test voxel region meshing + + openvdb::CoordBBox bbox(openvdb::Coord(1), openvdb::Coord(6)); + + grid.tree().fill(bbox, -1.0f); + + std::vector points; + std::vector quads; + std::vector triangles; + + openvdb::tools::volumeToMesh(grid, points, quads); + + CPPUNIT_ASSERT(!points.empty()); + CPPUNIT_ASSERT_EQUAL(size_t(216), quads.size()); + + + points.clear(); + quads.clear(); + triangles.clear(); + grid.clear(); + + + // test tile region meshing + + grid.tree().addTile(FloatTreeType::LeafNodeType::LEVEL + 1, openvdb::Coord(0), -1.0f, true); + + openvdb::tools::volumeToMesh(grid, points, quads); + + CPPUNIT_ASSERT(!points.empty()); + CPPUNIT_ASSERT_EQUAL(size_t(384), quads.size()); + + + points.clear(); + quads.clear(); + triangles.clear(); + grid.clear(); + + + // test tile region and bool volume meshing + + typedef FloatTreeType::ValueConverter::Type BoolTreeType; + typedef openvdb::Grid BoolGridType; + + BoolGridType maskGrid(false); + + maskGrid.tree().addTile(BoolTreeType::LeafNodeType::LEVEL + 1, openvdb::Coord(0), true, true); + + openvdb::tools::volumeToMesh(maskGrid, points, quads); + + CPPUNIT_ASSERT(!points.empty()); + CPPUNIT_ASSERT_EQUAL(size_t(384), quads.size()); +} diff --git a/openvdb/unittest/TestVolumeToSpheres.cc b/openvdb/unittest/TestVolumeToSpheres.cc new file mode 100644 index 00000000..aec47964 --- /dev/null +++ b/openvdb/unittest/TestVolumeToSpheres.cc @@ -0,0 +1,243 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include // for createLevelSetSphere +#include // for sdfToFogVolume +#include // for fillWithSpheres + +#include +#include +#include +#include + + +class TestVolumeToSpheres: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestVolumeToSpheres); + CPPUNIT_TEST(testFromLevelSet); + CPPUNIT_TEST(testFromFog); + CPPUNIT_TEST(testMinimumSphereCount); + CPPUNIT_TEST(testClosestSurfacePoint); + CPPUNIT_TEST_SUITE_END(); + + void testFromLevelSet(); + void testFromFog(); + void testMinimumSphereCount(); + void testClosestSurfacePoint(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestVolumeToSpheres); + + +//////////////////////////////////////// + + +void +TestVolumeToSpheres::testFromLevelSet() +{ + const float + radius = 20.0f, + voxelSize = 1.0f, + halfWidth = 3.0f; + const openvdb::Vec3f center(15.0f, 13.0f, 16.0f); + + openvdb::FloatGrid::ConstPtr grid = openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, halfWidth); + + const bool overlapping = false; + const int instanceCount = 10000; + const float + isovalue = 0.0f, + minRadius = 5.0f, + maxRadius = std::numeric_limits::max(); + const openvdb::Vec2i sphereCount(1, 100); + + { + std::vector spheres; + + openvdb::tools::fillWithSpheres(*grid, spheres, sphereCount, overlapping, + minRadius, maxRadius, isovalue, instanceCount); + + CPPUNIT_ASSERT_EQUAL(1, int(spheres.size())); + + //for (size_t i=0; i< spheres.size(); ++i) { + // std::cout << "\nSphere #" << i << ": " << spheres[i] << std::endl; + //} + + const auto tolerance = 2.0 * voxelSize; + CPPUNIT_ASSERT_DOUBLES_EQUAL(center[0], spheres[0][0], tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(center[1], spheres[0][1], tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(center[2], spheres[0][2], tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(radius, spheres[0][3], tolerance); + } + { + // Verify that an isovalue outside the narrow band still produces a valid sphere. + std::vector spheres; + openvdb::tools::fillWithSpheres(*grid, spheres, sphereCount, + overlapping, minRadius, maxRadius, 1.5f * halfWidth, instanceCount); + CPPUNIT_ASSERT_EQUAL(1, int(spheres.size())); + } + { + // Verify that an isovalue inside the narrow band produces no spheres. + std::vector spheres; + openvdb::tools::fillWithSpheres(*grid, spheres, sphereCount, + overlapping, minRadius, maxRadius, -1.5f * halfWidth, instanceCount); + CPPUNIT_ASSERT_EQUAL(0, int(spheres.size())); + } +} + + +void +TestVolumeToSpheres::testFromFog() +{ + const float + radius = 20.0f, + voxelSize = 1.0f, + halfWidth = 3.0f; + const openvdb::Vec3f center(15.0f, 13.0f, 16.0f); + + auto grid = openvdb::tools::createLevelSetSphere( + radius, center, voxelSize, halfWidth); + openvdb::tools::sdfToFogVolume(*grid); + + const bool overlapping = false; + const int instanceCount = 10000; + const float + isovalue = 0.01f, + minRadius = 5.0f, + maxRadius = std::numeric_limits::max(); + const openvdb::Vec2i sphereCount(1, 100); + + { + std::vector spheres; + openvdb::tools::fillWithSpheres(*grid, spheres, sphereCount, overlapping, + minRadius, maxRadius, isovalue, instanceCount); + + //for (size_t i=0; i< spheres.size(); ++i) { + // std::cout << "\nSphere #" << i << ": " << spheres[i] << std::endl; + //} + + CPPUNIT_ASSERT_EQUAL(1, int(spheres.size())); + + const auto tolerance = 2.0 * voxelSize; + CPPUNIT_ASSERT_DOUBLES_EQUAL(center[0], spheres[0][0], tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(center[1], spheres[0][1], tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(center[2], spheres[0][2], tolerance); + CPPUNIT_ASSERT_DOUBLES_EQUAL(radius, spheres[0][3], tolerance); + } + { + // Verify that an isovalue outside the narrow band still produces valid spheres. + std::vector spheres; + openvdb::tools::fillWithSpheres(*grid, spheres, sphereCount, overlapping, + minRadius, maxRadius, 10.0f, instanceCount); + CPPUNIT_ASSERT(!spheres.empty()); + } +} + + +void +TestVolumeToSpheres::testMinimumSphereCount() +{ + using namespace openvdb; + { + auto grid = tools::createLevelSetSphere(/*radius=*/5.0f, + /*center=*/Vec3f(15.0f, 13.0f, 16.0f), /*voxelSize=*/1.0f, /*halfWidth=*/3.0f); + + // Verify that the requested minimum number of spheres is generated, for various minima. + const int maxSphereCount = 100; + for (int minSphereCount = 1; minSphereCount < 20; minSphereCount += 5) { + std::vector spheres; + tools::fillWithSpheres(*grid, spheres, Vec2i(minSphereCount, maxSphereCount), + /*overlapping=*/true, /*minRadius=*/2.0f); + + // Given the relatively large minimum radius, the actual sphere count + // should be no larger than the requested mimimum count. + CPPUNIT_ASSERT_EQUAL(minSphereCount, int(spheres.size())); + //CPPUNIT_ASSERT(int(spheres.size()) >= minSphereCount); + CPPUNIT_ASSERT(int(spheres.size()) <= maxSphereCount); + } + } + { + // One step in the sphere packing algorithm is to erode the active voxel mask + // of the input grid. Previously, for very small grids this sometimes resulted in + // an empty mask and therefore no spheres. Verify that that no longer happens + // (as long as the minimum sphere count is nonzero). + + FloatGrid grid; + CoordBBox bbox(Coord(1), Coord(2)); + grid.fill(bbox, 1.0f); + + const float minRadius = 1.0f; + const Vec2i sphereCount(5, 100); + + std::vector spheres; + tools::fillWithSpheres(grid, spheres, sphereCount, /*overlapping=*/true, minRadius); + + CPPUNIT_ASSERT(int(spheres.size()) >= sphereCount[0]); + } +} + + +void +TestVolumeToSpheres::testClosestSurfacePoint() +{ + using namespace openvdb; + + const float voxelSize = 1.0f; + const Vec3f center{0.0f}; // ensure multiple internal nodes + + for (const float radius: { 8.0f, 50.0f }) { + // Construct a spherical level set. + const auto sphere = tools::createLevelSetSphere(radius, center, voxelSize); + CPPUNIT_ASSERT(sphere); + + // Construct the corners of a cube that exactly encloses the sphere. + const std::vector corners{ + { -radius, -radius, -radius }, + { -radius, -radius, radius }, + { -radius, radius, -radius }, + { -radius, radius, radius }, + { radius, -radius, -radius }, + { radius, -radius, radius }, + { radius, radius, -radius }, + { radius, radius, radius }, + }; + // Compute the distance from a corner of the cube to the surface of the sphere. + const auto distToSurface = Vec3d{radius}.length() - radius; + + auto csp = tools::ClosestSurfacePoint::create(*sphere); + CPPUNIT_ASSERT(csp); + + // Move each corner point to the closest surface point. + auto points = corners; + std::vector distances; + bool ok = csp->searchAndReplace(points, distances); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(8, int(points.size())); + CPPUNIT_ASSERT_EQUAL(8, int(distances.size())); + + for (auto d: distances) { + CPPUNIT_ASSERT((std::abs(d - distToSurface) / distToSurface) < 0.01); // rel err < 1% + } + for (int i = 0; i < 8; ++i) { + const auto intersection = corners[i] + distToSurface * (center - corners[i]).unit(); + CPPUNIT_ASSERT(points[i].eq(intersection, /*tolerance=*/0.1)); + } + + // Place a point inside the sphere. + points.clear(); + distances.clear(); + points.emplace_back(1, 0, 0); + ok = csp->searchAndReplace(points, distances); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(1, int(points.size())); + CPPUNIT_ASSERT_EQUAL(1, int(distances.size())); + CPPUNIT_ASSERT((std::abs(radius - 1 - distances[0]) / (radius - 1)) < 0.01); + CPPUNIT_ASSERT(points[0].eq(Vec3R{radius, 0, 0}, /*tolerance=*/0.5)); + ///< @todo off by half a voxel in y and z + } +} diff --git a/openvdb/unittest/main.cc b/openvdb/unittest/main.cc new file mode 100644 index 00000000..fd4f9883 --- /dev/null +++ b/openvdb/unittest/main.cc @@ -0,0 +1,246 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for std::shuffle() +#include // for std::round() +#include // for EXIT_SUCCESS +#include // for strrchr() +#include +#include +#include +#include +#include +#include + + +namespace { + +using StringVec = std::vector; + + +void +usage(const char* progName, std::ostream& ostrm) +{ + ostrm << +"Usage: " << progName << " [options]\n" << +"Which: runs OpenVDB library unit tests\n" << +"Options:\n" << +" -f file read whitespace-separated names of tests to be run\n" << +" from the given file (\"#\" comments are supported)\n" << +" -l list all available tests\n" << +" -shuffle run tests in random order\n" << +" -t test specific suite or test to run, e.g., \"-t TestGrid\"\n" << +" or \"-t TestGrid::testGetGrid\" (default: run all tests)\n" << +" -v verbose output\n"; +#ifdef OPENVDB_USE_LOG4CPLUS + ostrm << +"\n" << +" -error log fatal and non-fatal errors (default: log only fatal errors)\n" << +" -warn log warnings and errors\n" << +" -info log info messages, warnings and errors\n" << +" -debug log debugging messages, info messages, warnings and errors\n"; +#endif +} + + +void +getTestNames(StringVec& nameVec, const CppUnit::Test* test) +{ + if (test) { + const int numChildren = test->getChildTestCount(); + if (numChildren == 0) { + nameVec.push_back(test->getName()); + } else { + for (int i = 0; i < test->getChildTestCount(); ++i) { + getTestNames(nameVec, test->getChildTestAt(i)); + } + } + } +} + + +/// Listener that prints the name, elapsed time, and error status of each test +class TimedTestProgressListener: public CppUnit::TestListener +{ +public: + void startTest(CppUnit::Test* test) override + { + mFailed = false; + std::cout << test->getName() << std::flush; + mTimer.start(); + } + + void addFailure(const CppUnit::TestFailure& failure) override + { + std::cout << " : " << (failure.isError() ? "error" : "assertion"); + mFailed = true; + } + + void endTest(CppUnit::Test*) override + { + if (!mFailed) { + // Print elapsed time only for successful tests. + const double msec = std::round(mTimer.milliseconds()); + if (msec > 1.0) { + openvdb::util::printTime(std::cout, msec, " : OK (", ")", + /*width=*/0, /*precision=*/(msec > 1000.0 ? 1 : 0), /*verbose=*/0); + } else { + std::cout << " : OK (<1ms)"; + } + } + std::cout << std::endl; + } + +private: + openvdb::util::CpuTimer mTimer; + bool mFailed = false; +}; + + +int +run(int argc, char* argv[]) +{ + const char* progName = argv[0]; + if (const char* ptr = ::strrchr(progName, '/')) progName = ptr + 1; + + bool shuffle = false, verbose = false; + StringVec tests; + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "-l") { + StringVec allTests; + getTestNames(allTests, + CppUnit::TestFactoryRegistry::getRegistry().makeTest()); + for (const auto& name: allTests) { std::cout << name << "\n"; } + return EXIT_SUCCESS; + } else if (arg == "-shuffle") { + shuffle = true; + } else if (arg == "-v") { + verbose = true; + } else if (arg == "-t") { + if (i + 1 < argc) { + ++i; + tests.push_back(argv[i]); + } else { + OPENVDB_LOG_FATAL("missing test name after \"-t\""); + usage(progName, std::cerr); + return EXIT_FAILURE; + } + } else if (arg == "-f") { + if (i + 1 < argc) { + ++i; + std::ifstream file{argv[i]}; + if (file.fail()) { + OPENVDB_LOG_FATAL("unable to read file " << argv[i]); + return EXIT_FAILURE; + } + while (file) { + // Read a whitespace-separated string from the file. + std::string test; + file >> test; + if (!test.empty()) { + if (test[0] != '#') { + tests.push_back(test); + } else { + // If the string starts with a comment symbol ("#"), + // skip it and jump to the end of the line. + while (file) { if (file.get() == '\n') break; } + } + } + } + } else { + OPENVDB_LOG_FATAL("missing filename after \"-f\""); + usage(progName, std::cerr); + return EXIT_FAILURE; + } + } else if (arg == "-h" || arg == "-help" || arg == "--help") { + usage(progName, std::cout); + return EXIT_SUCCESS; + } else { + OPENVDB_LOG_FATAL("unrecognized option \"" << arg << "\""); + usage(progName, std::cerr); + return EXIT_FAILURE; + } + } + + try { + CppUnit::TestFactoryRegistry& registry = + CppUnit::TestFactoryRegistry::getRegistry(); + + auto* root = registry.makeTest(); + if (!root) { + throw std::runtime_error( + "CppUnit test registry was not initialized properly"); + } + + if (!shuffle) { + if (tests.empty()) tests.push_back(""); + } else { + // Get the names of all selected tests and their children. + StringVec allTests; + if (tests.empty()) { + getTestNames(allTests, root); + } else { + for (const auto& name: tests) { + getTestNames(allTests, root->findTest(name)); + } + } + // Randomly shuffle the list of names. + std::random_device randDev; + std::mt19937 generator(randDev()); + std::shuffle(allTests.begin(), allTests.end(), generator); + tests.swap(allTests); + } + + CppUnit::TestRunner runner; + runner.addTest(root); + + CppUnit::TestResult controller; + + CppUnit::TestResultCollector result; + controller.addListener(&result); + + CppUnit::TextTestProgressListener progress; + TimedTestProgressListener vProgress; + if (verbose) { + controller.addListener(&vProgress); + } else { + controller.addListener(&progress); + } + + for (size_t i = 0; i < tests.size(); ++i) { + runner.run(controller, tests[i]); + } + + CppUnit::CompilerOutputter outputter(&result, std::cerr); + outputter.write(); + + return result.wasSuccessful() ? EXIT_SUCCESS : EXIT_FAILURE; + + } catch (std::exception& e) { + OPENVDB_LOG_FATAL(e.what()); + return EXIT_FAILURE; + } +} + +} // anonymous namespace + + +int +main(int argc, char *argv[]) +{ + openvdb::logging::initialize(argc, argv); + + return run(argc, argv); +} diff --git a/openvdb/unittest/util.h b/openvdb/unittest/util.h new file mode 100644 index 00000000..a2d0021b --- /dev/null +++ b/openvdb/unittest/util.h @@ -0,0 +1,140 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_UNITTEST_UTIL_HAS_BEEN_INCLUDED +#define OPENVDB_UNITTEST_UTIL_HAS_BEEN_INCLUDED + +#include +#include // for math::Random01 +#include // for pruneLevelSet +#include + +namespace unittest_util { + +enum SphereMode { SPHERE_DENSE, SPHERE_DENSE_NARROW_BAND, SPHERE_SPARSE_NARROW_BAND }; + +/// @brief Generates the signed distance to a sphere located at @a center +/// and with a specified @a radius (both in world coordinates). Only voxels +/// in the domain [0,0,0] -> @a dim are considered. Also note that the +/// level set is either dense, dense narrow-band or sparse narrow-band. +/// +/// @note This method is VERY SLOW and should only be used for debugging purposes! +/// However it works for any transform and even with open level sets. +/// A faster approch for closed narrow band generation is to only set voxels +/// sparsely and then use grid::signedFloodFill to define the sign +/// of the background values and tiles! This is implemented in openvdb/tools/LevelSetSphere.h +template +inline void +makeSphere(const openvdb::Coord& dim, const openvdb::Vec3f& center, float radius, + GridType& grid, SphereMode mode) +{ + typedef typename GridType::ValueType ValueT; + const ValueT + zero = openvdb::zeroVal(), + outside = grid.background(), + inside = -outside; + + typename GridType::Accessor acc = grid.getAccessor(); + openvdb::Coord xyz; + for (xyz[0]=0; xyz[0] +inline void +makeSphere(const openvdb::Coord& dim, const openvdb::Vec3f& center, + float radius, openvdb::BoolGrid& grid, SphereMode) +{ + openvdb::BoolGrid::Accessor acc = grid.getAccessor(); + openvdb::Coord xyz; + for (xyz[0]=0; xyz[0]((p-center).length() - radius); + if (dist <= 0) acc.setValue(xyz, true); + } + } + } +} + +// This method will soon be replaced by the one above!!!!! +template +inline void +makeSphere(const openvdb::Coord& dim, const openvdb::Vec3f& center, float radius, + GridType &grid, float dx, SphereMode mode) +{ + grid.setTransform(openvdb::math::Transform::createLinearTransform(/*voxel size=*/dx)); + makeSphere(dim, center, radius, grid, mode); +} + +// Generate random points by uniformly distributing points +// on a unit-sphere. +inline void genPoints(const int numPoints, std::vector& points) +{ + // init + openvdb::math::Random01 randNumber(0); + const int n = int(std::sqrt(double(numPoints))); + const double xScale = (2.0 * M_PI) / double(n); + const double yScale = M_PI / double(n); + + double x, y, theta, phi; + openvdb::Vec3R pos; + + points.reserve(n*n); + + // loop over a [0 to n) x [0 to n) grid. + for (int a = 0; a < n; ++a) { + for (int b = 0; b < n; ++b) { + + // jitter, move to random pos. inside the current cell + x = double(a) + randNumber(); + y = double(b) + randNumber(); + + // remap to a lat/long map + theta = y * yScale; // [0 to PI] + phi = x * xScale; // [0 to 2PI] + + // convert to cartesian coordinates on a unit sphere. + // spherical coordinate triplet (r=1, theta, phi) + pos[0] = std::sin(theta)*std::cos(phi); + pos[1] = std::sin(theta)*std::sin(phi); + pos[2] = std::cos(theta); + + points.push_back(pos); + } + } +} + +// @todo makePlane + +} // namespace unittest_util + +#endif // OPENVDB_UNITTEST_UTIL_HAS_BEEN_INCLUDED diff --git a/openvdb/util/CpuTimer.h b/openvdb/util/CpuTimer.h new file mode 100644 index 00000000..3db8afc6 --- /dev/null +++ b/openvdb/util/CpuTimer.h @@ -0,0 +1,174 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_UTIL_CPUTIMER_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_CPUTIMER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include // for std::cerr +#include // for ostringstream +#include // for setprecision +#include "Formats.h"// for printTime + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +/// @brief Simple timer for basic profiling. +/// +/// @code +/// util::CpuTimer timer; +/// // code here will not be timed! +/// timer.start("algorithm"); +/// // code to be timed goes here +/// timer.stop(); +/// @endcode +/// +/// or to time multiple blocks of code +/// +/// @code +/// util::CpuTimer timer("algorithm 1"); +/// // code to be timed goes here +/// timer.restart("algorithm 2"); +/// // code to be timed goes here +/// timer.stop(); +/// @endcode +/// +/// or to measure speedup between multiple runs +/// +/// @code +/// util::CpuTimer timer("algorithm 1"); +/// // code for the first run goes here +/// const double t1 = timer.restart("algorithm 2"); +/// // code for the second run goes here +/// const double t2 = timer.stop(); +/// std::cerr << "Algorithm 1 is " << (t2/t1) +/// << " timers faster than algorithm 2\n"; +/// @endcode +/// +/// or to measure multiple blocks of code with deferred output +/// +/// @code +/// util::CpuTimer timer(); +/// // code here will not be timed! +/// timer.start(); +/// // code for the first run goes here +/// const double t1 = timer.restart();//time in milliseconds +/// // code for the second run goes here +/// const double t2 = timer.restart();//time in milliseconds +/// // code here will not be timed! +/// util::printTime(std::cout, t1, "Algorithm 1 completed in "); +/// util::printTime(std::cout, t2, "Algorithm 2 completed in "); +/// @endcode +class CpuTimer +{ +public: + + /// @brief Initiate timer + CpuTimer(std::ostream& os = std::cerr) : mOutStream(os), mT0(tbb::tick_count::now()) {} + + /// @brief Prints message and start timer. + /// + /// @note Should normally be followed by a call to stop() + CpuTimer(const std::string& msg, std::ostream& os = std::cerr) : mOutStream(os), mT0() { this->start(msg); } + + /// @brief Start timer. + /// + /// @note Should normally be followed by a call to milliseconds() or stop(std::string) + inline void start() { mT0 = tbb::tick_count::now(); } + + /// @brief Print message and start timer. + /// + /// @note Should normally be followed by a call to stop() + inline void start(const std::string& msg) + { + mOutStream << msg << " ..."; + this->start(); + } + + /// @brief Return Time difference in milliseconds since construction or start was called. + /// + /// @note Combine this method with start() to get timing without any outputs. + inline double milliseconds() const + { + tbb::tick_count::interval_t dt = tbb::tick_count::now() - mT0; + return 1000.0*dt.seconds(); + } + + /// @brief Return Time difference in seconds since construction or start was called. + /// + /// @note Combine this method with start() to get timing without any outputs. + inline double seconds() const + { + tbb::tick_count::interval_t dt = tbb::tick_count::now() - mT0; + return dt.seconds(); + } + + /// @brief This method is identical to milliseconds() - deprecated + OPENVDB_DEPRECATED inline double delta() const { return this->milliseconds(); } + + inline std::string time() const + { + const double msec = this->milliseconds(); + std::ostringstream os; + printTime(os, msec, "", "", 4, 1, 1); + return os.str(); + } + + /// @brief Returns and prints time in milliseconds since construction or start was called. + /// + /// @note Combine this method with start(std::string) to print at start and stop of task being timed. + inline double stop() const + { + const double msec = this->milliseconds(); + printTime(mOutStream, msec, " completed in ", "\n", 4, 3, 1); + return msec; + } + + /// @brief Returns and prints time in milliseconds since construction or start was called. + /// + /// @note Combine this method with start() to delay output of task being timed. + inline double stop(const std::string& msg) const + { + const double msec = this->milliseconds(); + mOutStream << msg << " ..."; + printTime(mOutStream, msec, " completed in ", "\n", 4, 3, 1); + return msec; + } + + /// @brief Re-start timer. + /// @return time in milliseconds since previous start or restart. + /// + /// @note Should normally be followed by a call to stop() or restart() + inline double restart() + { + const double msec = this->milliseconds(); + this->start(); + return msec; + } + + /// @brief Stop previous timer, print message and re-start timer. + /// @return time in milliseconds since previous start or restart. + /// + /// @note Should normally be followed by a call to stop() or restart() + inline double restart(const std::string& msg) + { + const double delta = this->stop(); + this->start(msg); + return delta; + } + +private: + std::ostream& mOutStream; + tbb::tick_count mT0; +};// CpuTimer + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_UTIL_CPUTIMER_HAS_BEEN_INCLUDED diff --git a/openvdb/util/Formats.cc b/openvdb/util/Formats.cc new file mode 100644 index 00000000..e7b87331 --- /dev/null +++ b/openvdb/util/Formats.cc @@ -0,0 +1,148 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Formats.h" +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +int +printBytes(std::ostream& os, uint64_t bytes, + const std::string& head, const std::string& tail, + bool exact, int width, int precision) +{ + const uint64_t one = 1; + int group = 0; + + // Write to a string stream so that I/O manipulators like + // std::setprecision() don't alter the output stream. + std::ostringstream ostr; + ostr << head; + ostr << std::setprecision(precision) << std::setiosflags(std::ios::fixed); + if (bytes >> 40) { + ostr << std::setw(width) << (double(bytes) / double(one << 40)) << " TB"; + group = 4; + } else if (bytes >> 30) { + ostr << std::setw(width) << (double(bytes) / double(one << 30)) << " GB"; + group = 3; + } else if (bytes >> 20) { + ostr << std::setw(width) << (double(bytes) / double(one << 20)) << " MB"; + group = 2; + } else if (bytes >> 10) { + ostr << std::setw(width) << (double(bytes) / double(one << 10)) << " KB"; + group = 1; + } else { + ostr << std::setw(width) << bytes << " Bytes"; + } + if (exact && group) ostr << " (" << bytes << " Bytes)"; + ostr << tail; + + os << ostr.str(); + + return group; +} + + +int +printNumber(std::ostream& os, uint64_t number, + const std::string& head, const std::string& tail, + bool exact, int width, int precision) +{ + int group = 0; + + // Write to a string stream so that I/O manipulators like + // std::setprecision() don't alter the output stream. + std::ostringstream ostr; + ostr << head; + ostr << std::setprecision(precision) << std::setiosflags(std::ios::fixed); + if (number / UINT64_C(1000000000000)) { + ostr << std::setw(width) << (double(number) / 1000000000000.0) << " trillion"; + group = 4; + } else if (number / UINT64_C(1000000000)) { + ostr << std::setw(width) << (double(number) / 1000000000.0) << " billion"; + group = 3; + } else if (number / UINT64_C(1000000)) { + ostr << std::setw(width) << (double(number) / 1000000.0) << " million"; + group = 2; + } else if (number / UINT64_C(1000)) { + ostr << std::setw(width) << (double(number) / 1000.0) << " thousand"; + group = 1; + } else { + ostr << std::setw(width) << number; + } + if (exact && group) ostr << " (" << number << ")"; + ostr << tail; + + os << ostr.str(); + + return group; +} + +int +printTime(std::ostream& os, double milliseconds, + const std::string& head, const std::string& tail, + int width, int precision, int verbose) + { + int group = 0; + + // Write to a string stream so that I/O manipulators like + // std::setprecision() don't alter the output stream. + std::ostringstream ostr; + ostr << head; + ostr << std::setprecision(precision) << std::setiosflags(std::ios::fixed); + + if (milliseconds >= 1000.0) {// one second or longer + const uint32_t seconds = static_cast(milliseconds / 1000.0) % 60 ; + const uint32_t minutes = static_cast(milliseconds / (1000.0*60)) % 60; + const uint32_t hours = static_cast(milliseconds / (1000.0*60*60)) % 24; + const uint32_t days = static_cast(milliseconds / (1000.0*60*60*24)); + if (days>0) { + ostr << days << (verbose==0 ? "d " : days>1 ? " days, " : " day, "); + group = 4; + } + if (hours>0) { + ostr << hours << (verbose==0 ? "h " : hours>1 ? " hours, " : " hour, "); + if (!group) group = 3; + } + if (minutes>0) { + ostr << minutes << (verbose==0 ? "m " : minutes>1 ? " minutes, " : " minute, "); + if (!group) group = 2; + } + if (seconds>0) { + if (verbose) { + ostr << seconds << (seconds>1 ? " seconds and " : " second and "); + const double msec = milliseconds - (seconds + (minutes + (hours + days * 24) * 60) * 60) * 1000.0; + ostr << std::setw(width) << msec << " milliseconds (" << milliseconds << "ms)"; + } else { + const double sec = milliseconds/1000.0 - (minutes + (hours + days * 24) * 60) * 60; + ostr << std::setw(width) << sec << "s"; + } + } else {// zero seconds + const double msec = milliseconds - (minutes + (hours + days * 24) * 60) * 60 * 1000.0; + if (verbose) { + ostr << std::setw(width) << msec << " milliseconds (" << milliseconds << "ms)"; + } else { + ostr << std::setw(width) << msec << "ms"; + } + } + if (!group) group = 1; + } else {// less than a second + ostr << std::setw(width) << milliseconds << (verbose ? " milliseconds" : "ms"); + } + + ostr << tail; + + os << ostr.str(); + + return group; + } + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/util/Formats.h b/openvdb/util/Formats.h new file mode 100644 index 00000000..e603891a --- /dev/null +++ b/openvdb/util/Formats.h @@ -0,0 +1,124 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @author Ken Museth +/// +/// @file Formats.h +/// +/// @brief Utility routines to output nicely-formatted numeric values + + +#ifndef OPENVDB_UTIL_FORMATS_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_FORMATS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +/// Output a byte count with the correct binary suffix (KB, MB, GB or TB). +/// @param os the output stream +/// @param bytes the byte count to be output +/// @param head a string to be output before the numeric text +/// @param tail a string to be output after the numeric text +/// @param exact if true, also output the unmodified count, e.g., "4.6 KB (4620 Bytes)" +/// @param width a fixed width for the numeric text +/// @param precision the number of digits after the decimal point +/// @return 0, 1, 2, 3 or 4, denoting the order of magnitude of the count. +OPENVDB_API int +printBytes(std::ostream& os, uint64_t bytes, + const std::string& head = "", + const std::string& tail = "\n", + bool exact = false, int width = 8, int precision = 3); + +/// Output a number with the correct SI suffix (thousand, million, billion or trillion) +/// @param os the output stream +/// @param number the number to be output +/// @param head a string to be output before the numeric text +/// @param tail a string to be output after the numeric text +/// @param exact if true, also output the unmodified count, e.g., "4.6 Thousand (4620)" +/// @param width a fixed width for the numeric text +/// @param precision the number of digits after the decimal point +/// @return 0, 1, 2, 3 or 4, denoting the order of magnitude of the number. +OPENVDB_API int +printNumber(std::ostream& os, uint64_t number, + const std::string& head = "", + const std::string& tail = "\n", + bool exact = true, int width = 8, int precision = 3); + +/// Output a time in milliseconds with the correct suffix (days, hours, minutes, seconds and milliseconds) +/// @param os the output stream +/// @param milliseconds the time to be output +/// @param head a string to be output before the time +/// @param tail a string to be output after the time +/// @param width a fixed width for the numeric text +/// @param precision the number of digits after the decimal point +/// @param verbose verbose level, 0 is compact format and 1 is long format +/// @return 0, 1, 2, 3, or 4 denoting the order of magnitude of the time. +OPENVDB_API int +printTime(std::ostream& os, double milliseconds, + const std::string& head = "", + const std::string& tail = "\n", + int width = 4, int precision = 1, int verbose = 0); + + +//////////////////////////////////////// + + +/// @brief I/O manipulator that formats integer values with thousands separators +template +class FormattedInt +{ +public: + static char sep() { return ','; } + + FormattedInt(IntT n): mInt(n) {} + + std::ostream& put(std::ostream& os) const + { + // Convert the integer to a string. + std::ostringstream ostr; + ostr << mInt; + std::string s = ostr.str(); + // Prefix the string with spaces if its length is not a multiple of three. + size_t padding = (s.size() % 3) ? 3 - (s.size() % 3) : 0; + s = std::string(padding, ' ') + s; + // Construct a new string in which groups of three digits are followed + // by a separator character. + ostr.str(""); + for (size_t i = 0, N = s.size(); i < N; ) { + ostr << s[i]; + ++i; + if (i >= padding && i % 3 == 0 && i < s.size()) { + ostr << sep(); + } + } + // Remove any padding that was added and output the string. + s = ostr.str(); + os << s.substr(padding, s.size()); + return os; + } + +private: + IntT mInt; +}; + +template +std::ostream& operator<<(std::ostream& os, const FormattedInt& n) { return n.put(os); } + +/// @return an I/O manipulator that formats the given integer value for output to a stream. +template +FormattedInt formattedInt(IntT n) { return FormattedInt(n); } + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_FORMATS_HAS_BEEN_INCLUDED diff --git a/openvdb/util/MapsUtil.h b/openvdb/util/MapsUtil.h new file mode 100644 index 00000000..16c2c8e1 --- /dev/null +++ b/openvdb/util/MapsUtil.h @@ -0,0 +1,294 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file MapsUtil.h + +#ifndef OPENVDB_UTIL_MAPSUTIL_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_MAPSUTIL_HAS_BEEN_INCLUDED + +#include +#include // for std::min(), std::max() +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +// Utility methods for calculating bounding boxes + +/// @brief Calculate an axis-aligned bounding box in the given map's domain +/// (e.g., index space) from an axis-aligned bounding box in its range +/// (e.g., world space) +template +inline void +calculateBounds(const MapType& map, const BBoxd& in, BBoxd& out) +{ + const Vec3d& min = in.min(); + const Vec3d& max = in.max(); + + // the pre-image of the 8 corners of the box + Vec3d corners[8]; + corners[0] = in.min();; + corners[1] = Vec3d(min(0), min(1), min(2)); + corners[2] = Vec3d(max(0), max(1), min(2)); + corners[3] = Vec3d(min(0), max(1), min(2)); + corners[4] = Vec3d(min(0), min(1), max(2)); + corners[5] = Vec3d(max(0), min(1), max(2)); + corners[6] = max; + corners[7] = Vec3d(min(0), max(1), max(2)); + + Vec3d pre_image; + Vec3d& out_min = out.min(); + Vec3d& out_max = out.max(); + out_min = map.applyInverseMap(corners[0]); + out_max = min; + for (int i = 1; i < 8; ++i) { + pre_image = map.applyInverseMap(corners[i]); + for (int j = 0; j < 3; ++j) { + out_min(j) = std::min( out_min(j), pre_image(j)); + out_max(j) = std::max( out_max(j), pre_image(j)); + } + } +} + + +/// @brief Calculate an axis-aligned bounding box in the given map's domain +/// from a spherical bounding box in its range. +template +inline void +calculateBounds(const MapType& map, const Vec3d& center, const Real radius, BBoxd& out) +{ + // On return, out gives a bounding box in continuous index space + // that encloses the sphere. + // + // the image of a sphere under the inverse of the linearMap will be an ellipsoid. + + if (math::is_linear::value) { + // I want to find extrema for three functions f(x', y', z') = x', or = y', or = z' + // with the constraint that g = (x-xo)^2 + (y-yo)^2 + (z-zo)^2 = r^2. + // Where the point x,y,z is the image of x',y',z' + // Solve: \lambda Grad(g) = Grad(f) and g = r^2. + // Note: here (x,y,z) is the image of (x',y',z'), and the gradient + // is w.r.t the (') space. + // + // This can be solved exactly: e_a^T (x' -xo') =\pm r\sqrt(e_a^T J^(-1)J^(-T)e_a) + // where e_a is one of the three unit vectors. - djh. + + /// find the image of the center of the sphere + Vec3d center_pre_image = map.applyInverseMap(center); + + std::vector coordinate_units; + coordinate_units.push_back(Vec3d(1,0,0)); + coordinate_units.push_back(Vec3d(0,1,0)); + coordinate_units.push_back(Vec3d(0,0,1)); + + Vec3d& out_min = out.min(); + Vec3d& out_max = out.max(); + for (int direction = 0; direction < 3; ++direction) { + Vec3d temp = map.applyIJT(coordinate_units[direction]); + double offset = + radius * sqrt(temp.x()*temp.x() + temp.y()*temp.y() + temp.z()*temp.z()); + out_min(direction) = center_pre_image(direction) - offset; + out_max(direction) = center_pre_image(direction) + offset; + } + + } else { + // This is some unknown map type. In this case, we form an axis-aligned + // bounding box for the sphere in world space and find the pre-images of + // the corners in index space. From these corners we compute an axis-aligned + // bounding box in index space. + BBoxd bounding_box(center - radius*Vec3d(1,1,1), center + radius*Vec3d(1,1,1)); + calculateBounds(map, bounding_box, out); + } +} + + +namespace { // anonymous namespace for this helper function + +/// @brief Find the intersection of a line passing through the point +/// (x=0, z=−1/g) with the circle +/// (xxo)² + (zzo)² = r² +/// at a point tangent to the circle. +/// @return 0 if the focal point (0, -1/g) is inside the circle, +/// 1 if the focal point touches the circle, or 2 when both points are found. +inline int +findTangentPoints(const double g, const double xo, const double zo, + const double r, double& xp, double& zp, double& xm, double& zm) +{ + double x2 = xo * xo; + double r2 = r * r; + double xd = g * xo; + double xd2 = xd*xd; + double zd = g * zo + 1.; + double zd2 = zd*zd; + double rd2 = r2*g*g; + + double distA = xd2 + zd2; + double distB = distA - rd2; + + if (distB > 0) { + double discriminate = sqrt(distB); + + xp = xo - xo*rd2/distA + r * zd *discriminate / distA; + xm = xo - xo*rd2/distA - r * zd *discriminate / distA; + + zp = (zo*zd2 + zd*g*(x2 - r2) - xo*xo*g - r*xd*discriminate) / distA; + zm = (zo*zd2 + zd*g*(x2 - r2) - xo*xo*g + r*xd*discriminate) / distA; + + return 2; + + } if (0 >= distB && distB >= -1e-9) { + // the circle touches the focal point (x=0, z = -1/g) + xp = 0; xm = 0; + zp = -1/g; zm = -1/g; + + return 1; + } + + return 0; +} + +} // end anonymous namespace + + +/// @brief Calculate an axis-aligned bounding box in index space +/// from a spherical bounding box in world space. +/// @note This specialization is optimized for a frustum map +template<> +inline void +calculateBounds(const math::NonlinearFrustumMap& frustum, + const Vec3d& center, const Real radius, BBoxd& out) +{ + // The frustum is a nonlinear map followed by a uniform scale, rotation, translation. + // First we invert the translation, rotation and scale to find the spherical pre-image + // of the sphere in "local" coordinates where the frustum is aligned with the near plane + // on the z=0 plane and the "camera" is located at (x=0, y=0, z=-1/g). + + // check that the internal map has no shear. + const math::AffineMap& secondMap = frustum.secondMap(); + // test if the linear part has shear or non-uniform scaling + if (!frustum.hasSimpleAffine()) { + + // In this case, we form an axis-aligned bounding box for sphere in world space + // and find the pre_images of the corners in voxel space. From these corners we + // compute an axis-algined bounding box in voxel-spae + BBoxd bounding_box(center - radius*Vec3d(1,1,1), center + radius*Vec3d(1,1,1)); + calculateBounds(frustum, bounding_box, out); + return; + } + + // for convenience + Vec3d& out_min = out.min(); + Vec3d& out_max = out.max(); + + Vec3d centerLS = secondMap.applyInverseMap(center); + Vec3d voxelSize = secondMap.voxelSize(); + + // all the voxels have the same size since we know this is a simple affine map + double radiusLS = radius / voxelSize(0); + + double gamma = frustum.getGamma(); + double xp; + double zp; + double xm; + double zm; + int soln_number; + + // the bounding box in index space for the points in the frustum + const BBoxd& bbox = frustum.getBBox(); + // initialize min and max + const double x_min = bbox.min().x(); + const double y_min = bbox.min().y(); + const double z_min = bbox.min().z(); + + const double x_max = bbox.max().x(); + const double y_max = bbox.max().y(); + const double z_max = bbox.max().z(); + + out_min.x() = x_min; + out_max.x() = x_max; + out_min.y() = y_min; + out_max.y() = y_max; + + Vec3d extreme; + Vec3d extreme2; + Vec3d pre_image; + // find the x-range + soln_number = findTangentPoints(gamma, centerLS.x(), centerLS.z(), radiusLS, xp, zp, xm, zm); + if (soln_number == 2) { + extreme.x() = xp; + extreme.y() = centerLS.y(); + extreme.z() = zp; + + // location in world space of the tangent point + extreme2 = secondMap.applyMap(extreme); + // convert back to voxel space + pre_image = frustum.applyInverseMap(extreme2); + out_max.x() = std::max(x_min, std::min(x_max, pre_image.x())); + + extreme.x() = xm; + extreme.y() = centerLS.y(); + extreme.z() = zm; + // location in world space of the tangent point + extreme2 = secondMap.applyMap(extreme); + + // convert back to voxel space + pre_image = frustum.applyInverseMap(extreme2); + out_min.x() = std::max(x_min, std::min(x_max, pre_image.x())); + + } else if (soln_number == 1) { + // the circle was tangent at the focal point + } else if (soln_number == 0) { + // the focal point was inside the circle + } + + // find the y-range + soln_number = findTangentPoints(gamma, centerLS.y(), centerLS.z(), radiusLS, xp, zp, xm, zm); + if (soln_number == 2) { + extreme.x() = centerLS.x(); + extreme.y() = xp; + extreme.z() = zp; + + // location in world space of the tangent point + extreme2 = secondMap.applyMap(extreme); + // convert back to voxel space + pre_image = frustum.applyInverseMap(extreme2); + out_max.y() = std::max(y_min, std::min(y_max, pre_image.y())); + + extreme.x() = centerLS.x(); + extreme.y() = xm; + extreme.z() = zm; + extreme2 = secondMap.applyMap(extreme); + + // convert back to voxel space + pre_image = frustum.applyInverseMap(extreme2); + out_min.y() = std::max(y_min, std::min(y_max, pre_image.y())); + + } else if (soln_number == 1) { + // the circle was tangent at the focal point + } else if (soln_number == 0) { + // the focal point was inside the circle + } + + // the near and far + // the closest point. The front of the frustum is at 0 in index space + double near_dist = std::max(centerLS.z() - radiusLS, 0.); + // the farthest point. The back of the frustum is at mDepth in index space + double far_dist = std::min(centerLS.z() + radiusLS, frustum.getDepth() ); + + Vec3d near_point(0.f, 0.f, near_dist); + Vec3d far_point(0.f, 0.f, far_dist); + + out_min.z() = std::max(z_min, frustum.applyInverseMap(secondMap.applyMap(near_point)).z()); + out_max.z() = std::min(z_max, frustum.applyInverseMap(secondMap.applyMap(far_point)).z()); + +} + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_MAPSUTIL_HAS_BEEN_INCLUDED diff --git a/openvdb/util/Name.h b/openvdb/util/Name.h new file mode 100644 index 00000000..c5e362a6 --- /dev/null +++ b/openvdb/util/Name.h @@ -0,0 +1,41 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_UTIL_NAME_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_NAME_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +typedef std::string Name; + +inline Name +readString(std::istream& is) +{ + uint32_t size; + is.read(reinterpret_cast(&size), sizeof(uint32_t)); + std::string buffer(size, ' '); + if (size>0) is.read(&buffer[0], size); + return buffer; +} + + +inline void +writeString(std::ostream& os, const Name& name) +{ + uint32_t size = uint32_t(name.size()); + os.write(reinterpret_cast(&size), sizeof(uint32_t)); + os.write(&name[0], size); +} + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_NAME_HAS_BEEN_INCLUDED diff --git a/openvdb/util/NodeMasks.h b/openvdb/util/NodeMasks.h new file mode 100644 index 00000000..6434fb6f --- /dev/null +++ b/openvdb/util/NodeMasks.h @@ -0,0 +1,1415 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @author Ken Museth +/// +/// @file NodeMasks.h + +#ifndef OPENVDB_UTIL_NODEMASKS_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_NODEMASKS_HAS_BEEN_INCLUDED + +#include // for std::min() +#include +#include +#include // for cout +#include +#include +//#include +//#include // for ffs + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +/// Return the number of on bits in the given 8-bit value. +inline Index32 +CountOn(Byte v) +{ +#if defined(OPENVDB_USE_SSE42) && defined(_MSC_VER) + return __popcnt16(v); +#elif defined(OPENVDB_USE_SSE42) && (defined(__GNUC__) || defined(__clang__)) + return __builtin_popcount(v); +#else + // Software Implementation - Simple LUT + static const Byte numBits[256] = { +#define COUNTONB2(n) n, n+1, n+1, n+2 +#define COUNTONB4(n) COUNTONB2(n), COUNTONB2(n+1), COUNTONB2(n+1), COUNTONB2(n+2) +#define COUNTONB6(n) COUNTONB4(n), COUNTONB4(n+1), COUNTONB4(n+1), COUNTONB4(n+2) + COUNTONB6(0), COUNTONB6(1), COUNTONB6(1), COUNTONB6(2) + }; + return numBits[v]; +#undef COUNTONB6 +#undef COUNTONB4 +#undef COUNTONB2 +#endif +} + +/// Return the number of off bits in the given 8-bit value. +inline Index32 CountOff(Byte v) { return CountOn(static_cast(~v)); } + +/// Return the number of on bits in the given 32-bit value. +inline Index32 +CountOn(Index32 v) +{ + v = v - ((v >> 1) & 0x55555555U); + v = (v & 0x33333333U) + ((v >> 2) & 0x33333333U); + return (((v + (v >> 4)) & 0xF0F0F0FU) * 0x1010101U) >> 24; +} + +/// Return the number of off bits in the given 32-bit value. +inline Index32 CountOff(Index32 v) { return CountOn(~v); } + +/// Return the number of on bits in the given 64-bit value. +inline Index32 +CountOn(Index64 v) +{ +#if defined(OPENVDB_USE_SSE42) && defined(_MSC_VER) && defined(_M_X64) + v = __popcnt64(v); +#elif defined(OPENVDB_USE_SSE42) && (defined(__GNUC__) || defined(__clang__)) + v = __builtin_popcountll(v); +#else + // Software Implementation + v = v - ((v >> 1) & UINT64_C(0x5555555555555555)); + v = (v & UINT64_C(0x3333333333333333)) + ((v >> 2) & UINT64_C(0x3333333333333333)); + v = (((v + (v >> 4)) & UINT64_C(0xF0F0F0F0F0F0F0F)) * UINT64_C(0x101010101010101)) >> 56; +#endif + return static_cast(v); +} + +/// Return the number of off bits in the given 64-bit value. +inline Index32 CountOff(Index64 v) { return CountOn(~v); } + +/// Return the least significant on bit of the given 8-bit value. +inline Index32 +FindLowestOn(Byte v) +{ + assert(v); +#if defined(OPENVDB_USE_SSE42) && defined(_MSC_VER) + unsigned long index; + _BitScanForward(&index, static_cast(v)); + return static_cast(index); +#elif defined(OPENVDB_USE_SSE42) && (defined(__GNUC__) || defined(__clang__)) + return __builtin_ctz(v); +#else + // Software Implementation + static const Byte DeBruijn[8] = {0, 1, 6, 2, 7, 5, 4, 3}; + return DeBruijn[Byte((v & -v) * 0x1DU) >> 5]; +#endif +} + +/// Return the least significant on bit of the given 32-bit value. +inline Index32 +FindLowestOn(Index32 v) +{ + assert(v); + //return ffs(v); + static const Byte DeBruijn[32] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + return DeBruijn[Index32((v & -v) * 0x077CB531U) >> 27]; +} + +/// Return the least significant on bit of the given 64-bit value. +inline Index32 +FindLowestOn(Index64 v) +{ + assert(v); +#if defined(OPENVDB_USE_SSE42) && defined(_MSC_VER) + unsigned long index; + _BitScanForward64(&index, v); + return static_cast(index); +#elif defined(OPENVDB_USE_SSE42) && (defined(__GNUC__) || defined(__clang__)) + return static_cast(__builtin_ctzll(v)); +#else + // Software Implementation + static const Byte DeBruijn[64] = { + 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, + 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, + 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, + 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12, + }; + return DeBruijn[Index64((v & -v) * UINT64_C(0x022FDD63CC95386D)) >> 58]; +#endif +} + +/// Return the most significant on bit of the given 32-bit value. +inline Index32 +FindHighestOn(Index32 v) +{ + static const Byte DeBruijn[32] = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + v |= v >> 1; // first round down to one less than a power of 2 + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return DeBruijn[Index32(v * 0x07C4ACDDU) >> 27]; +} + + +//////////////////////////////////////// + + +/// Base class for the bit mask iterators +template +class BaseMaskIterator +{ +protected: + Index32 mPos; // bit position + const NodeMask* mParent; // this iterator can't change the parent_mask! + +public: + BaseMaskIterator(): mPos(NodeMask::SIZE), mParent(nullptr) {} + BaseMaskIterator(const BaseMaskIterator&) = default; + BaseMaskIterator(Index32 pos, const NodeMask* parent): mPos(pos), mParent(parent) + { + assert((parent == nullptr && pos == 0) || (parent != nullptr && pos <= NodeMask::SIZE)); + } + bool operator==(const BaseMaskIterator &iter) const {return mPos == iter.mPos;} + bool operator!=(const BaseMaskIterator &iter) const {return mPos != iter.mPos;} + bool operator< (const BaseMaskIterator &iter) const {return mPos < iter.mPos;} + BaseMaskIterator& operator=(const BaseMaskIterator& iter) + { + mPos = iter.mPos; mParent = iter.mParent; return *this; + } + Index32 offset() const { return mPos; } + Index32 pos() const { return mPos; } + bool test() const { assert(mPos <= NodeMask::SIZE); return (mPos != NodeMask::SIZE); } + operator bool() const { return this->test(); } +}; // class BaseMaskIterator + + +/// @note This happens to be a const-iterator! +template +class OnMaskIterator: public BaseMaskIterator +{ +private: + using BaseType = BaseMaskIterator; + using BaseType::mPos;//bit position; + using BaseType::mParent;//this iterator can't change the parent_mask! +public: + OnMaskIterator() : BaseType() {} + OnMaskIterator(Index32 pos,const NodeMask *parent) : BaseType(pos,parent) {} + void increment() + { + assert(mParent != nullptr); + mPos = mParent->findNextOn(mPos+1); + assert(mPos <= NodeMask::SIZE); + } + void increment(Index n) { while(n-- && this->next()) ; } + bool next() + { + this->increment(); + return this->test(); + } + bool operator*() const {return true;} + OnMaskIterator& operator++() + { + this->increment(); + return *this; + } +}; // class OnMaskIterator + + +template +class OffMaskIterator: public BaseMaskIterator +{ +private: + using BaseType = BaseMaskIterator; + using BaseType::mPos;//bit position; + using BaseType::mParent;//this iterator can't change the parent_mask! +public: + OffMaskIterator() : BaseType() {} + OffMaskIterator(Index32 pos,const NodeMask *parent) : BaseType(pos,parent) {} + void increment() + { + assert(mParent != nullptr); + mPos=mParent->findNextOff(mPos+1); + assert(mPos <= NodeMask::SIZE); + } + void increment(Index n) { while(n-- && this->next()) ; } + bool next() + { + this->increment(); + return this->test(); + } + bool operator*() const {return false;} + OffMaskIterator& operator++() + { + this->increment(); + return *this; + } +}; // class OffMaskIterator + + +template +class DenseMaskIterator: public BaseMaskIterator +{ +private: + using BaseType = BaseMaskIterator; + using BaseType::mPos;//bit position; + using BaseType::mParent;//this iterator can't change the parent_mask! + +public: + DenseMaskIterator() : BaseType() {} + DenseMaskIterator(Index32 pos,const NodeMask *parent) : BaseType(pos,parent) {} + void increment() + { + assert(mParent != nullptr); + mPos += 1;//careful - the increment might go beyond the end + assert(mPos<= NodeMask::SIZE); + } + void increment(Index n) { while(n-- && this->next()) ; } + bool next() + { + this->increment(); + return this->test(); + } + bool operator*() const {return mParent->isOn(mPos);} + DenseMaskIterator& operator++() + { + this->increment(); + return *this; + } +}; // class DenseMaskIterator + + +/// @brief Bit mask for the internal and leaf nodes of VDB. This +/// is a 64-bit implementation. +/// +/// @note A template specialization for Log2Dim=1 and Log2Dim=2 are +/// given below. +template +class NodeMask +{ +public: + static_assert(Log2Dim > 2, "expected NodeMask template specialization, got base template"); + + static const Index32 LOG2DIM = Log2Dim; + static const Index32 DIM = 1<> 6;// 2^6=64 + using Word = Index64; + +private: + + // The bits are represented as a linear array of Words, and the + // size of a Word is 32 or 64 bits depending on the platform. + // The BIT_MASK is defined as the number of bits in a Word - 1 + //static const Index32 BIT_MASK = sizeof(void*) == 8 ? 63 : 31; + //static const Index32 LOG2WORD = BIT_MASK == 63 ? 6 : 5; + //static const Index32 WORD_COUNT = SIZE >> LOG2WORD; + //using Word = boost::mpl::if_c::type; + + Word mWords[WORD_COUNT];//only member data! + +public: + /// Default constructor sets all bits off + NodeMask() { this->setOff(); } + /// All bits are set to the specified state + NodeMask(bool on) { this->set(on); } + /// Copy constructor + NodeMask(const NodeMask &other) { *this = other; } + /// Destructor + ~NodeMask() {} + /// Assignment operator + NodeMask& operator=(const NodeMask& other) + { + Index32 n = WORD_COUNT; + const Word* w2 = other.mWords; + for (Word* w1 = mWords; n--; ++w1, ++w2) *w1 = *w2; + return *this; + } + + using OnIterator = OnMaskIterator; + using OffIterator = OffMaskIterator; + using DenseIterator = DenseMaskIterator; + + OnIterator beginOn() const { return OnIterator(this->findFirstOn(),this); } + OnIterator endOn() const { return OnIterator(SIZE,this); } + OffIterator beginOff() const { return OffIterator(this->findFirstOff(),this); } + OffIterator endOff() const { return OffIterator(SIZE,this); } + DenseIterator beginDense() const { return DenseIterator(0,this); } + DenseIterator endDense() const { return DenseIterator(SIZE,this); } + + bool operator == (const NodeMask &other) const + { + int n = WORD_COUNT; + for (const Word *w1=mWords, *w2=other.mWords; n-- && *w1++ == *w2++;) ; + return n == -1; + } + + bool operator != (const NodeMask &other) const { return !(*this == other); } + + // + // Bitwise logical operations + // + + /// @brief Apply a functor to the words of the this and the other mask. + /// + /// @details An example that implements the "operator&=" method: + /// @code + /// struct Op { inline void operator()(W &w1, const W& w2) const { w1 &= w2; } }; + /// @endcode + template + const NodeMask& foreach(const NodeMask& other, const WordOp& op) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) op( *w1, *w2); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const WordOp& op) + { + Word *w1 = mWords; + const Word *w2 = other1.mWords, *w3 = other2.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2, ++w3) op( *w1, *w2, *w3); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const NodeMask& other3, + const WordOp& op) + { + Word *w1 = mWords; + const Word *w2 = other1.mWords, *w3 = other2.mWords, *w4 = other3.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2, ++w3, ++w4) op( *w1, *w2, *w3, *w4); + return *this; + } + /// @brief Bitwise intersection + const NodeMask& operator&=(const NodeMask& other) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) *w1 &= *w2; + return *this; + } + /// @brief Bitwise union + const NodeMask& operator|=(const NodeMask& other) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) *w1 |= *w2; + return *this; + } + /// @brief Bitwise difference + const NodeMask& operator-=(const NodeMask& other) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) *w1 &= ~*w2; + return *this; + } + /// @brief Bitwise XOR + const NodeMask& operator^=(const NodeMask& other) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) *w1 ^= *w2; + return *this; + } + NodeMask operator!() const { NodeMask m(*this); m.toggle(); return m; } + NodeMask operator&(const NodeMask& other) const { NodeMask m(*this); m &= other; return m; } + NodeMask operator|(const NodeMask& other) const { NodeMask m(*this); m |= other; return m; } + NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; } + + /// Return the byte size of this NodeMask + static Index32 memUsage() { return static_cast(WORD_COUNT*sizeof(Word)); } + /// Return the total number of on bits + Index32 countOn() const + { + Index32 sum = 0, n = WORD_COUNT; + for (const Word* w = mWords; n--; ++w) sum += CountOn(*w); + return sum; + } + /// Return the total number of on bits + Index32 countOff() const { return SIZE-this->countOn(); } + /// Set the nth bit on + void setOn(Index32 n) { + assert( (n >> 6) < WORD_COUNT ); + mWords[n >> 6] |= Word(1) << (n & 63); + } + /// Set the nth bit off + void setOff(Index32 n) { + assert( (n >> 6) < WORD_COUNT ); + mWords[n >> 6] &= ~(Word(1) << (n & 63)); + } + /// Set the nth bit to the specified state + void set(Index32 n, bool On) { On ? this->setOn(n) : this->setOff(n); } + /// Set all bits to the specified state + void set(bool on) + { + const Word state = on ? ~Word(0) : Word(0); + Index32 n = WORD_COUNT; + for (Word* w = mWords; n--; ++w) *w = state; + } + /// Set all bits on + void setOn() + { + Index32 n = WORD_COUNT; + for (Word* w = mWords; n--; ++w) *w = ~Word(0); + } + /// Set all bits off + void setOff() + { + Index32 n = WORD_COUNT; + for (Word* w = mWords; n--; ++w) *w = Word(0); + } + /// Toggle the state of the nth bit + void toggle(Index32 n) { + assert( (n >> 6) < WORD_COUNT ); + mWords[n >> 6] ^= Word(1) << (n & 63); + } + /// Toggle the state of all bits in the mask + void toggle() + { + Index32 n = WORD_COUNT; + for (Word* w = mWords; n--; ++w) *w = ~*w; + } + /// Set the first bit on + void setFirstOn() { this->setOn(0); } + /// Set the last bit on + void setLastOn() { this->setOn(SIZE-1); } + /// Set the first bit off + void setFirstOff() { this->setOff(0); } + /// Set the last bit off + void setLastOff() { this->setOff(SIZE-1); } + /// Return @c true if the nth bit is on + bool isOn(Index32 n) const + { + assert( (n >> 6) < WORD_COUNT ); + return 0 != (mWords[n >> 6] & (Word(1) << (n & 63))); + } + /// Return @c true if the nth bit is off + bool isOff(Index32 n) const {return !this->isOn(n); } + /// Return @c true if all the bits are on + bool isOn() const + { + int n = WORD_COUNT; + for (const Word *w = mWords; n-- && *w++ == ~Word(0);) ; + return n == -1; + } + /// Return @c true if all the bits are off + bool isOff() const + { + int n = WORD_COUNT; + for (const Word *w = mWords; n-- && *w++ == Word(0);) ; + return n == -1; + } + /// Return @c true if bits are either all off OR all on. + /// @param isOn Takes on the values of all bits if the method + /// returns true - else it is undefined. + bool isConstant(bool &isOn) const + { + isOn = (mWords[0] == ~Word(0));//first word has all bits on + if ( !isOn && mWords[0] != Word(0)) return false;//early out + const Word *w = mWords + 1, *n = mWords + WORD_COUNT; + while( wn
th word of the bit mask, for a word of arbitrary size. + template + WordT getWord(Index n) const + { + assert(n*8*sizeof(WordT) < SIZE); + return reinterpret_cast(mWords)[n]; + } + template + WordT& getWord(Index n) + { + assert(n*8*sizeof(WordT) < SIZE); + return reinterpret_cast(mWords)[n]; + } + //@} + + void save(std::ostream& os) const + { + os.write(reinterpret_cast(mWords), this->memUsage()); + } + void load(std::istream& is) { is.read(reinterpret_cast(mWords), this->memUsage()); } + void seek(std::istream& is) const { is.seekg(this->memUsage(), std::ios_base::cur); } + /// @brief simple print method for debugging + void printInfo(std::ostream& os=std::cout) const + { + os << "NodeMask: Dim=" << DIM << " Log2Dim=" << Log2Dim + << " Bit count=" << SIZE << " word count=" << WORD_COUNT << std::endl; + } + void printBits(std::ostream& os=std::cout, Index32 max_out=80u) const + { + const Index32 n=(SIZE>max_out ? max_out : SIZE); + for (Index32 i=0; i < n; ++i) { + if ( !(i & 63) ) + os << "||"; + else if ( !(i%8) ) + os << "|"; + os << this->isOn(i); + } + os << "|" << std::endl; + } + void printAll(std::ostream& os=std::cout, Index32 max_out=80u) const + { + this->printInfo(os); + this->printBits(os, max_out); + } + + Index32 findNextOn(Index32 start) const + { + Index32 n = start >> 6;//initiate + if (n >= WORD_COUNT) return SIZE; // check for out of bounds + Index32 m = start & 63; + Word b = mWords[n]; + if (b & (Word(1) << m)) return start;//simpel case: start is on + b &= ~Word(0) << m;// mask out lower bits + while(!b && ++n> 6;//initiate + if (n >= WORD_COUNT) return SIZE; // check for out of bounds + Index32 m = start & 63; + Word b = ~mWords[n]; + if (b & (Word(1) << m)) return start;//simpel case: start is on + b &= ~Word(0) << m;// mask out lower bits + while(!b && ++n +class NodeMask<1> +{ +public: + + static const Index32 LOG2DIM = 1; + static const Index32 DIM = 2; + static const Index32 SIZE = 8; + static const Index32 WORD_COUNT = 1; + using Word = Byte; + +private: + + Byte mByte;//only member data! + +public: + /// Default constructor sets all bits off + NodeMask() : mByte(0x00U) {} + /// All bits are set to the specified state + NodeMask(bool on) : mByte(on ? 0xFFU : 0x00U) {} + /// Copy constructor + NodeMask(const NodeMask &other) : mByte(other.mByte) {} + /// Destructor + ~NodeMask() {} + /// Assignment operator + void operator = (const NodeMask &other) { mByte = other.mByte; } + + using OnIterator = OnMaskIterator; + using OffIterator = OffMaskIterator; + using DenseIterator = DenseMaskIterator; + + OnIterator beginOn() const { return OnIterator(this->findFirstOn(),this); } + OnIterator endOn() const { return OnIterator(SIZE,this); } + OffIterator beginOff() const { return OffIterator(this->findFirstOff(),this); } + OffIterator endOff() const { return OffIterator(SIZE,this); } + DenseIterator beginDense() const { return DenseIterator(0,this); } + DenseIterator endDense() const { return DenseIterator(SIZE,this); } + + bool operator == (const NodeMask &other) const { return mByte == other.mByte; } + + bool operator != (const NodeMask &other) const {return mByte != other.mByte; } + + // + // Bitwise logical operations + // + + /// @brief Apply a functor to the words of the this and the other mask. + /// + /// @details An example that implements the "operator&=" method: + /// @code + /// struct Op { inline void operator()(Word &w1, const Word& w2) const { w1 &= w2; } }; + /// @endcode + template + const NodeMask& foreach(const NodeMask& other, const WordOp& op) + { + op(mByte, other.mByte); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const WordOp& op) + { + op(mByte, other1.mByte, other2.mByte); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const NodeMask& other3, + const WordOp& op) + { + op(mByte, other1.mByte, other2.mByte, other3.mByte); + return *this; + } + /// @brief Bitwise intersection + const NodeMask& operator&=(const NodeMask& other) + { + mByte &= other.mByte; + return *this; + } + /// @brief Bitwise union + const NodeMask& operator|=(const NodeMask& other) + { + mByte |= other.mByte; + return *this; + } + /// @brief Bitwise difference + const NodeMask& operator-=(const NodeMask& other) + { + mByte &= static_cast(~other.mByte); + return *this; + } + /// @brief Bitwise XOR + const NodeMask& operator^=(const NodeMask& other) + { + mByte ^= other.mByte; + return *this; + } + NodeMask operator!() const { NodeMask m(*this); m.toggle(); return m; } + NodeMask operator&(const NodeMask& other) const { NodeMask m(*this); m &= other; return m; } + NodeMask operator|(const NodeMask& other) const { NodeMask m(*this); m |= other; return m; } + NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; } + /// Return the byte size of this NodeMask + static Index32 memUsage() { return 1; } + /// Return the total number of on bits + Index32 countOn() const { return CountOn(mByte); } + /// Return the total number of on bits + Index32 countOff() const { return CountOff(mByte); } + /// Set the nth bit on + void setOn(Index32 n) { + assert( n < 8 ); + mByte = static_cast(mByte | 0x01U << (n & 7)); + } + /// Set the nth bit off + void setOff(Index32 n) { + assert( n < 8 ); + mByte = static_cast(mByte & ~(0x01U << (n & 7))); + } + /// Set the nth bit to the specified state + void set(Index32 n, bool On) { On ? this->setOn(n) : this->setOff(n); } + /// Set all bits to the specified state + void set(bool on) { mByte = on ? 0xFFU : 0x00U; } + /// Set all bits on + void setOn() { mByte = 0xFFU; } + /// Set all bits off + void setOff() { mByte = 0x00U; } + /// Toggle the state of the nth bit + void toggle(Index32 n) { + assert( n < 8 ); + mByte = static_cast(mByte ^ 0x01U << (n & 7)); + } + /// Toggle the state of all bits in the mask + void toggle() { mByte = static_cast(~mByte); } + /// Set the first bit on + void setFirstOn() { this->setOn(0); } + /// Set the last bit on + void setLastOn() { this->setOn(7); } + /// Set the first bit off + void setFirstOff() { this->setOff(0); } + /// Set the last bit off + void setLastOff() { this->setOff(7); } + /// Return true if the nth bit is on + bool isOn(Index32 n) const + { + assert( n < 8 ); + return mByte & (0x01U << (n & 7)); + } + /// Return true if the nth bit is off + bool isOff(Index32 n) const {return !this->isOn(n); } + /// Return true if all the bits are on + bool isOn() const { return mByte == 0xFFU; } + /// Return true if all the bits are off + bool isOff() const { return mByte == 0; } + /// Return @c true if bits are either all off OR all on. + /// @param isOn Takes on the values of all bits if the method + /// returns true - else it is undefined. + bool isConstant(bool &isOn) const + { + isOn = this->isOn(); + return isOn || this->isOff(); + } + Index32 findFirstOn() const { return mByte ? FindLowestOn(mByte) : 8; } + Index32 findFirstOff() const + { + const Byte b = static_cast(~mByte); + return b ? FindLowestOn(b) : 8; + } + /* + //@{ + /// Return the nth word of the bit mask, for a word of arbitrary size. + /// @note This version assumes WordT=Byte and n=0! + template + WordT getWord(Index n) const + { + static_assert(sizeof(WordT) == sizeof(Byte), "expected word size to be one byte"); + assert(n == 0); + return reinterpret_cast(mByte); + } + template + WordT& getWord(Index n) + { + static_assert(sizeof(WordT) == sizeof(Byte), "expected word size to be one byte"); + assert(n == 0); + return reinterpret_cast(mByte); + } + //@} + */ + void save(std::ostream& os) const { os.write(reinterpret_cast(&mByte), 1); } + void load(std::istream& is) { is.read(reinterpret_cast(&mByte), 1); } + void seek(std::istream& is) const { is.seekg(1, std::ios_base::cur); } + /// @brief simple print method for debugging + void printInfo(std::ostream& os=std::cout) const + { + os << "NodeMask: Dim=2, Log2Dim=1, Bit count=8, Word count=1"<isOn(i); + os << "||" << std::endl; + } + void printAll(std::ostream& os=std::cout) const + { + this->printInfo(os); + this->printBits(os); + } + + Index32 findNextOn(Index32 start) const + { + if (start>=8) return 8; + const Byte b = static_cast(mByte & (0xFFU << start)); + return b ? FindLowestOn(b) : 8; + } + + Index32 findNextOff(Index32 start) const + { + if (start>=8) return 8; + const Byte b = static_cast(~mByte & (0xFFU << start)); + return b ? FindLowestOn(b) : 8; + } + +};// NodeMask<1> + + +/// @brief Template specialization of NodeMask for Log2Dim=2, i.e. 4^3 nodes +template<> +class NodeMask<2> +{ +public: + + static const Index32 LOG2DIM = 2; + static const Index32 DIM = 4; + static const Index32 SIZE = 64; + static const Index32 WORD_COUNT = 1; + using Word = Index64; + +private: + + Word mWord;//only member data! + +public: + /// Default constructor sets all bits off + NodeMask() : mWord(UINT64_C(0x00)) {} + /// All bits are set to the specified state + NodeMask(bool on) : mWord(on ? UINT64_C(0xFFFFFFFFFFFFFFFF) : UINT64_C(0x00)) {} + /// Copy constructor + NodeMask(const NodeMask &other) : mWord(other.mWord) {} + /// Destructor + ~NodeMask() {} + /// Assignment operator + void operator = (const NodeMask &other) { mWord = other.mWord; } + + using OnIterator = OnMaskIterator; + using OffIterator = OffMaskIterator; + using DenseIterator = DenseMaskIterator; + + OnIterator beginOn() const { return OnIterator(this->findFirstOn(),this); } + OnIterator endOn() const { return OnIterator(SIZE,this); } + OffIterator beginOff() const { return OffIterator(this->findFirstOff(),this); } + OffIterator endOff() const { return OffIterator(SIZE,this); } + DenseIterator beginDense() const { return DenseIterator(0,this); } + DenseIterator endDense() const { return DenseIterator(SIZE,this); } + + bool operator == (const NodeMask &other) const { return mWord == other.mWord; } + + bool operator != (const NodeMask &other) const {return mWord != other.mWord; } + + // + // Bitwise logical operations + // + + /// @brief Apply a functor to the words of the this and the other mask. + /// + /// @details An example that implements the "operator&=" method: + /// @code + /// struct Op { inline void operator()(Word &w1, const Word& w2) const { w1 &= w2; } }; + /// @endcode + template + const NodeMask& foreach(const NodeMask& other, const WordOp& op) + { + op(mWord, other.mWord); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const WordOp& op) + { + op(mWord, other1.mWord, other2.mWord); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const NodeMask& other3, + const WordOp& op) + { + op(mWord, other1.mWord, other2.mWord, other3.mWord); + return *this; + } + /// @brief Bitwise intersection + const NodeMask& operator&=(const NodeMask& other) + { + mWord &= other.mWord; + return *this; + } + /// @brief Bitwise union + const NodeMask& operator|=(const NodeMask& other) + { + mWord |= other.mWord; + return *this; + } + /// @brief Bitwise difference + const NodeMask& operator-=(const NodeMask& other) + { + mWord &= ~other.mWord; + return *this; + } + /// @brief Bitwise XOR + const NodeMask& operator^=(const NodeMask& other) + { + mWord ^= other.mWord; + return *this; + } + NodeMask operator!() const { NodeMask m(*this); m.toggle(); return m; } + NodeMask operator&(const NodeMask& other) const { NodeMask m(*this); m &= other; return m; } + NodeMask operator|(const NodeMask& other) const { NodeMask m(*this); m |= other; return m; } + NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; } + /// Return the byte size of this NodeMask + static Index32 memUsage() { return 8; } + /// Return the total number of on bits + Index32 countOn() const { return CountOn(mWord); } + /// Return the total number of on bits + Index32 countOff() const { return CountOff(mWord); } + /// Set the nth bit on + void setOn(Index32 n) { + assert( n < 64 ); + mWord |= UINT64_C(0x01) << (n & 63); + } + /// Set the nth bit off + void setOff(Index32 n) { + assert( n < 64 ); + mWord &= ~(UINT64_C(0x01) << (n & 63)); + } + /// Set the nth bit to the specified state + void set(Index32 n, bool On) { On ? this->setOn(n) : this->setOff(n); } + /// Set all bits to the specified state + void set(bool on) { mWord = on ? UINT64_C(0xFFFFFFFFFFFFFFFF) : UINT64_C(0x00); } + /// Set all bits on + void setOn() { mWord = UINT64_C(0xFFFFFFFFFFFFFFFF); } + /// Set all bits off + void setOff() { mWord = UINT64_C(0x00); } + /// Toggle the state of the nth bit + void toggle(Index32 n) { + assert( n < 64 ); + mWord ^= UINT64_C(0x01) << (n & 63); + } + /// Toggle the state of all bits in the mask + void toggle() { mWord = ~mWord; } + /// Set the first bit on + void setFirstOn() { this->setOn(0); } + /// Set the last bit on + void setLastOn() { this->setOn(63); } + /// Set the first bit off + void setFirstOff() { this->setOff(0); } + /// Set the last bit off + void setLastOff() { this->setOff(63); } + /// Return true if the nth bit is on + bool isOn(Index32 n) const + { + assert( n < 64 ); + return 0 != (mWord & (UINT64_C(0x01) << (n & 63))); + } + /// Return true if the nth bit is off + bool isOff(Index32 n) const {return !this->isOn(n); } + /// Return true if all the bits are on + bool isOn() const { return mWord == UINT64_C(0xFFFFFFFFFFFFFFFF); } + /// Return true if all the bits are off + bool isOff() const { return mWord == 0; } + /// Return @c true if bits are either all off OR all on. + /// @param isOn Takes on the values of all bits if the method + /// returns true - else it is undefined. + bool isConstant(bool &isOn) const + { isOn = this->isOn(); + return isOn || this->isOff(); + } + Index32 findFirstOn() const { return mWord ? FindLowestOn(mWord) : 64; } + Index32 findFirstOff() const + { + const Word w = ~mWord; + return w ? FindLowestOn(w) : 64; + } + //@{ + /// Return the nth word of the bit mask, for a word of arbitrary size. + template + WordT getWord(Index n) const + { + assert(n*8*sizeof(WordT) < SIZE); + return reinterpret_cast(&mWord)[n]; + } + template + WordT& getWord(Index n) + { + assert(n*8*sizeof(WordT) < SIZE); + return reinterpret_cast(mWord)[n]; + } + //@} + void save(std::ostream& os) const { os.write(reinterpret_cast(&mWord), 8); } + void load(std::istream& is) { is.read(reinterpret_cast(&mWord), 8); } + void seek(std::istream& is) const { is.seekg(8, std::ios_base::cur); } + /// @brief simple print method for debugging + void printInfo(std::ostream& os=std::cout) const + { + os << "NodeMask: Dim=4, Log2Dim=2, Bit count=64, Word count=1"<isOn(i); + } + os << "||" << std::endl; + } + void printAll(std::ostream& os=std::cout) const + { + this->printInfo(os); + this->printBits(os); + } + + Index32 findNextOn(Index32 start) const + { + if (start>=64) return 64; + const Word w = mWord & (UINT64_C(0xFFFFFFFFFFFFFFFF) << start); + return w ? FindLowestOn(w) : 64; + } + + Index32 findNextOff(Index32 start) const + { + if (start>=64) return 64; + const Word w = ~mWord & (UINT64_C(0xFFFFFFFFFFFFFFFF) << start); + return w ? FindLowestOn(w) : 64; + } + +};// NodeMask<2> + + +// Unlike NodeMask above this RootNodeMask has a run-time defined size. +// It is only included for backward compatibility and will likely be +// deprecated in the future! +// This class is 32-bit specefic, hence the use if Index32 vs Index! +class RootNodeMask +{ +protected: + Index32 mBitSize, mIntSize; + Index32 *mBits; + +public: + RootNodeMask(): mBitSize(0), mIntSize(0), mBits(nullptr) {} + RootNodeMask(Index32 bit_size): + mBitSize(bit_size), mIntSize(((bit_size-1)>>5)+1), mBits(new Index32[mIntSize]) + { + for (Index32 i=0; i>5)+1; + delete [] mBits; + mBits = new Index32[mIntSize]; + for (Index32 i=0; igetBitSize()), mParent(parent) { assert(pos <= mBitSize); } + bool operator==(const BaseIterator &iter) const {return mPos == iter.mPos;} + bool operator!=(const BaseIterator &iter) const {return mPos != iter.mPos;} + bool operator< (const BaseIterator &iter) const {return mPos < iter.mPos;} + BaseIterator& operator=(const BaseIterator& iter) { + mPos = iter.mPos; + mBitSize = iter.mBitSize; + mParent = iter.mParent; + return *this; + } + + Index32 offset() const {return mPos;} + + Index32 pos() const {return mPos;} + + bool test() const { + assert(mPos <= mBitSize); + return (mPos != mBitSize); + } + + operator bool() const {return this->test();} + }; // class BaseIterator + + /// @note This happens to be a const-iterator! + class OnIterator: public BaseIterator + { + protected: + using BaseIterator::mPos;//bit position; + using BaseIterator::mBitSize;//bit size; + using BaseIterator::mParent;//this iterator can't change the parent_mask! + public: + OnIterator() : BaseIterator() {} + OnIterator(Index32 pos,const RootNodeMask *parent) : BaseIterator(pos,parent) {} + void increment() { + assert(mParent != nullptr); + mPos=mParent->findNextOn(mPos+1); + assert(mPos <= mBitSize); + } + void increment(Index n) { + for (Index i=0; inext(); ++i) {} + } + bool next() { + this->increment(); + return this->test(); + } + bool operator*() const {return true;} + OnIterator& operator++() { + this->increment(); + return *this; + } + }; // class OnIterator + + class OffIterator: public BaseIterator + { + protected: + using BaseIterator::mPos;//bit position; + using BaseIterator::mBitSize;//bit size; + using BaseIterator::mParent;//this iterator can't change the parent_mask! + public: + OffIterator() : BaseIterator() {} + OffIterator(Index32 pos,const RootNodeMask *parent) : BaseIterator(pos,parent) {} + void increment() { + assert(mParent != nullptr); + mPos=mParent->findNextOff(mPos+1); + assert(mPos <= mBitSize); + } + void increment(Index n) { + for (Index i=0; inext(); ++i) {} + } + bool next() { + this->increment(); + return this->test(); + } + bool operator*() const {return true;} + OffIterator& operator++() { + this->increment(); + return *this; + } + }; // class OffIterator + + class DenseIterator: public BaseIterator + { + protected: + using BaseIterator::mPos;//bit position; + using BaseIterator::mBitSize;//bit size; + using BaseIterator::mParent;//this iterator can't change the parent_mask! + public: + DenseIterator() : BaseIterator() {} + DenseIterator(Index32 pos,const RootNodeMask *parent) : BaseIterator(pos,parent) {} + void increment() { + assert(mParent != nullptr); + mPos += 1;//carefull - the increament might go beyond the end + assert(mPos<= mBitSize); + } + void increment(Index n) { + for (Index i=0; inext(); ++i) {} + } + bool next() { + this->increment(); + return this->test(); + } + bool operator*() const {return mParent->isOn(mPos);} + DenseIterator& operator++() { + this->increment(); + return *this; + } + }; // class DenseIterator + + OnIterator beginOn() const { return OnIterator(this->findFirstOn(),this); } + OnIterator endOn() const { return OnIterator(mBitSize,this); } + OffIterator beginOff() const { return OffIterator(this->findFirstOff(),this); } + OffIterator endOff() const { return OffIterator(mBitSize,this); } + DenseIterator beginDense() const { return DenseIterator(0,this); } + DenseIterator endDense() const { return DenseIterator(mBitSize,this); } + + bool operator == (const RootNodeMask &B) const { + if (mBitSize != B.mBitSize) return false; + for (Index32 i=0; i(mIntSize*sizeof(Index32) + sizeof(*this)); + } + + Index32 countOn() const { + assert(mBits); + Index32 n=0; + for (Index32 i=0; i< mIntSize; ++i) n += CountOn(mBits[i]); + return n; + } + + Index32 countOff() const { return mBitSize-this->countOn(); } + + void setOn(Index32 i) { + assert(mBits); + assert( (i>>5) < mIntSize); + mBits[i>>5] |= 1<<(i&31); + } + + void setOff(Index32 i) { + assert(mBits); + assert( (i>>5) < mIntSize); + mBits[i>>5] &= ~(1<<(i&31)); + } + + void set(Index32 i, bool On) { On ? this->setOn(i) : this->setOff(i); } + + void setOn() { + assert(mBits); + for (Index32 i=0; i>5) < mIntSize); + mBits[i>>5] ^= 1<<(i&31); + } + void toggle() { + assert(mBits); + for (Index32 i=0; isetOn(0); } + void setLastOn() { this->setOn(mBitSize-1); } + void setFirstOff() { this->setOff(0); } + void setLastOff() { this->setOff(mBitSize-1); } + bool isOn(Index32 i) const { + assert(mBits); + assert( (i>>5) < mIntSize); + return ( mBits[i >> 5] & (1<<(i&31)) ); + } + bool isOff(Index32 i) const { + assert(mBits); + assert( (i>>5) < mIntSize); + return ( ~mBits[i >> 5] & (1<<(i&31)) ); + } + + bool isOn() const { + if (!mBits) return false;//undefined is off + for (Index32 i=0; i(mBits), mIntSize * sizeof(Index32)); + } + void load(std::istream& is) { + assert(mBits); + is.read(reinterpret_cast(mBits), mIntSize * sizeof(Index32)); + } + void seek(std::istream& is) const { + assert(mBits); + is.seekg(mIntSize * sizeof(Index32), std::ios_base::cur); + } + /// @brief simple print method for debugging + void printInfo(std::ostream& os=std::cout) const { + os << "RootNodeMask: Bit-size="<isOn(i); + } + os << "|" << std::endl; + } + + void printAll(std::ostream& os=std::cout, Index32 max_out=80u) const { + this->printInfo(os); + this->printBits(os,max_out); + } + + Index32 findNextOn(Index32 start) const { + assert(mBits); + Index32 n = start >> 5, m = start & 31;//initiate + if (n>=mIntSize) return mBitSize; // check for out of bounds + Index32 b = mBits[n]; + if (b & (1<> 5, m = start & 31;//initiate + if (n>=mIntSize) return mBitSize; // check for out of bounds + Index32 b = ~mBits[n]; + if (b & (1<(sizeof(Index32*)+(2+mIntSize)*sizeof(Index32));//in bytes + } +}; // class RootNodeMask + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_NODEMASKS_HAS_BEEN_INCLUDED diff --git a/openvdb/util/NullInterrupter.h b/openvdb/util/NullInterrupter.h new file mode 100644 index 00000000..6f2da9f4 --- /dev/null +++ b/openvdb/util/NullInterrupter.h @@ -0,0 +1,59 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file NullInterrupter.h + +#ifndef OPENVDB_UTIL_NULL_INTERRUPTER_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_NULL_INTERRUPTER_HAS_BEEN_INCLUDED + +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +/// @brief Dummy NOOP interrupter class defining interface +/// +/// This shows the required interface for the @c InterrupterType template argument +/// using by several threaded applications (e.g. tools/PointAdvect.h). The host +/// application calls start() at the beginning of an interruptible operation, end() +/// at the end of the operation, and wasInterrupted() periodically during the operation. +/// If any call to wasInterrupted() returns @c true, the operation will be aborted. +/// @note This Dummy interrupter will NEVER interrupt since wasInterrupted() always +/// returns false! +struct NullInterrupter +{ + /// Default constructor + NullInterrupter () {} + /// Signal the start of an interruptible operation. + /// @param name an optional descriptive name for the operation + void start(const char* name = nullptr) { (void)name; } + /// Signal the end of an interruptible operation. + void end() {} + /// Check if an interruptible operation should be aborted. + /// @param percent an optional (when >= 0) percentage indicating + /// the fraction of the operation that has been completed + /// @note this method is assumed to be thread-safe. The current + /// implementation is clearly a NOOP and should compile out during + /// optimization! + inline bool wasInterrupted(int percent = -1) { (void)percent; return false; } +}; + +/// This method allows NullInterrupter::wasInterrupted to be compiled +/// out when client code only has a pointer (vs reference) to the interrupter. +/// +/// @note This is a free-standing function since C++ doesn't allow for +/// partial template specialization (in client code of the interrupter). +template +inline bool wasInterrupted(T* i, int percent = -1) { return i && i->wasInterrupted(percent); } + +/// Specialization for NullInterrupter +template<> +inline bool wasInterrupted(util::NullInterrupter*, int) { return false; } + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_NULL_INTERRUPTER_HAS_BEEN_INCLUDED diff --git a/openvdb/util/PagedArray.h b/openvdb/util/PagedArray.h new file mode 100644 index 00000000..9a8cf82a --- /dev/null +++ b/openvdb/util/PagedArray.h @@ -0,0 +1,749 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +/// +/// @file PagedArray.h +/// +/// @author Ken Museth +/// +/// @brief Concurrent, page-based, dynamically-sized linear data +/// structure with O(1) random access and STL-compliant +/// iterators. It is primarily intended for applications +/// that involve multi-threading push_back of (a possibly +/// unkown number of) elements into a dynamically growing +/// linear array, and fast random access to said elements. + +#ifndef OPENVDB_UTIL_PAGED_ARRAY_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_PAGED_ARRAY_HAS_BEEN_INCLUDED + +#include +#include // SharedPtr +#include +#include +#include +#include // std::swap +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +//////////////////////////////////////// + + +/// @brief Concurrent, page-based, dynamically-sized linear data structure +/// with O(1) random access and STL-compliant iterators. It is +/// primarily intended for applications that concurrently insert +/// (a possibly unkown number of) elements into a dynamically +/// growing linear array, and fast random access to said elements. +/// +/// @note Multiple threads can grow the page-table and push_back +/// new elements concurrently. A ValueBuffer provides accelerated +/// and threadsafe push_back at the cost of potentially re-ordering +/// elements (when multiple instances are used). +/// +/// @details This data structure employes contiguous pages of elements +/// (a std::deque) which avoids moving data when the +/// capacity is out-grown and new pages are allocated. The +/// size of the pages can be controlled with the Log2PageSize +/// template parameter (defaults to 1024 elements of type ValueT). +/// +/// There are three fundamentally different ways to insert elements to +/// this container - each with different advanteges and disadvanteges. +/// +/// The simplest way to insert elements is to use PagedArray::push_back e.g. +/// @code +/// PagedArray array; +/// for (size_t i=0; i<100000; ++i) array.push_back(i); +/// @endcode +/// or with TBB task-based multi-threading +/// @code +/// PagedArray array; +/// tbb::parallel_for( +/// tbb::blocked_range(0, 10000, array.pageSize()), +/// [&array](const tbb::blocked_range& range) { +/// for (size_t i=range.begin(); i!=range.end(); ++i) array.push_back(i); +/// } +/// ); +/// @endcode +/// PagedArray::push_back has the advantage that it's thread-safe and +/// preserves the ordering of the inserted elements. In fact it returns +/// the linear offset to the added element which can then be used for +/// fast O(1) random access. The disadvantage is it's the slowest of +/// the three different ways of inserting elements. +/// +/// The fastest way (by far) to insert elements by means of a PagedArray::ValueBuffer, e.g. +/// @code +/// PagedArray array; +/// PagedArray::ValueBuffer buffer(array); +/// for (size_t i=0; i<100000; ++i) buffer.push_back(i); +/// buffer.flush(); +/// @endcode +/// or +/// @code +/// PagedArray array; +/// { +/// //local scope of a single thread +/// PagedArray::ValueBuffer buffer(array); +/// for (size_t i=0; i<100000; ++i) buffer.push_back(i); +/// } +/// @endcode +/// or with TBB task-based multi-threading +/// @code +/// PagedArray array; +/// tbb::parallel_for( +/// tbb::blocked_range(0, 10000, array.pageSize()), +/// [&array](const tbb::blocked_range& range) { +/// PagedArray::ValueBuffer buffer(array); +/// for (size_t i=range.begin(); i!=range.end(); ++i) buffer.push_back(i); +/// } +/// ); +/// @endcode +/// or with TBB thread-local storage for even better performance (due +/// to fewer concurrent instantiations of partially full ValueBuffers) +/// @code +/// PagedArray array; +/// PagedArray::ValueBuffer exemplar(array);//dummy used for initialization +/// tbb::enumerable_thread_specific::ValueBuffer> +/// pool(exemplar);//thread local storage pool of ValueBuffers +/// tbb::parallel_for( +/// tbb::blocked_range(0, 10000, array.pageSize()), +/// [&pool](const tbb::blocked_range& range) { +/// PagedArray::ValueBuffer &buffer = pool.local(); +/// for (size_t i=range.begin(); i!=range.end(); ++i) buffer.push_back(i); +/// } +/// ); +/// for (auto i=pool.begin(); i!=pool.end(); ++i) i->flush(); +/// @endcode +/// This technique generally outperforms PagedArray::push_back, +/// std::vector::push_back, std::deque::push_back and even +/// tbb::concurrent_vector::push_back. Additionally it +/// is thread-safe as long as each thread has it's own instance of a +/// PagedArray::ValueBuffer. The only disadvantage is the ordering of +/// the elements is undefined if multiple instance of a +/// PagedArray::ValueBuffer are employed. This is typically the case +/// in the context of multi-threading, where the +/// ordering of inserts are undefined anyway. Note that a local scope +/// can be used to guarentee that the ValueBuffer has inserted all its +/// elements by the time the scope ends. Alternatively the ValueBuffer +/// can be explicitly flushed by calling ValueBuffer::flush. +/// +/// The third way to insert elements is to resize the container and use +/// random access, e.g. +/// @code +/// PagedArray array; +/// array.resize(100000); +/// for (int i=0; i<100000; ++i) array[i] = i; +/// @endcode +/// or in terms of the random access iterator +/// @code +/// PagedArray array; +/// array.resize(100000); +/// for (auto i=array.begin(); i!=array.end(); ++i) *i = i.pos(); +/// @endcode +/// While this approach is both fast and thread-safe it suffers from the +/// major disadvantage that the problem size, i.e. number of elements, needs to +/// be known in advance. If that's the case you might as well consider +/// using std::vector or a raw c-style array! In other words the +/// PagedArray is most useful in the context of applications that +/// involve multi-threading of dynamically growing linear arrays that +/// require fast random access. + +template +class PagedArray +{ +private: + class Page; + + // must allow mutiple threads to call operator[] as long as only one thread calls push_back + using PageTableT = std::deque; + +public: + using ValueType = ValueT; + using Ptr = SharedPtr; + + /// @brief Default constructor + PagedArray() : mCapacity{0} { mSize = 0; } + + /// @brief Destructor removed all allocated pages + ~PagedArray() { this->clear(); } + + // Disallow copy construction and assignment + PagedArray(const PagedArray&) = delete; + PagedArray& operator=(const PagedArray&) = delete; + + /// @brief Return a shared pointer to a new instance of this class + static Ptr create() { return Ptr(new PagedArray); } + + /// @brief Caches values into a local memory Page to improve + /// performance of push_back into a PagedArray. + /// + /// @note The ordering of inserted elements is undefined when + /// multiple ValueBuffers are used! + /// + /// @warning By design this ValueBuffer is not threadsafe so + /// make sure to create an instance per thread! + class ValueBuffer; + + /// Const std-compliant iterator + class ConstIterator; + + /// Non-const std-compliant iterator + class Iterator; + + /// @brief Thread safe insertion, adds a new element at + /// the end and increases the container size by one and + /// returns the linear offset for the inserted element. + /// + /// @param value value to be added to this PagedArray + /// + /// @details Constant time complexity. May allocate a new page. + size_t push_back(const ValueType& value) + { + const size_t index = mSize.fetch_and_increment(); + if (index >= mCapacity) this->grow(index); + (*mPageTable[index >> Log2PageSize])[index] = value; + return index; + } + + /// @brief Slightly faster than the thread-safe push_back above. + /// + /// @param value value to be added to this PagedArray + /// + /// @note For best performance consider using the ValueBuffer! + /// + /// @warning Not thread-safe! + size_t push_back_unsafe(const ValueType& value) + { + const size_t index = mSize.fetch_and_increment(); + if (index >= mCapacity) { + mPageTable.push_back( new Page() ); + mCapacity += Page::Size; + } + (*mPageTable[index >> Log2PageSize])[index] = value; + return index; + } + + /// @brief Reduce the page table to fix the current size. + /// + /// @warning Not thread-safe! + void shrink_to_fit(); + + /// @brief Return a reference to the value at the specified offset + /// + /// @param i linear offset of the value to be accessed. + /// + /// @note This random access has constant time complexity. + /// + /// @warning It is assumed that the i'th element is already allocated! + ValueType& operator[](size_t i) + { + assert(i>Log2PageSize])[i]; + } + + /// @brief Return a const-reference to the value at the specified offset + /// + /// @param i linear offset of the value to be accessed. + /// + /// @note This random access has constant time complexity. + /// + /// @warning It is assumed that the i'th element is already allocated! + const ValueType& operator[](size_t i) const + { + assert(i>Log2PageSize])[i]; + } + + /// @brief Set all elements in the page table to the specified value + /// + /// @param v value to be filled in all the existing pages of this PagedArray. + /// + /// @note Multi-threaded + void fill(const ValueType& v) + { + auto op = [&](const tbb::blocked_range& r){ + for(size_t i=r.begin(); i!=r.end(); ++i) mPageTable[i]->fill(v); + }; + tbb::parallel_for(tbb::blocked_range(0, this->pageCount()), op); + } + + /// @brief Copy the first @a count values in this PageArray into + /// a raw c-style array, assuming it to be at least @a count + /// elements long. + /// + /// @param p pointer to an array that will used as the destination of the copy. + /// @param count number of elements to be copied. + /// + bool copy(ValueType *p, size_t count) const + { + size_t last_page = count >> Log2PageSize; + if (last_page >= this->pageCount()) return false; + auto op = [&](const tbb::blocked_range& r){ + for (size_t i=r.begin(); i!=r.end(); ++i) { + mPageTable[i]->copy(p+i*Page::Size, Page::Size); + } + }; + if (size_t m = count & Page::Mask) {//count is not divisible by page size + tbb::parallel_for(tbb::blocked_range(0, last_page, 32), op); + mPageTable[last_page]->copy(p+last_page*Page::Size, m); + } else { + tbb::parallel_for(tbb::blocked_range(0, last_page+1, 32), op); + } + return true; + } + void copy(ValueType *p) const { this->copy(p, mSize); } + + /// @brief Resize this array to the specified size. + /// + /// @param size number of elements that this PageArray will contain. + /// + /// @details Will grow or shrink the page table to contain + /// the specified number of elements. It will affect the size(), + /// iteration will go over all those elements, push_back will + /// insert after them and operator[] can be used directly access + /// them. + /// + /// @note No reserve method is implemented due to efficiency concerns + /// (especially for the ValueBuffer) from having to deal with empty pages. + /// + /// @warning Not thread-safe! + void resize(size_t size) + { + mSize = size; + if (size > mCapacity) { + this->grow(size-1); + } else { + this->shrink_to_fit(); + } + } + + /// @brief Resize this array to the specified size and initialize + /// all values to @a v. + /// + /// @param size number of elements that this PageArray will contain. + /// @param v value of all the @a size values. + /// + /// @details Will grow or shrink the page table to contain + /// the specified number of elements. It will affect the size(), + /// iteration will go over all those elements, push_back will + /// insert after them and operator[] can be used directly access them. + /// + /// @note No reserve method is implemented due to efficiency concerns + /// (especially for the ValueBuffer) from having to deal with empty pages. + /// + /// @warning Not thread-safe! + void resize(size_t size, const ValueType& v) + { + this->resize(size); + this->fill(v); + } + + /// @brief Return the number of elements in this array. + size_t size() const { return mSize; } + + /// @brief Return the maximum number of elements that this array + /// can contain without allocating more memory pages. + size_t capacity() const { return mCapacity; } + + /// @brief Return the number of additional elements that can be + /// added to this array without allocating more memory pages. + size_t freeCount() const { return mCapacity - mSize; } + + /// @brief Return the number of allocated memory pages. + size_t pageCount() const { return mPageTable.size(); } + + /// @brief Return the number of elements per memory page. + static size_t pageSize() { return Page::Size; } + + /// @brief Return log2 of the number of elements per memory page. + static size_t log2PageSize() { return Log2PageSize; } + + /// @brief Return the memory footprint of this array in bytes. + size_t memUsage() const + { + return sizeof(*this) + mPageTable.size() * Page::memUsage(); + } + + /// @brief Return true if the container contains no elements. + bool isEmpty() const { return mSize == 0; } + + /// @brief Return true if the page table is partially full, i.e. the + /// last non-empty page contains less than pageSize() elements. + /// + /// @details When the page table is partially full calling merge() + /// or using a ValueBuffer will rearrange the ordering of + /// existing elements. + bool isPartiallyFull() const { return (mSize & Page::Mask) > 0; } + + /// @brief Removes all elements from the array and delete all pages. + /// + /// @warning Not thread-safe! + void clear() + { + for (size_t i=0, n=mPageTable.size(); ibegin(), this->end(), std::less() ); } + + /// @brief Parallel sort of all the elements in descending order. + void invSort() { tbb::parallel_sort(this->begin(), this->end(), std::greater()); } + + //@{ + /// @brief Parallel sort of all the elements based on a custom + /// functor with the api: + /// @code bool operator()(const ValueT& a, const ValueT& b) @endcode + /// which returns true if a comes before b. + template + void sort(Functor func) { tbb::parallel_sort(this->begin(), this->end(), func ); } + //@} + + /// @brief Transfer all the elements (and pages) from the other array to this array. + /// + /// @param other non-const reference to the PagedArray that will be merged into this PagedArray. + /// + /// @note The other PagedArray is empty on return. + /// + /// @warning The ordering of elements is undefined if this page table is partially full! + void merge(PagedArray& other); + + /// @brief Print information for debugging + void print(std::ostream& os = std::cout) const + { + os << "PagedArray:\n" + << "\tSize: " << this->size() << " elements\n" + << "\tPage table: " << this->pageCount() << " pages\n" + << "\tPage size: " << this->pageSize() << " elements\n" + << "\tCapacity: " << this->capacity() << " elements\n" + << "\tFootprint: " << this->memUsage() << " bytes\n"; + } + +private: + + friend class ValueBuffer; + + void grow(size_t index) + { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + while(index >= mCapacity) { + mPageTable.push_back( new Page() ); + mCapacity += Page::Size; + } + } + + void add_full(Page*& page, size_t size); + + void add_partially_full(Page*& page, size_t size); + + void add(Page*& page, size_t size) { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + if (size == Page::Size) {//page is full + this->add_full(page, size); + } else if (size>0) {//page is only partially full + this->add_partially_full(page, size); + } + } + PageTableT mPageTable;//holds points to allocated pages + tbb::atomic mSize;// current number of elements in array + size_t mCapacity;//capacity of array given the current page count + tbb::spin_mutex mGrowthMutex;//Mutex-lock required to grow pages +}; // Public class PagedArray + +//////////////////////////////////////////////////////////////////////////////// + +template +void PagedArray::shrink_to_fit() +{ + if (mPageTable.size() > (mSize >> Log2PageSize) + 1) { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + const size_t pageCount = (mSize >> Log2PageSize) + 1; + if (mPageTable.size() > pageCount) { + delete mPageTable.back(); + mPageTable.pop_back(); + mCapacity -= Page::Size; + } + } +} + +template +void PagedArray::merge(PagedArray& other) +{ + if (&other != this && !other.isEmpty()) { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + // extract last partially full page if it exists + Page* page = nullptr; + const size_t size = mSize & Page::Mask; //number of elements in the last page + if ( size > 0 ) { + page = mPageTable.back(); + mPageTable.pop_back(); + mSize -= size; + } + // transfer all pages from the other page table + mPageTable.insert(mPageTable.end(), other.mPageTable.begin(), other.mPageTable.end()); + mSize += other.mSize; + mCapacity = Page::Size*mPageTable.size(); + other.mSize = 0; + other.mCapacity = 0; + PageTableT().swap(other.mPageTable); + // add back last partially full page + if (page) this->add_partially_full(page, size); + } +} + +template +void PagedArray::add_full(Page*& page, size_t size) +{ + assert(size == Page::Size);//page must be full + if (mSize & Page::Mask) {//page-table is partially full + Page*& tmp = mPageTable.back(); + std::swap(tmp, page);//swap last table entry with page + } + mPageTable.push_back(page); + mCapacity += Page::Size; + mSize += size; + page = nullptr; +} + +template +void PagedArray::add_partially_full(Page*& page, size_t size) +{ + assert(size > 0 && size < Page::Size);//page must be partially full + if (size_t m = mSize & Page::Mask) {//page table is also partially full + ValueT *s = page->data(), *t = mPageTable.back()->data() + m; + for (size_t i=std::min(mSize+size, mCapacity)-mSize; i; --i) *t++ = *s++; + if (mSize+size > mCapacity) {//grow page table + mPageTable.push_back( new Page() ); + t = mPageTable.back()->data(); + for (size_t i=mSize+size-mCapacity; i; --i) *t++ = *s++; + mCapacity += Page::Size; + } + } else {//page table is full so simply append page + mPageTable.push_back( page ); + mCapacity += Page::Size; + page = nullptr; + } + mSize += size; +} + +//////////////////////////////////////////////////////////////////////////////// + +// Public member-class of PagedArray +template +class PagedArray:: +ValueBuffer +{ +public: + using PagedArrayType = PagedArray; + /// @brief Constructor from a PageArray + ValueBuffer(PagedArray& parent) : mParent(&parent), mPage(new Page()), mSize(0) {} + /// @warning This copy-constructor is shallow in the sense that no + /// elements are copied, i.e. size = 0. + ValueBuffer(const ValueBuffer& other) : mParent(other.mParent), mPage(new Page()), mSize(0) {} + /// @brief Destructor that transfers an buffered values to the parent PagedArray. + ~ValueBuffer() { mParent->add(mPage, mSize); delete mPage; } + + ValueBuffer& operator=(const ValueBuffer&) = delete;// disallow copy assignment + + /// @brief Add a value to the buffer and increment the size. + /// + /// @details If the internal memory page is full it will + /// automaically flush the page to the parent PagedArray. + void push_back(const ValueT& v) { + (*mPage)[mSize++] = v; + if (mSize == Page::Size) this->flush(); + } + /// @brief Manually transfers the values in this buffer to the parent PagedArray. + /// + /// @note This method is also called by the destructor and + /// push_back so it should only be called if one manually wants to + /// sync up the buffer with the array, e.g. during debugging. + void flush() { + mParent->add(mPage, mSize); + if (mPage == nullptr) mPage = new Page(); + mSize = 0; + } + /// @brief Return a reference to the parent PagedArray + PagedArrayType& parent() const { return *mParent; } + /// @brief Return the current number of elements cached in this buffer. + size_t size() const { return mSize; } +private: + PagedArray* mParent; + Page* mPage; + size_t mSize; +};// Public class PagedArray::ValueBuffer + +//////////////////////////////////////////////////////////////////////////////// + +// Const std-compliant iterator +// Public member-class of PagedArray +template +class PagedArray:: +ConstIterator : public std::iterator +{ +public: + using BaseT = std::iterator; + using difference_type = typename BaseT::difference_type; + // constructors and assignment + ConstIterator() : mPos(0), mParent(nullptr) {} + ConstIterator(const PagedArray& parent, size_t pos=0) : mPos(pos), mParent(&parent) {} + ConstIterator(const ConstIterator& other) : mPos(other.mPos), mParent(other.mParent) {} + ConstIterator& operator=(const ConstIterator& other) { + mPos=other.mPos; + mParent=other.mParent; + return *this; + } + // prefix + ConstIterator& operator++() { ++mPos; return *this; } + ConstIterator& operator--() { --mPos; return *this; } + // postfix + ConstIterator operator++(int) { ConstIterator tmp(*this); ++mPos; return tmp; } + ConstIterator operator--(int) { ConstIterator tmp(*this); --mPos; return tmp; } + // value access + const ValueT& operator*() const { return (*mParent)[mPos]; } + const ValueT* operator->() const { return &(this->operator*()); } + const ValueT& operator[](const difference_type& pos) const { return (*mParent)[mPos+pos]; } + // offset + ConstIterator& operator+=(const difference_type& pos) { mPos += pos; return *this; } + ConstIterator& operator-=(const difference_type& pos) { mPos -= pos; return *this; } + ConstIterator operator+(const difference_type &pos) const { return Iterator(*mParent,mPos+pos); } + ConstIterator operator-(const difference_type &pos) const { return Iterator(*mParent,mPos-pos); } + difference_type operator-(const ConstIterator& other) const { return mPos - other.pos(); } + // comparisons + bool operator==(const ConstIterator& other) const { return mPos == other.mPos; } + bool operator!=(const ConstIterator& other) const { return mPos != other.mPos; } + bool operator>=(const ConstIterator& other) const { return mPos >= other.mPos; } + bool operator<=(const ConstIterator& other) const { return mPos <= other.mPos; } + bool operator< (const ConstIterator& other) const { return mPos < other.mPos; } + bool operator> (const ConstIterator& other) const { return mPos > other.mPos; } + // non-std methods + bool isValid() const { return mParent != nullptr && mPos < mParent->size(); } + size_t pos() const { return mPos; } +private: + size_t mPos; + const PagedArray* mParent; +};// Public class PagedArray::ConstIterator + +//////////////////////////////////////////////////////////////////////////////// + +// Non-const std-compliant iterator +// Public member-class of PagedArray +template +class PagedArray:: +Iterator : public std::iterator +{ +public: + using BaseT = std::iterator; + using difference_type = typename BaseT::difference_type; + // constructors and assignment + Iterator() : mPos(0), mParent(nullptr) {} + Iterator(PagedArray& parent, size_t pos=0) : mPos(pos), mParent(&parent) {} + Iterator(const Iterator& other) : mPos(other.mPos), mParent(other.mParent) {} + Iterator& operator=(const Iterator& other) { + mPos=other.mPos; + mParent=other.mParent; + return *this; + } + // prefix + Iterator& operator++() { ++mPos; return *this; } + Iterator& operator--() { --mPos; return *this; } + // postfix + Iterator operator++(int) { Iterator tmp(*this); ++mPos; return tmp; } + Iterator operator--(int) { Iterator tmp(*this); --mPos; return tmp; } + // value access + ValueT& operator*() const { return (*mParent)[mPos]; } + ValueT* operator->() const { return &(this->operator*()); } + ValueT& operator[](const difference_type& pos) const { return (*mParent)[mPos+pos]; } + // offset + Iterator& operator+=(const difference_type& pos) { mPos += pos; return *this; } + Iterator& operator-=(const difference_type& pos) { mPos -= pos; return *this; } + Iterator operator+(const difference_type &pos) const { return Iterator(*mParent, mPos+pos); } + Iterator operator-(const difference_type &pos) const { return Iterator(*mParent, mPos-pos); } + difference_type operator-(const Iterator& other) const { return mPos - other.pos(); } + // comparisons + bool operator==(const Iterator& other) const { return mPos == other.mPos; } + bool operator!=(const Iterator& other) const { return mPos != other.mPos; } + bool operator>=(const Iterator& other) const { return mPos >= other.mPos; } + bool operator<=(const Iterator& other) const { return mPos <= other.mPos; } + bool operator< (const Iterator& other) const { return mPos < other.mPos; } + bool operator> (const Iterator& other) const { return mPos > other.mPos; } + // non-std methods + bool isValid() const { return mParent != nullptr && mPos < mParent->size(); } + size_t pos() const { return mPos; } + private: + size_t mPos; + PagedArray* mParent; +};// Public class PagedArray::Iterator + +//////////////////////////////////////////////////////////////////////////////// + +// Private member-class of PagedArray implementing a memory page +template +class PagedArray:: +Page +{ +public: + static const size_t Size = 1UL << Log2PageSize; + static const size_t Mask = Size - 1UL; + static size_t memUsage() { return sizeof(ValueT)*Size; } + // Raw memory allocation without any initialization + Page() : mData(reinterpret_cast(new char[sizeof(ValueT)*Size])) {} + ~Page() { delete [] mData; } + Page(const Page&) = delete;//copy construction is not implemented + Page& operator=(const Page&) = delete;//copy assignment is not implemented + ValueT& operator[](const size_t i) { return mData[i & Mask]; } + const ValueT& operator[](const size_t i) const { return mData[i & Mask]; } + void fill(const ValueT& v) { + ValueT* dst = mData; + for (size_t i=Size; i; --i) *dst++ = v; + } + ValueT* data() { return mData; } + // Copy the first n elements of this Page to dst (which is assumed to large + // enough to hold the n elements). + void copy(ValueType *dst, size_t n) const { + const ValueT* src = mData; + for (size_t i=n; i; --i) *dst++ = *src++; + } +protected: + ValueT* mData; +};// Private class PagedArray::Page + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_PAGED_ARRAY_HAS_BEEN_INCLUDED diff --git a/openvdb/util/Util.cc b/openvdb/util/Util.cc new file mode 100644 index 00000000..ec56bf80 --- /dev/null +++ b/openvdb/util/Util.cc @@ -0,0 +1,46 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Util.h" +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +const Index32 INVALID_IDX = std::numeric_limits::max(); + +const Coord COORD_OFFSETS[26] = +{ + Coord( 1, 0, 0), /// Voxel-face adjacent neghbours + Coord(-1, 0, 0), /// 0 to 5 + Coord( 0, 1, 0), + Coord( 0, -1, 0), + Coord( 0, 0, 1), + Coord( 0, 0, -1), + Coord( 1, 0, -1), /// Voxel-edge adjacent neghbours + Coord(-1, 0, -1), /// 6 to 17 + Coord( 1, 0, 1), + Coord(-1, 0, 1), + Coord( 1, 1, 0), + Coord(-1, 1, 0), + Coord( 1, -1, 0), + Coord(-1, -1, 0), + Coord( 0, -1, 1), + Coord( 0, -1, -1), + Coord( 0, 1, 1), + Coord( 0, 1, -1), + Coord(-1, -1, -1), /// Voxel-corner adjacent neghbours + Coord(-1, -1, 1), /// 18 to 25 + Coord( 1, -1, 1), + Coord( 1, -1, -1), + Coord(-1, 1, -1), + Coord(-1, 1, 1), + Coord( 1, 1, 1), + Coord( 1, 1, -1) +}; + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb/util/Util.h b/openvdb/util/Util.h new file mode 100644 index 00000000..b7eaee3b --- /dev/null +++ b/openvdb/util/Util.h @@ -0,0 +1,135 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_UTIL_UTIL_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_UTIL_HAS_BEEN_INCLUDED + +#include +#include +#include +#include // for tree::pruneInactive + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +OPENVDB_API extern const Index32 INVALID_IDX; + +/// @brief coordinate offset table for neighboring voxels +OPENVDB_API extern const Coord COORD_OFFSETS[26]; + + +//////////////////////////////////////// + + +/// Return @a voxelCoord rounded to the closest integer coordinates. +inline Coord +nearestCoord(const Vec3d& voxelCoord) +{ + Coord ijk; + ijk[0] = int(std::floor(voxelCoord[0])); + ijk[1] = int(std::floor(voxelCoord[1])); + ijk[2] = int(std::floor(voxelCoord[2])); + return ijk; +} + + +//////////////////////////////////////// + + +/// @brief Functor for use with tools::foreach() to compute the boolean intersection +/// between the value masks of corresponding leaf nodes in two trees +template +class LeafTopologyIntOp +{ +public: + LeafTopologyIntOp(const TreeType2& tree): mOtherTree(&tree) {} + + inline void operator()(const typename TreeType1::LeafIter& lIter) const + { + const Coord xyz = lIter->origin(); + const typename TreeType2::LeafNodeType* leaf = mOtherTree->probeConstLeaf(xyz); + if (leaf) {//leaf node + lIter->topologyIntersection(*leaf, zeroVal()); + } else if (!mOtherTree->isValueOn(xyz)) {//inactive tile + lIter->setValuesOff(); + } + } + +private: + const TreeType2* mOtherTree; +}; + + +/// @brief Functor for use with tools::foreach() to compute the boolean difference +/// between the value masks of corresponding leaf nodes in two trees +template +class LeafTopologyDiffOp +{ +public: + LeafTopologyDiffOp(const TreeType2& tree): mOtherTree(&tree) {} + + inline void operator()(const typename TreeType1::LeafIter& lIter) const + { + const Coord xyz = lIter->origin(); + const typename TreeType2::LeafNodeType* leaf = mOtherTree->probeConstLeaf(xyz); + if (leaf) {//leaf node + lIter->topologyDifference(*leaf, zeroVal()); + } else if (mOtherTree->isValueOn(xyz)) {//active tile + lIter->setValuesOff(); + } + } + +private: + const TreeType2* mOtherTree; +}; + + +//////////////////////////////////////// + + +/// @brief Perform a boolean intersection between two leaf nodes' topology masks. +/// @return a pointer to a new, boolean-valued tree containing the overlapping voxels. +template +inline typename TreeType1::template ValueConverter::Type::Ptr +leafTopologyIntersection(const TreeType1& lhs, const TreeType2& rhs, bool threaded = true) +{ + typedef typename TreeType1::template ValueConverter::Type BoolTreeType; + + typename BoolTreeType::Ptr topologyTree(new BoolTreeType( + lhs, /*inactiveValue=*/false, /*activeValue=*/true, TopologyCopy())); + + tools::foreach(topologyTree->beginLeaf(), + LeafTopologyIntOp(rhs), threaded); + + tools::pruneInactive(*topologyTree, threaded); + return topologyTree; +} + + +/// @brief Perform a boolean difference between two leaf nodes' topology masks. +/// @return a pointer to a new, boolean-valued tree containing the non-overlapping +/// voxels from the lhs. +template +inline typename TreeType1::template ValueConverter::Type::Ptr +leafTopologyDifference(const TreeType1& lhs, const TreeType2& rhs, bool threaded = true) +{ + typedef typename TreeType1::template ValueConverter::Type BoolTreeType; + + typename BoolTreeType::Ptr topologyTree(new BoolTreeType( + lhs, /*inactiveValue=*/false, /*activeValue=*/true, TopologyCopy())); + + tools::foreach(topologyTree->beginLeaf(), + LeafTopologyDiffOp(rhs), threaded); + + tools::pruneInactive(*topologyTree, threaded); + return topologyTree; +} + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_UTIL_HAS_BEEN_INCLUDED diff --git a/openvdb/util/logging.h b/openvdb/util/logging.h new file mode 100644 index 00000000..171d815c --- /dev/null +++ b/openvdb/util/logging.h @@ -0,0 +1,319 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED + +#include + +#ifdef OPENVDB_USE_LOG4CPLUS + +#include +#include +#include +#include +#include +#include +#include // for std::remove() +#include // for ::strrchr() +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace logging { + +/// @brief Message severity level +enum class Level { + Debug = log4cplus::DEBUG_LOG_LEVEL, + Info = log4cplus::INFO_LOG_LEVEL, + Warn = log4cplus::WARN_LOG_LEVEL, + Error = log4cplus::ERROR_LOG_LEVEL, + Fatal = log4cplus::FATAL_LOG_LEVEL +}; + + +namespace internal { + +/// @brief log4cplus layout that outputs text in different colors +/// for different log levels, using ANSI escape codes +class ColoredPatternLayout: public log4cplus::PatternLayout +{ +public: + explicit ColoredPatternLayout(const std::string& progName_, bool useColor = true) + : log4cplus::PatternLayout( + progName_.empty() ? std::string{"%5p: %m%n"} : (progName_ + " %5p: %m%n")) + , mUseColor(useColor) + , mProgName(progName_) + { + } + + ~ColoredPatternLayout() override {} + + const std::string& progName() const { return mProgName; } + + void formatAndAppend(log4cplus::tostream& strm, + const log4cplus::spi::InternalLoggingEvent& event) override + { + if (!mUseColor) { + log4cplus::PatternLayout::formatAndAppend(strm, event); + return; + } + log4cplus::tostringstream s; + switch (event.getLogLevel()) { + case log4cplus::DEBUG_LOG_LEVEL: s << "\033[32m"; break; // green + case log4cplus::ERROR_LOG_LEVEL: + case log4cplus::FATAL_LOG_LEVEL: s << "\033[31m"; break; // red + case log4cplus::INFO_LOG_LEVEL: s << "\033[36m"; break; // cyan + case log4cplus::WARN_LOG_LEVEL: s << "\033[35m"; break; // magenta + } + log4cplus::PatternLayout::formatAndAppend(s, event); + strm << s.str() << "\033[0m" << std::flush; + } + +// Disable deprecation warnings for std::auto_ptr. +#if defined(__ICC) + #pragma warning push + #pragma warning disable:1478 +#elif defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +#if defined(LOG4CPLUS_VERSION) && defined(LOG4CPLUS_MAKE_VERSION) + #if LOG4CPLUS_VERSION >= LOG4CPLUS_MAKE_VERSION(2, 0, 0) + // In log4cplus 2.0.0, std::auto_ptr was replaced with std::unique_ptr. + using Ptr = std::unique_ptr; + #else + using Ptr = std::auto_ptr; + #endif +#else + using Ptr = std::auto_ptr; +#endif + + static Ptr create(const std::string& progName_, bool useColor = true) + { + return Ptr{new ColoredPatternLayout{progName_, useColor}}; + } + +#if defined(__ICC) + #pragma warning pop +#elif defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) + #pragma GCC diagnostic pop +#endif + +private: + bool mUseColor = true; + std::string mProgName; +}; // class ColoredPatternLayout + + +inline log4cplus::Logger +getLogger() +{ + return log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("openvdb")); +} + + +inline log4cplus::SharedAppenderPtr +getAppender() +{ + return getLogger().getAppender(LOG4CPLUS_TEXT("OPENVDB")); +} + +} // namespace internal + + +/// @brief Return the current logging level. +inline Level +getLevel() +{ + switch (internal::getLogger().getLogLevel()) { + case log4cplus::DEBUG_LOG_LEVEL: return Level::Debug; + case log4cplus::INFO_LOG_LEVEL: return Level::Info; + case log4cplus::WARN_LOG_LEVEL: return Level::Warn; + case log4cplus::ERROR_LOG_LEVEL: return Level::Error; + case log4cplus::FATAL_LOG_LEVEL: break; + } + return Level::Fatal; +} + + +/// @brief Set the logging level. (Lower-level messages will be suppressed.) +inline void +setLevel(Level lvl) +{ + internal::getLogger().setLogLevel(static_cast(lvl)); +} + + +/// @brief If "-debug", "-info", "-warn", "-error" or "-fatal" is found +/// in the given array of command-line arguments, set the logging level +/// appropriately and remove the relevant argument(s) from the array. +inline void +setLevel(int& argc, char* argv[]) +{ + for (int i = 1; i < argc; ++i) { // note: skip argv[0] + const std::string arg{argv[i]}; + bool remove = true; + if (arg == "-debug") { setLevel(Level::Debug); } + else if (arg == "-error") { setLevel(Level::Error); } + else if (arg == "-fatal") { setLevel(Level::Fatal); } + else if (arg == "-info") { setLevel(Level::Info); } + else if (arg == "-warn") { setLevel(Level::Warn); } + else { remove = false; } + if (remove) argv[i] = nullptr; + } + auto end = std::remove(argv + 1, argv + argc, nullptr); + argc = static_cast(end - argv); +} + + +/// @brief Specify a program name to be displayed in log messages. +inline void +setProgramName(const std::string& progName, bool useColor = true) +{ + // Change the layout of the OpenVDB appender to use colored text + // and to incorporate the supplied program name. + if (auto appender = internal::getAppender()) { + appender->setLayout(internal::ColoredPatternLayout::create(progName, useColor)); + } +} + + +/// @brief Initialize the logging system if it is not already initialized. +inline void +initialize(bool useColor = true) +{ + log4cplus::initialize(); + + if (internal::getAppender()) return; // already initialized + + // Create the OpenVDB logger if it doesn't already exist. + auto logger = internal::getLogger(); + + // Disable "additivity", so that OpenVDB-related messages are directed + // to the OpenVDB logger only and are not forwarded up the logger tree. + logger.setAdditivity(false); + + // Attach a console appender to the OpenVDB logger. + if (auto appender = log4cplus::SharedAppenderPtr{new log4cplus::ConsoleAppender}) { + appender->setName(LOG4CPLUS_TEXT("OPENVDB")); + logger.addAppender(appender); + } + + setLevel(Level::Warn); + setProgramName("", useColor); +} + + +/// @brief Initialize the logging system from command-line arguments. +/// @details If "-debug", "-info", "-warn", "-error" or "-fatal" is found +/// in the given array of command-line arguments, set the logging level +/// appropriately and remove the relevant argument(s) from the array. +inline void +initialize(int& argc, char* argv[], bool useColor = true) +{ + initialize(); + + setLevel(argc, argv); + + auto progName = (argc > 0 ? argv[0] : ""); + if (const char* ptr = ::strrchr(progName, '/')) progName = ptr + 1; + setProgramName(progName, useColor); +} + +} // namespace logging +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#define OPENVDB_LOG(level, message) \ + do { \ + auto _log = openvdb::logging::internal::getLogger(); \ + if (_log.isEnabledFor(log4cplus::level##_LOG_LEVEL)) { \ + std::ostringstream _buf; \ + _buf << message; \ + _log.forcedLog(log4cplus::level##_LOG_LEVEL, _buf.str(), __FILE__, __LINE__); \ + } \ + } while (0); + +/// Log an info message of the form 'someVar << "some text" << ...'. +#define OPENVDB_LOG_INFO(message) OPENVDB_LOG(INFO, message) +/// Log a warning message of the form 'someVar << "some text" << ...'. +#define OPENVDB_LOG_WARN(message) OPENVDB_LOG(WARN, message) +/// Log an error message of the form 'someVar << "some text" << ...'. +#define OPENVDB_LOG_ERROR(message) OPENVDB_LOG(ERROR, message) +/// Log a fatal error message of the form 'someVar << "some text" << ...'. +#define OPENVDB_LOG_FATAL(message) OPENVDB_LOG(FATAL, message) +#ifdef DEBUG +/// In debug builds only, log a debugging message of the form 'someVar << "text" << ...'. +#define OPENVDB_LOG_DEBUG(message) OPENVDB_LOG(DEBUG, message) +#else +/// In debug builds only, log a debugging message of the form 'someVar << "text" << ...'. +#define OPENVDB_LOG_DEBUG(message) +#endif +/// @brief Log a debugging message in both debug and optimized builds. +/// @warning Don't use this in performance-critical code. +#define OPENVDB_LOG_DEBUG_RUNTIME(message) OPENVDB_LOG(DEBUG, message) + +#else // ifdef OPENVDB_USE_LOG4CPLUS + +#include + +#define OPENVDB_LOG_INFO(mesg) +#define OPENVDB_LOG_WARN(mesg) do { std::cerr << "WARNING: " << mesg << std::endl; } while (0); +#define OPENVDB_LOG_ERROR(mesg) do { std::cerr << "ERROR: " << mesg << std::endl; } while (0); +#define OPENVDB_LOG_FATAL(mesg) do { std::cerr << "FATAL: " << mesg << std::endl; } while (0); +#define OPENVDB_LOG_DEBUG(mesg) +#define OPENVDB_LOG_DEBUG_RUNTIME(mesg) + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace logging { + +enum class Level { Debug, Info, Warn, Error, Fatal }; + +inline Level getLevel() { return Level::Warn; } +inline void setLevel(Level) {} +inline void setLevel(int&, char*[]) {} +inline void setProgramName(const std::string&, bool = true) {} +inline void initialize() {} +inline void initialize(int&, char*[], bool = true) {} + +} // namespace logging +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_USE_LOG4CPLUS + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace logging { + +/// @brief A LevelScope object sets the logging level to a given level +/// and restores it to the current level when the object goes out of scope. +struct LevelScope +{ + Level level; + explicit LevelScope(Level newLevel): level(getLevel()) { setLevel(newLevel); } + ~LevelScope() { setLevel(level); } +}; + +} // namespace logging +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED diff --git a/openvdb/version.h b/openvdb/version.h new file mode 100644 index 00000000..2e662bff --- /dev/null +++ b/openvdb/version.h @@ -0,0 +1,219 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file version.h +/// @brief Library and file format version numbers +/// +/// @details +/// When the library is built with the latest ABI, its namespace has the form +/// openvdb::vX_Y, where @e X and @e Y are the major and minor version numbers. +/// +/// The library can be built using an older ABI by changing the value of the +/// @b OPENVDB_ABI_VERSION_NUMBER macro (e.g., via -DOPENVDB_ABI_VERSION_NUMBER=N). +/// In that case, the namespace has the form openvdb::vX_YabiN, +/// where N is the ABI version number. +/// The ABI version must be set consistently when building code that depends on OpenVDB. +/// +/// The ABI version number defaults to the library major version number, +/// which gets incremented whenever changes are made to the ABI of the +/// Grid class or related classes (Tree, Transform, Metadata, etc.). +/// Setting the ABI version number to an earlier library version number +/// disables grid ABI changes made since that library version. +/// The OpenVDB 1.x ABI is no longer supported, and support for other old ABIs +/// might also eventually be dropped. +/// +/// The library minor version number gets incremented whenever a change is made +/// to any aspect of the public API (not just the grid API) that necessitates +/// changes to client code. Changes to APIs in private or internal namespaces +/// do not trigger a minor version number increment; such APIs should not be used +/// in client code. +/// +/// A patch version number increment indicates a change—usually a new feature +/// or a bug fix—that does not necessitate changes to client code but rather +/// only recompilation of that code (because the library namespace incorporates +/// the version number). +/// +/// The file format version number gets incremented when it becomes possible +/// to write files that cannot safely be read with older versions of the library. +/// Not all files written in a newer format are incompatible with older libraries, however. +/// And in general, files containing grids of unknown type can be read safely, +/// although the unknown grids will not be accessible. + +#ifndef OPENVDB_VERSION_HAS_BEEN_INCLUDED +#define OPENVDB_VERSION_HAS_BEEN_INCLUDED + +#include "Platform.h" +#include // uint32_t + + +// Library major, minor and patch version numbers +#define OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER 7 +#define OPENVDB_LIBRARY_MINOR_VERSION_NUMBER 0 +#define OPENVDB_LIBRARY_PATCH_VERSION_NUMBER 0 + +// If OPENVDB_ABI_VERSION_NUMBER is already defined (e.g., via -DOPENVDB_ABI_VERSION_NUMBER=N) +// use that ABI version. Otherwise, use this library version's default ABI. +#ifdef OPENVDB_ABI_VERSION_NUMBER + #if OPENVDB_ABI_VERSION_NUMBER > OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER + #error expected OPENVDB_ABI_VERSION_NUMBER <= OPENVDB_LIBRARY_MAJOR VERSION_NUMBER + #endif +#else + // Older versions of the library used the macros OPENVDB_2_ABI_COMPATIBLE + // and OPENVDB_3_ABI_COMPATIBLE. For now, continue to support them. + #if defined OPENVDB_2_ABI_COMPATIBLE ///< @todo deprecated + #define OPENVDB_ABI_VERSION_NUMBER 2 + #elif defined OPENVDB_3_ABI_COMPATIBLE ///< @todo deprecated + #define OPENVDB_ABI_VERSION_NUMBER 3 + #else + #define OPENVDB_ABI_VERSION_NUMBER OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER + #endif +#endif + +// If using an OPENVDB_ABI_VERSION_NUMBER that has been deprecated, issue a message +// directive. Note that an error is also set in openvdb.cc which enforces stricter +// behavior during compilation of the library. Both can be optionally suppressed +// by defining OPENVDB_USE_DEPRECATED_ABI. +#ifndef OPENVDB_USE_DEPRECATED_ABI + #if OPENVDB_ABI_VERSION_NUMBER == 4 + PRAGMA(message("NOTE: ABI = 4 is deprecated, define OPENVDB_USE_DEPRECATED_ABI " + "to suppress this message")) + #endif +#endif + +#if OPENVDB_ABI_VERSION_NUMBER == OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER + /// @brief The version namespace name for this library version + /// @hideinitializer + /// + /// When the ABI version number matches the library major version number, + /// symbols are named as in the following examples: + /// - @b openvdb::vX_Y::Vec3i + /// - @b openvdb::vX_Y::io::File + /// - @b openvdb::vX_Y::tree::Tree + /// + /// where X and Y are the major and minor version numbers. + /// + /// When the ABI version number does not match the library major version number, + /// symbol names include the ABI version: + /// - @b openvdb::vX_YabiN::Vec3i + /// - @b openvdb::vX_YabiN::io::File + /// - @b openvdb::vX_YabiN::tree::Tree + /// + /// where X, Y and N are the major, minor and ABI version numbers, respectively. + #define OPENVDB_VERSION_NAME \ + OPENVDB_PREPROC_CONCAT(v, \ + OPENVDB_PREPROC_CONCAT(OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER, \ + OPENVDB_PREPROC_CONCAT(_, OPENVDB_LIBRARY_MINOR_VERSION_NUMBER))) +#else + // This duplication of code is necessary to avoid issues with recursive macro expansion. + #define OPENVDB_VERSION_NAME \ + OPENVDB_PREPROC_CONCAT(v, \ + OPENVDB_PREPROC_CONCAT(OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER, \ + OPENVDB_PREPROC_CONCAT(_, \ + OPENVDB_PREPROC_CONCAT(OPENVDB_LIBRARY_MINOR_VERSION_NUMBER, \ + OPENVDB_PREPROC_CONCAT(abi, OPENVDB_ABI_VERSION_NUMBER))))) +#endif + +/// @brief Library version number string of the form ".." +/// @details This is a macro rather than a static constant because we typically +/// want the compile-time version number, not the runtime version number +/// (although the two are usually the same). +/// @hideinitializer +#define OPENVDB_LIBRARY_VERSION_STRING \ + OPENVDB_PREPROC_STRINGIFY(OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER) "." \ + OPENVDB_PREPROC_STRINGIFY(OPENVDB_LIBRARY_MINOR_VERSION_NUMBER) "." \ + OPENVDB_PREPROC_STRINGIFY(OPENVDB_LIBRARY_PATCH_VERSION_NUMBER) + +/// @brief Library version number string of the form "..abi" +/// @details This is a macro rather than a static constant because we typically +/// want the compile-time version number, not the runtime version number +/// (although the two are usually the same). +/// @hideinitializer +#define OPENVDB_LIBRARY_ABI_VERSION_STRING \ + OPENVDB_LIBRARY_VERSION_STRING "abi" OPENVDB_PREPROC_STRINGIFY(OPENVDB_ABI_VERSION_NUMBER) + +/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) +#define OPENVDB_LIBRARY_VERSION_NUMBER \ + ((OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER << 24) | \ + ((OPENVDB_LIBRARY_MINOR_VERSION_NUMBER & 0xFF) << 16) | \ + (OPENVDB_LIBRARY_PATCH_VERSION_NUMBER & 0xFFFF)) + + +/// By default, the @b OPENVDB_REQUIRE_VERSION_NAME macro is undefined, and +/// symbols from the version namespace are promoted to the top-level namespace +/// so that, for example, @b openvdb::v5_0::io::File can be referred to +/// simply as @b openvdb::io::File. +/// +/// When @b OPENVDB_REQUIRE_VERSION_NAME is defined, symbols must be +/// fully namespace-qualified. +/// @hideinitializer +#ifdef OPENVDB_REQUIRE_VERSION_NAME +#define OPENVDB_USE_VERSION_NAMESPACE +#else +// The empty namespace clause below ensures that OPENVDB_VERSION_NAME +// is recognized as a namespace name. +#define OPENVDB_USE_VERSION_NAMESPACE \ + namespace OPENVDB_VERSION_NAME {} \ + using namespace OPENVDB_VERSION_NAME; +#endif + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +/// @brief The magic number is stored in the first four bytes of every VDB file. +/// @details This can be used to quickly test whether we have a valid file or not. +const int32_t OPENVDB_MAGIC = 0x56444220; + +// Library major, minor and patch version numbers +const uint32_t + OPENVDB_LIBRARY_MAJOR_VERSION = OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER, + OPENVDB_LIBRARY_MINOR_VERSION = OPENVDB_LIBRARY_MINOR_VERSION_NUMBER, + OPENVDB_LIBRARY_PATCH_VERSION = OPENVDB_LIBRARY_PATCH_VERSION_NUMBER; +/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) +const uint32_t OPENVDB_LIBRARY_VERSION = OPENVDB_LIBRARY_VERSION_NUMBER; +// ABI version number +const uint32_t OPENVDB_ABI_VERSION = OPENVDB_ABI_VERSION_NUMBER; + +/// @brief The current version number of the VDB file format +/// @details This can be used to enable various backwards compatibility switches +/// or to reject files that cannot be read. +const uint32_t OPENVDB_FILE_VERSION = 224; + +/// Notable file format version numbers +enum { + OPENVDB_FILE_VERSION_ROOTNODE_MAP = 213, + OPENVDB_FILE_VERSION_INTERNALNODE_COMPRESSION = 214, + OPENVDB_FILE_VERSION_SIMPLIFIED_GRID_TYPENAME = 215, + OPENVDB_FILE_VERSION_GRID_INSTANCING = 216, + OPENVDB_FILE_VERSION_BOOL_LEAF_OPTIMIZATION = 217, + OPENVDB_FILE_VERSION_BOOST_UUID = 218, + OPENVDB_FILE_VERSION_NO_GRIDMAP = 219, + OPENVDB_FILE_VERSION_NEW_TRANSFORM = 219, + OPENVDB_FILE_VERSION_SELECTIVE_COMPRESSION = 220, + OPENVDB_FILE_VERSION_FLOAT_FRUSTUM_BBOX = 221, + OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION = 222, + OPENVDB_FILE_VERSION_BLOSC_COMPRESSION = 223, + OPENVDB_FILE_VERSION_POINT_INDEX_GRID = 223, + OPENVDB_FILE_VERSION_MULTIPASS_IO = 224 +}; + + +/// Return a library version number string of the form "..". +inline constexpr const char* getLibraryVersionString() { return OPENVDB_LIBRARY_VERSION_STRING; } +/// Return a library version number string of the form "..abi". +inline constexpr const char* getLibraryAbiVersionString() { + return OPENVDB_LIBRARY_ABI_VERSION_STRING; +} + + +struct VersionId { + uint32_t first, second; + VersionId(): first(0), second(0) {} + VersionId(uint32_t major, uint32_t minor): first(major), second(minor) {} +}; + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_VERSION_HAS_BEEN_INCLUDED diff --git a/openvdb/viewer/Camera.cc b/openvdb/viewer/Camera.cc new file mode 100644 index 00000000..44215368 --- /dev/null +++ b/openvdb/viewer/Camera.cc @@ -0,0 +1,249 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Camera.h" + +#include + +#ifdef OPENVDB_USE_GLFW_3 +#define GLFW_INCLUDE_GLU +#include +#else // if !defined(OPENVDB_USE_GLFW_3) +#if defined(__APPLE__) || defined(MACOSX) +#include +#include +#else +#include +#include +#endif +#include +#endif // !defined(OPENVDB_USE_GLFW_3) + + +namespace openvdb_viewer { + +const double Camera::sDeg2rad = M_PI / 180.0; + + +Camera::Camera() + : mFov(65.0) + , mNearPlane(0.1) + , mFarPlane(10000.0) + , mTarget(openvdb::Vec3d(0.0)) + , mLookAt(mTarget) + , mUp(openvdb::Vec3d(0.0, 1.0, 0.0)) + , mForward(openvdb::Vec3d(0.0, 0.0, 1.0)) + , mRight(openvdb::Vec3d(1.0, 0.0, 0.0)) + , mEye(openvdb::Vec3d(0.0, 0.0, -1.0)) + , mTumblingSpeed(0.5) + , mZoomSpeed(0.2) + , mStrafeSpeed(0.05) + , mHead(30.0) + , mPitch(45.0) + , mTargetDistance(25.0) + , mDistance(mTargetDistance) + , mMouseDown(false) + , mStartTumbling(false) + , mZoomMode(false) + , mChanged(true) + , mNeedsDisplay(true) + , mMouseXPos(0.0) + , mMouseYPos(0.0) +#if GLFW_VERSION_MAJOR >= 3 + , mWindow(nullptr) +#endif +{ +} + + +void +Camera::lookAt(const openvdb::Vec3d& p, double dist) +{ + mLookAt = p; + mDistance = dist; + mNeedsDisplay = true; +} + + +void +Camera::lookAtTarget() +{ + mLookAt = mTarget; + mDistance = mTargetDistance; + mNeedsDisplay = true; +} + + +void +Camera::setSpeed(double zoomSpeed, double strafeSpeed, double tumblingSpeed) +{ + mZoomSpeed = std::max(0.0001, mDistance * zoomSpeed); + mStrafeSpeed = std::max(0.0001, mDistance * strafeSpeed); + mTumblingSpeed = std::max(0.2, mDistance * tumblingSpeed); + mTumblingSpeed = std::min(1.0, mDistance * tumblingSpeed); +} + + +void +Camera::setTarget(const openvdb::Vec3d& p, double dist) +{ + mTarget = p; + mTargetDistance = dist; +} + + +void +Camera::aim() +{ +#if GLFW_VERSION_MAJOR >= 3 + if (mWindow == nullptr) return; +#endif + + // Get the window size + int width, height; +#if GLFW_VERSION_MAJOR >= 3 + glfwGetFramebufferSize(mWindow, &width, &height); +#else + glfwGetWindowSize(&width, &height); +#endif + + // Make sure that height is non-zero to avoid division by zero + height = std::max(1, height); + + glViewport(0, 0, width, height); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Set up the projection matrix + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // Window aspect (assumes square pixels) + double aspectRatio = double(width) / double(height); + + // Set perspective view (fov is in degrees in the y direction.) + gluPerspective(mFov, aspectRatio, mNearPlane, mFarPlane); + + if (mChanged) { + + mChanged = false; + + mEye[0] = mLookAt[0] + mDistance * std::cos(mHead * sDeg2rad) * std::cos(mPitch * sDeg2rad); + mEye[1] = mLookAt[1] + mDistance * std::sin(mHead * sDeg2rad); + mEye[2] = mLookAt[2] + mDistance * std::cos(mHead * sDeg2rad) * std::sin(mPitch * sDeg2rad); + + mForward = mLookAt - mEye; + mForward.normalize(); + + mUp[1] = std::cos(mHead * sDeg2rad) > 0 ? 1.0 : -1.0; + mRight = mForward.cross(mUp); + } + + // Set up modelview matrix + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + gluLookAt(mEye[0], mEye[1], mEye[2], + mLookAt[0], mLookAt[1], mLookAt[2], + mUp[0], mUp[1], mUp[2]); + mNeedsDisplay = false; +} + + +void +Camera::keyCallback(int key, int) +{ +#if GLFW_VERSION_MAJOR >= 3 + if (mWindow == nullptr) return; + int state = glfwGetKey(mWindow, key); +#else + int state = glfwGetKey(key); +#endif + switch (state) { + case GLFW_PRESS: + switch(key) { + case GLFW_KEY_SPACE: + mZoomMode = true; + break; + } + break; + case GLFW_RELEASE: + switch(key) { + case GLFW_KEY_SPACE: + mZoomMode = false; + break; + } + break; + } + mChanged = true; +} + + +void +Camera::mouseButtonCallback(int button, int action) +{ + if (button == GLFW_MOUSE_BUTTON_LEFT) { + if (action == GLFW_PRESS) mMouseDown = true; + else if (action == GLFW_RELEASE) mMouseDown = false; + } else if (button == GLFW_MOUSE_BUTTON_RIGHT) { + if (action == GLFW_PRESS) { + mMouseDown = true; + mZoomMode = true; + } else if (action == GLFW_RELEASE) { + mMouseDown = false; + mZoomMode = false; + } + } + if (action == GLFW_RELEASE) mMouseDown = false; + + mStartTumbling = true; + mChanged = true; +} + + +void +Camera::mousePosCallback(int x, int y) +{ + if (mStartTumbling) { + mMouseXPos = x; + mMouseYPos = y; + mStartTumbling = false; + } + + double dx, dy; + dx = x - mMouseXPos; + dy = y - mMouseYPos; + + if (mMouseDown && !mZoomMode) { + mNeedsDisplay = true; + mHead += dy * mTumblingSpeed; + mPitch += dx * mTumblingSpeed; + } else if (mMouseDown && mZoomMode) { + mNeedsDisplay = true; + mLookAt += (dy * mUp - dx * mRight) * mStrafeSpeed; + } + + mMouseXPos = x; + mMouseYPos = y; + mChanged = true; +} + + +void +Camera::mouseWheelCallback(int pos, int prevPos) +{ + double speed = std::abs(prevPos - pos); + + if (prevPos < pos) { + mDistance += speed * mZoomSpeed; + } else { + double temp = mDistance - speed * mZoomSpeed; + mDistance = std::max(0.0, temp); + } + setSpeed(); + + mChanged = true; + mNeedsDisplay = true; +} + +} // namespace openvdb_viewer diff --git a/openvdb/viewer/Camera.h b/openvdb/viewer/Camera.h new file mode 100644 index 00000000..d9905045 --- /dev/null +++ b/openvdb/viewer/Camera.h @@ -0,0 +1,66 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Camera.h +/// @brief Basic GL camera class + +#ifndef OPENVDB_VIEWER_CAMERA_HAS_BEEN_INCLUDED +#define OPENVDB_VIEWER_CAMERA_HAS_BEEN_INCLUDED + +#include + +#ifdef OPENVDB_USE_GLFW_3 +struct GLFWwindow; // forward declaration +#endif + + +namespace openvdb_viewer { + +class Camera +{ +public: + Camera(); + +#ifdef OPENVDB_USE_GLFW_3 + void setWindow(GLFWwindow* w) { mWindow = w; } +#endif + + void aim(); + + void lookAt(const openvdb::Vec3d& p, double dist = 1.0); + void lookAtTarget(); + + void setTarget(const openvdb::Vec3d& p, double dist = 1.0); + + void setNearFarPlanes(double n, double f) { mNearPlane = n; mFarPlane = f; } + void setFieldOfView(double degrees) { mFov = degrees; } + void setSpeed(double zoomSpeed = 0.1, double strafeSpeed = 0.002, double tumblingSpeed = 0.02); + + void keyCallback(int key, int action); + void mouseButtonCallback(int button, int action); + void mousePosCallback(int x, int y); + void mouseWheelCallback(int pos, int prevPos); + + bool needsDisplay() const { return mNeedsDisplay; } + +private: + // Camera parameters + double mFov, mNearPlane, mFarPlane; + openvdb::Vec3d mTarget, mLookAt, mUp, mForward, mRight, mEye; + double mTumblingSpeed, mZoomSpeed, mStrafeSpeed; + double mHead, mPitch, mTargetDistance, mDistance; + + // Input states + bool mMouseDown, mStartTumbling, mZoomMode, mChanged, mNeedsDisplay; + double mMouseXPos, mMouseYPos; + +#ifdef OPENVDB_USE_GLFW_3 + GLFWwindow* mWindow; +#endif + + static const double sDeg2rad; +}; // class Camera + +} // namespace openvdb_viewer + +#endif // OPENVDB_VIEWER_CAMERA_HAS_BEEN_INCLUDED diff --git a/openvdb/viewer/ClipBox.cc b/openvdb/viewer/ClipBox.cc new file mode 100644 index 00000000..33e99c1d --- /dev/null +++ b/openvdb/viewer/ClipBox.cc @@ -0,0 +1,266 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "ClipBox.h" + + +namespace openvdb_viewer { + +ClipBox::ClipBox() + : mStepSize(1.0) + , mBBox() + , mXIsActive(false) + , mYIsActive(false) + , mZIsActive(false) + , mShiftIsDown(false) + , mCtrlIsDown(false) +{ + GLdouble front [] = { 0.0, 0.0, 1.0, 0.0}; + std::copy(front, front + 4, mFrontPlane); + + GLdouble back [] = { 0.0, 0.0,-1.0, 0.0}; + std::copy(back, back + 4, mBackPlane); + + GLdouble left [] = { 1.0, 0.0, 0.0, 0.0}; + std::copy(left, left + 4, mLeftPlane); + + GLdouble right [] = {-1.0, 0.0, 0.0, 0.0}; + std::copy(right, right + 4, mRightPlane); + + GLdouble top [] = { 0.0, 1.0, 0.0, 0.0}; + std::copy(top, top + 4, mTopPlane); + + GLdouble bottom [] = { 0.0,-1.0, 0.0, 0.0}; + std::copy(bottom, bottom + 4, mBottomPlane); +} + + +void +ClipBox::setBBox(const openvdb::BBoxd& bbox) +{ + mBBox = bbox; + reset(); +} + + +void +ClipBox::update(double steps) +{ + if (mXIsActive) { + GLdouble s = steps * mStepSize.x() * 4.0; + + if (mShiftIsDown || mCtrlIsDown) { + mLeftPlane[3] -= s; + mLeftPlane[3] = -std::min(-mLeftPlane[3], (mRightPlane[3] - mStepSize.x())); + mLeftPlane[3] = -std::max(-mLeftPlane[3], mBBox.min().x()); + } + + if (!mShiftIsDown || mCtrlIsDown) { + mRightPlane[3] += s; + mRightPlane[3] = std::min(mRightPlane[3], mBBox.max().x()); + mRightPlane[3] = std::max(mRightPlane[3], (-mLeftPlane[3] + mStepSize.x())); + } + } + + if (mYIsActive) { + GLdouble s = steps * mStepSize.y() * 4.0; + + if (mShiftIsDown || mCtrlIsDown) { + mTopPlane[3] -= s; + mTopPlane[3] = -std::min(-mTopPlane[3], (mBottomPlane[3] - mStepSize.y())); + mTopPlane[3] = -std::max(-mTopPlane[3], mBBox.min().y()); + } + + if (!mShiftIsDown || mCtrlIsDown) { + mBottomPlane[3] += s; + mBottomPlane[3] = std::min(mBottomPlane[3], mBBox.max().y()); + mBottomPlane[3] = std::max(mBottomPlane[3], (-mTopPlane[3] + mStepSize.y())); + } + } + + if (mZIsActive) { + GLdouble s = steps * mStepSize.z() * 4.0; + + if (mShiftIsDown || mCtrlIsDown) { + mFrontPlane[3] -= s; + mFrontPlane[3] = -std::min(-mFrontPlane[3], (mBackPlane[3] - mStepSize.z())); + mFrontPlane[3] = -std::max(-mFrontPlane[3], mBBox.min().z()); + } + + if (!mShiftIsDown || mCtrlIsDown) { + mBackPlane[3] += s; + mBackPlane[3] = std::min(mBackPlane[3], mBBox.max().z()); + mBackPlane[3] = std::max(mBackPlane[3], (-mFrontPlane[3] + mStepSize.z())); + } + } +} + + +void +ClipBox::reset() +{ + mFrontPlane[3] = std::abs(mBBox.min().z()); + mBackPlane[3] = mBBox.max().z(); + + mLeftPlane[3] = std::abs(mBBox.min().x()); + mRightPlane[3] = mBBox.max().x(); + + mTopPlane[3] = std::abs(mBBox.min().y()); + mBottomPlane[3] = mBBox.max().y(); +} + + +void +ClipBox::update() const +{ + glClipPlane(GL_CLIP_PLANE0, mFrontPlane); + glClipPlane(GL_CLIP_PLANE1, mBackPlane); + glClipPlane(GL_CLIP_PLANE2, mLeftPlane); + glClipPlane(GL_CLIP_PLANE3, mRightPlane); + glClipPlane(GL_CLIP_PLANE4, mTopPlane); + glClipPlane(GL_CLIP_PLANE5, mBottomPlane); +} + + +void +ClipBox::enableClipping() const +{ + update(); + if (-mFrontPlane[3] > mBBox.min().z()) glEnable(GL_CLIP_PLANE0); + if (mBackPlane[3] < mBBox.max().z()) glEnable(GL_CLIP_PLANE1); + if (-mLeftPlane[3] > mBBox.min().x()) glEnable(GL_CLIP_PLANE2); + if (mRightPlane[3] < mBBox.max().x()) glEnable(GL_CLIP_PLANE3); + if (-mTopPlane[3] > mBBox.min().y()) glEnable(GL_CLIP_PLANE4); + if (mBottomPlane[3] < mBBox.max().y()) glEnable(GL_CLIP_PLANE5); +} + + +void +ClipBox::disableClipping() const +{ + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + glDisable(GL_CLIP_PLANE4); + glDisable(GL_CLIP_PLANE5); +} + + +void +ClipBox::render() +{ + bool drawBbox = false; + + const GLenum geoMode = GL_LINE_LOOP; + + glColor3d(0.1, 0.1, 0.9); + if (-mFrontPlane[3] > mBBox.min().z()) { + glBegin(geoMode); + glVertex3d(mBBox.min().x(), mBBox.min().y(), -mFrontPlane[3]); + glVertex3d(mBBox.min().x(), mBBox.max().y(), -mFrontPlane[3]); + glVertex3d(mBBox.max().x(), mBBox.max().y(), -mFrontPlane[3]); + glVertex3d(mBBox.max().x(), mBBox.min().y(), -mFrontPlane[3]); + glEnd(); + drawBbox = true; + } + + if (mBackPlane[3] < mBBox.max().z()) { + glBegin(geoMode); + glVertex3d(mBBox.min().x(), mBBox.min().y(), mBackPlane[3]); + glVertex3d(mBBox.min().x(), mBBox.max().y(), mBackPlane[3]); + glVertex3d(mBBox.max().x(), mBBox.max().y(), mBackPlane[3]); + glVertex3d(mBBox.max().x(), mBBox.min().y(), mBackPlane[3]); + glEnd(); + drawBbox = true; + } + + glColor3d(0.9, 0.1, 0.1); + if (-mLeftPlane[3] > mBBox.min().x()) { + glBegin(geoMode); + glVertex3d(-mLeftPlane[3], mBBox.min().y(), mBBox.min().z()); + glVertex3d(-mLeftPlane[3], mBBox.max().y(), mBBox.min().z()); + glVertex3d(-mLeftPlane[3], mBBox.max().y(), mBBox.max().z()); + glVertex3d(-mLeftPlane[3], mBBox.min().y(), mBBox.max().z()); + glEnd(); + drawBbox = true; + } + + if (mRightPlane[3] < mBBox.max().x()) { + glBegin(geoMode); + glVertex3d(mRightPlane[3], mBBox.min().y(), mBBox.min().z()); + glVertex3d(mRightPlane[3], mBBox.max().y(), mBBox.min().z()); + glVertex3d(mRightPlane[3], mBBox.max().y(), mBBox.max().z()); + glVertex3d(mRightPlane[3], mBBox.min().y(), mBBox.max().z()); + glEnd(); + drawBbox = true; + } + + glColor3d(0.1, 0.9, 0.1); + if (-mTopPlane[3] > mBBox.min().y()) { + glBegin(geoMode); + glVertex3d(mBBox.min().x(), -mTopPlane[3], mBBox.min().z()); + glVertex3d(mBBox.min().x(), -mTopPlane[3], mBBox.max().z()); + glVertex3d(mBBox.max().x(), -mTopPlane[3], mBBox.max().z()); + glVertex3d(mBBox.max().x(), -mTopPlane[3], mBBox.min().z()); + glEnd(); + drawBbox = true; + } + + if (mBottomPlane[3] < mBBox.max().y()) { + glBegin(geoMode); + glVertex3d(mBBox.min().x(), mBottomPlane[3], mBBox.min().z()); + glVertex3d(mBBox.min().x(), mBottomPlane[3], mBBox.max().z()); + glVertex3d(mBBox.max().x(), mBottomPlane[3], mBBox.max().z()); + glVertex3d(mBBox.max().x(), mBottomPlane[3], mBBox.min().z()); + glEnd(); + drawBbox = true; + } + + if (drawBbox) { + glColor3d(0.5, 0.5, 0.5); + glBegin(GL_LINE_LOOP); + glVertex3d(mBBox.min().x(), mBBox.min().y(), mBBox.min().z()); + glVertex3d(mBBox.min().x(), mBBox.min().y(), mBBox.max().z()); + glVertex3d(mBBox.max().x(), mBBox.min().y(), mBBox.max().z()); + glVertex3d(mBBox.max().x(), mBBox.min().y(), mBBox.min().z()); + glEnd(); + + glBegin(GL_LINE_LOOP); + glVertex3d(mBBox.min().x(), mBBox.max().y(), mBBox.min().z()); + glVertex3d(mBBox.min().x(), mBBox.max().y(), mBBox.max().z()); + glVertex3d(mBBox.max().x(), mBBox.max().y(), mBBox.max().z()); + glVertex3d(mBBox.max().x(), mBBox.max().y(), mBBox.min().z()); + glEnd(); + + glBegin(GL_LINES); + glVertex3d(mBBox.min().x(), mBBox.min().y(), mBBox.min().z()); + glVertex3d(mBBox.min().x(), mBBox.max().y(), mBBox.min().z()); + glVertex3d(mBBox.min().x(), mBBox.min().y(), mBBox.max().z()); + glVertex3d(mBBox.min().x(), mBBox.max().y(), mBBox.max().z()); + glVertex3d(mBBox.max().x(), mBBox.min().y(), mBBox.max().z()); + glVertex3d(mBBox.max().x(), mBBox.max().y(), mBBox.max().z()); + glVertex3d(mBBox.max().x(), mBBox.min().y(), mBBox.min().z()); + glVertex3d(mBBox.max().x(), mBBox.max().y(), mBBox.min().z()); + glEnd(); + } +} + + +//////////////////////////////////////// + + +bool +ClipBox::mouseButtonCallback(int /*button*/, int /*action*/) +{ + return false; // unhandled +} + + +bool +ClipBox::mousePosCallback(int /*x*/, int /*y*/) +{ + return false; // unhandled +} + +} // namespace openvdb_viewer diff --git a/openvdb/viewer/ClipBox.h b/openvdb/viewer/ClipBox.h new file mode 100644 index 00000000..68a53fe6 --- /dev/null +++ b/openvdb/viewer/ClipBox.h @@ -0,0 +1,60 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_VIEWER_CLIPBOX_HAS_BEEN_INCLUDED +#define OPENVDB_VIEWER_CLIPBOX_HAS_BEEN_INCLUDED + +#include + +#if defined(__APPLE__) || defined(MACOSX) +#include +#include +#else +#include +#include +#endif + + +namespace openvdb_viewer { + +class ClipBox +{ +public: + ClipBox(); + + void enableClipping() const; + void disableClipping() const; + + void setBBox(const openvdb::BBoxd&); + void setStepSize(const openvdb::Vec3d& s) { mStepSize = s; } + + void render(); + + void update(double steps); + void reset(); + + bool isActive() const { return (mXIsActive || mYIsActive ||mZIsActive); } + + bool& activateXPlanes() { return mXIsActive; } + bool& activateYPlanes() { return mYIsActive; } + bool& activateZPlanes() { return mZIsActive; } + + bool& shiftIsDown() { return mShiftIsDown; } + bool& ctrlIsDown() { return mCtrlIsDown; } + + bool mouseButtonCallback(int button, int action); + bool mousePosCallback(int x, int y); + +private: + void update() const; + + openvdb::Vec3d mStepSize; + openvdb::BBoxd mBBox; + bool mXIsActive, mYIsActive, mZIsActive, mShiftIsDown, mCtrlIsDown; + GLdouble mFrontPlane[4], mBackPlane[4], mLeftPlane[4], mRightPlane[4], + mTopPlane[4], mBottomPlane[4]; +}; // class ClipBox + +} // namespace openvdb_viewer + +#endif // OPENVDB_VIEWER_CLIPBOX_HAS_BEEN_INCLUDED diff --git a/openvdb/viewer/Font.cc b/openvdb/viewer/Font.cc new file mode 100644 index 00000000..a9876e57 --- /dev/null +++ b/openvdb/viewer/Font.cc @@ -0,0 +1,169 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Font.h" + +#include // for OPENVDB_START_THREADSAFE_STATIC_WRITE + + +namespace openvdb_viewer { + +GLuint BitmapFont13::sOffset = 0; + +GLubyte BitmapFont13::sCharacters[95][13] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36 }, + { 0x00, 0x00, 0x00, 0x66, 0x66, 0xFF, 0x66, 0x66, 0xFF, 0x66, 0x66, 0x00, 0x00 }, + { 0x00, 0x00, 0x18, 0x7E, 0xFF, 0x1B, 0x1F, 0x7E, 0xF8, 0xD8, 0xFF, 0x7E, 0x18 }, + { 0x00, 0x00, 0x0E, 0x1B, 0xDB, 0x6E, 0x30, 0x18, 0x0C, 0x76, 0xDB, 0xD8, 0x70 }, + { 0x00, 0x00, 0x7F, 0xC6, 0xCF, 0xD8, 0x70, 0x70, 0xD8, 0xCC, 0xCC, 0x6C, 0x38 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1C, 0x0C, 0x0E }, + { 0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0C }, + { 0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x30 }, + { 0x00, 0x00, 0x00, 0x00, 0x99, 0x5A, 0x3C, 0xFF, 0x3C, 0x5A, 0x99, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18, 0x00, 0x00 }, + { 0x00, 0x00, 0x30, 0x18, 0x1C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0C, 0x0C, 0x06, 0x06, 0x03, 0x03 }, + { 0x00, 0x00, 0x3C, 0x66, 0xC3, 0xE3, 0xF3, 0xDB, 0xCF, 0xC7, 0xC3, 0x66, 0x3C }, + { 0x00, 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18 }, + { 0x00, 0x00, 0xFF, 0xC0, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xE7, 0x7E }, + { 0x00, 0x00, 0x7E, 0xE7, 0x03, 0x03, 0x07, 0x7E, 0x07, 0x03, 0x03, 0xE7, 0x7E }, + { 0x00, 0x00, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xFF, 0xCC, 0x6C, 0x3C, 0x1C, 0x0C }, + { 0x00, 0x00, 0x7E, 0xE7, 0x03, 0x03, 0x07, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF }, + { 0x00, 0x00, 0x7E, 0xE7, 0xC3, 0xC3, 0xC7, 0xFE, 0xC0, 0xC0, 0xC0, 0xE7, 0x7E }, + { 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x03, 0x03, 0xFF }, + { 0x00, 0x00, 0x7E, 0xE7, 0xC3, 0xC3, 0xE7, 0x7E, 0xE7, 0xC3, 0xC3, 0xE7, 0x7E }, + { 0x00, 0x00, 0x7E, 0xE7, 0x03, 0x03, 0x03, 0x7F, 0xE7, 0xC3, 0xC3, 0xE7, 0x7E }, + { 0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x30, 0x18, 0x1C, 0x1C, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06 }, + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60 }, + { 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0C, 0x06, 0x03, 0xC3, 0xC3, 0x7E }, + { 0x00, 0x00, 0x3F, 0x60, 0xCF, 0xDB, 0xD3, 0xDD, 0xC3, 0x7E, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18 }, + { 0x00, 0x00, 0xFE, 0xC7, 0xC3, 0xC3, 0xC7, 0xFE, 0xC7, 0xC3, 0xC3, 0xC7, 0xFE }, + { 0x00, 0x00, 0x7E, 0xE7, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE7, 0x7E }, + { 0x00, 0x00, 0xFC, 0xCE, 0xC7, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0xCE, 0xFC }, + { 0x00, 0x00, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xFC, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF }, + { 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFC, 0xC0, 0xC0, 0xC0, 0xFF }, + { 0x00, 0x00, 0x7E, 0xE7, 0xC3, 0xC3, 0xCF, 0xC0, 0xC0, 0xC0, 0xC0, 0xE7, 0x7E }, + { 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3 }, + { 0x00, 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E }, + { 0x00, 0x00, 0x7C, 0xEE, 0xC6, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 }, + { 0x00, 0x00, 0xC3, 0xC6, 0xCC, 0xD8, 0xF0, 0xE0, 0xF0, 0xD8, 0xCC, 0xC6, 0xC3 }, + { 0x00, 0x00, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0 }, + { 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xDB, 0xFF, 0xFF, 0xE7, 0xC3 }, + { 0x00, 0x00, 0xC7, 0xC7, 0xCF, 0xCF, 0xDF, 0xDB, 0xFB, 0xF3, 0xF3, 0xE3, 0xE3 }, + { 0x00, 0x00, 0x7E, 0xE7, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7E }, + { 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0xC7, 0xC3, 0xC3, 0xC7, 0xFE }, + { 0x00, 0x00, 0x3F, 0x6E, 0xDF, 0xDB, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C }, + { 0x00, 0x00, 0xC3, 0xC6, 0xCC, 0xD8, 0xF0, 0xFE, 0xC7, 0xC3, 0xC3, 0xC7, 0xFE }, + { 0x00, 0x00, 0x7E, 0xE7, 0x03, 0x03, 0x07, 0x7E, 0xE0, 0xC0, 0xC0, 0xE7, 0x7E }, + { 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF }, + { 0x00, 0x00, 0x7E, 0xE7, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3 }, + { 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x66, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3 }, + { 0x00, 0x00, 0xC3, 0xE7, 0xFF, 0xFF, 0xDB, 0xDB, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3 }, + { 0x00, 0x00, 0xC3, 0x66, 0x66, 0x3C, 0x3C, 0x18, 0x3C, 0x3C, 0x66, 0x66, 0xC3 }, + { 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x3C, 0x66, 0x66, 0xC3 }, + { 0x00, 0x00, 0xFF, 0xC0, 0xC0, 0x60, 0x30, 0x7E, 0x0C, 0x06, 0x03, 0x03, 0xFF }, + { 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C }, + { 0x00, 0x03, 0x03, 0x06, 0x06, 0x0C, 0x0C, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60 }, + { 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x66, 0x3C, 0x18 }, + { 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x70 }, + { 0x00, 0x00, 0x7F, 0xC3, 0xC3, 0x7F, 0x03, 0xC3, 0x7E, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xFE, 0xC3, 0xC3, 0xC3, 0xC3, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0 }, + { 0x00, 0x00, 0x7E, 0xC3, 0xC0, 0xC0, 0xC0, 0xC3, 0x7E, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x7F, 0xC3, 0xC3, 0xC3, 0xC3, 0x7F, 0x03, 0x03, 0x03, 0x03, 0x03 }, + { 0x00, 0x00, 0x7F, 0xC0, 0xC0, 0xFE, 0xC3, 0xC3, 0x7E, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x33, 0x1E }, + { 0x7E, 0xC3, 0x03, 0x03, 0x7F, 0xC3, 0xC3, 0xC3, 0x7E, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0 }, + { 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00 }, + { 0x38, 0x6C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x00 }, + { 0x00, 0x00, 0xC6, 0xCC, 0xF8, 0xF0, 0xD8, 0xCC, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0 }, + { 0x00, 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78 }, + { 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xFE, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xFC, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + { 0xC0, 0xC0, 0xC0, 0xFE, 0xC3, 0xC3, 0xC3, 0xC3, 0xFE, 0x00, 0x00, 0x00, 0x00 }, + { 0x03, 0x03, 0x03, 0x7F, 0xC3, 0xC3, 0xC3, 0xC3, 0x7F, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xFE, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xFE, 0x03, 0x03, 0x7E, 0xC0, 0xC0, 0x7F, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x1C, 0x36, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x00 }, + { 0x00, 0x00, 0x7E, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x66, 0x66, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xC3, 0xE7, 0xFF, 0xDB, 0xC3, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0xC3, 0x00, 0x00, 0x00, 0x00 }, + { 0xC0, 0x60, 0x60, 0x30, 0x18, 0x3C, 0x66, 0x66, 0xC3, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x06, 0xFF, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x0F, 0x18, 0x18, 0x18, 0x38, 0xF0, 0x38, 0x18, 0x18, 0x18, 0x0F }, + { 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }, + { 0x00, 0x00, 0xF0, 0x18, 0x18, 0x18, 0x1C, 0x0F, 0x1C, 0x18, 0x18, 0x18, 0xF0 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8F, 0xF1, 0x60, 0x00, 0x00, 0x00 } +}; // sCharacters + + +void +BitmapFont13::initialize() +{ + OPENVDB_START_THREADSAFE_STATIC_WRITE + + glShadeModel(GL_FLAT); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + BitmapFont13::sOffset = glGenLists(128); + + for (GLuint c = 32; c < 127; ++c) { + glNewList(c + BitmapFont13::sOffset, GL_COMPILE); + glBitmap(8, 13, 0.0, 2.0, 10.0, 0.0, BitmapFont13::sCharacters[c-32]); + glEndList(); + } + OPENVDB_FINISH_THREADSAFE_STATIC_WRITE +} + + +void +BitmapFont13::enableFontRendering() +{ + glPushMatrix(); + GLint vp[4] = { 0, 0, 0, 0 }; + glGetIntegerv(GL_VIEWPORT, vp); + const int width = vp[2], height = std::max(1, vp[3]); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, 0, height, -1.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + //glShadeModel(GL_FLAT); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +} + + +void +BitmapFont13::disableFontRendering() +{ + glFlush(); + glPopMatrix(); +} + + +void +BitmapFont13::print(GLint px, GLint py, const std::string& str) +{ + glRasterPos2i(px, py); + glPushAttrib(GL_LIST_BIT); + glListBase(BitmapFont13::sOffset); + glCallLists(GLsizei(str.length()), GL_UNSIGNED_BYTE, + reinterpret_cast(str.c_str())); + glPopAttrib(); +} + +} // namespace openvdb_viewer diff --git a/openvdb/viewer/Font.h b/openvdb/viewer/Font.h new file mode 100644 index 00000000..a0e59eda --- /dev/null +++ b/openvdb/viewer/Font.h @@ -0,0 +1,39 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_VIEWER_FONT_HAS_BEEN_INCLUDED +#define OPENVDB_VIEWER_FONT_HAS_BEEN_INCLUDED + +#include + +#if defined(__APPLE__) || defined(MACOSX) +#include +#include +#else +#include +#include +#endif + + +namespace openvdb_viewer { + +class BitmapFont13 +{ +public: + BitmapFont13() {} + + static void initialize(); + + static void enableFontRendering(); + static void disableFontRendering(); + + static void print(GLint px, GLint py, const std::string&); + +private: + static GLuint sOffset; + static GLubyte sCharacters[95][13]; +}; + +} // namespace openvdb_viewer + +#endif // OPENVDB_VIEWER_FONT_HAS_BEEN_INCLUDED diff --git a/openvdb/viewer/RenderModules.cc b/openvdb/viewer/RenderModules.cc new file mode 100644 index 00000000..e9f8ef3a --- /dev/null +++ b/openvdb/viewer/RenderModules.cc @@ -0,0 +1,1665 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "RenderModules.h" + +#include +#include +#include +#include +#include +#include +#include +#include // for std::min() +#include // for std::abs(), std::fabs(), std::floor() +#include +#include // for std::is_const + + +namespace openvdb_viewer { + +namespace util { + +/// Helper class used internally by processTypedGrid() +template +struct GridProcessor { + static inline void call(OpType& op, openvdb::GridBase::Ptr grid) { +#ifdef _MSC_VER + op.operator()(openvdb::gridPtrCast(grid)); +#else + op.template operator()(openvdb::gridPtrCast(grid)); +#endif + } +}; + +/// Helper class used internally by processTypedGrid() +template +struct GridProcessor { + static inline void call(OpType& op, openvdb::GridBase::ConstPtr grid) { +#ifdef _MSC_VER + op.operator()(openvdb::gridConstPtrCast(grid)); +#else + op.template operator()(openvdb::gridConstPtrCast(grid)); +#endif + } +}; + + +/// Helper function used internally by processTypedGrid() +template +inline void +doProcessTypedGrid(GridPtrType grid, OpType& op) +{ + GridProcessor::value>::call(op, grid); +} + + +//////////////////////////////////////// + + +/// @brief Utility function that, given a generic grid pointer, +/// calls a functor on the fully-resolved grid +/// +/// Usage: +/// @code +/// struct PruneOp { +/// template +/// void operator()(typename GridT::Ptr grid) const { grid->tree()->prune(); } +/// }; +/// +/// processTypedGrid(myGridPtr, PruneOp()); +/// @endcode +/// +/// @return @c false if the grid type is unknown or unhandled. +template +bool +processTypedGrid(GridPtrType grid, OpType& op) +{ + using namespace openvdb; + if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) { + doProcessTypedGrid(grid, op); + } + else return false; + return true; +} + + +/// @brief Utility function that, given a generic grid pointer, calls +/// a functor on the fully-resolved grid, provided that the grid's +/// voxel values are scalars +template +bool +processTypedScalarGrid(GridPtrType grid, OpType& op) +{ + using namespace openvdb; + if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else return false; + return true; +} + + +/// @brief Utility function that, given a generic grid pointer, calls +/// a functor on the fully-resolved grid, provided that the grid's +/// voxel values are scalars or PointIndex objects +template +bool +processTypedScalarOrPointDataGrid(GridPtrType grid, OpType& op) +{ + using namespace openvdb; + if (processTypedScalarGrid(grid, op)) return true; + if (grid->template isType()) { + doProcessTypedGrid(grid, op); + return true; + } + return false; +} + + +/// @brief Utility function that, given a generic grid pointer, calls +/// a functor on the fully-resolved grid, provided that the grid's +/// voxel values are vectors +template +bool +processTypedVectorGrid(GridPtrType grid, OpType& op) +{ + using namespace openvdb; + if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else return false; + return true; +} + +template +class MinMaxVoxel +{ +public: + using LeafArray = openvdb::tree::LeafManager; + using ValueType = typename TreeType::ValueType; + + // LeafArray = openvdb::tree::LeafManager leafs(myTree) + MinMaxVoxel(LeafArray&); + + void runParallel(); + void runSerial(); + + const ValueType& minVoxel() const { return mMin; } + const ValueType& maxVoxel() const { return mMax; } + + inline MinMaxVoxel(const MinMaxVoxel&, tbb::split); + inline void operator()(const tbb::blocked_range&); + inline void join(const MinMaxVoxel&); + +private: + LeafArray& mLeafArray; + ValueType mMin, mMax; +}; + + +template +MinMaxVoxel::MinMaxVoxel(LeafArray& leafs) + : mLeafArray(leafs) + , mMin(std::numeric_limits::max()) + , mMax(-mMin) +{ +} + + +template +inline +MinMaxVoxel::MinMaxVoxel(const MinMaxVoxel& rhs, tbb::split) + : mLeafArray(rhs.mLeafArray) + , mMin(std::numeric_limits::max()) + , mMax(-mMin) +{ +} + + +template +void +MinMaxVoxel::runParallel() +{ + tbb::parallel_reduce(mLeafArray.getRange(), *this); +} + + +template +void +MinMaxVoxel::runSerial() +{ + (*this)(mLeafArray.getRange()); +} + + +template +inline void +MinMaxVoxel::operator()(const tbb::blocked_range& range) +{ + typename TreeType::LeafNodeType::ValueOnCIter iter; + + for (size_t n = range.begin(); n < range.end(); ++n) { + iter = mLeafArray.leaf(n).cbeginValueOn(); + for (; iter; ++iter) { + const ValueType value = iter.getValue(); + mMin = std::min(mMin, value); + mMax = std::max(mMax, value); + } + } +} + + +template +inline void +MinMaxVoxel::join(const MinMaxVoxel& rhs) +{ + mMin = std::min(mMin, rhs.mMin); + mMax = std::max(mMax, rhs.mMax); +} + +} // namespace util + + +//////////////////////////////////////// + + +// BufferObject + +BufferObject::BufferObject(): + mVertexBuffer(0), + mNormalBuffer(0), + mIndexBuffer(0), + mColorBuffer(0), + mPrimType(GL_POINTS), + mPrimNum(0) +{ +} + +BufferObject::~BufferObject() { clear(); } + +void +BufferObject::render() const +{ + if (mPrimNum == 0 || !glIsBuffer(mIndexBuffer) || !glIsBuffer(mVertexBuffer)) { + OPENVDB_LOG_DEBUG_RUNTIME("request to render empty or uninitialized buffer"); + return; + } + + const bool usesColorBuffer = glIsBuffer(mColorBuffer); + const bool usesNormalBuffer = glIsBuffer(mNormalBuffer); + + glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, nullptr); + + if (usesColorBuffer) { + glBindBuffer(GL_ARRAY_BUFFER, mColorBuffer); + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(3, GL_FLOAT, 0, nullptr); + } + + if (usesNormalBuffer) { + glEnableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mNormalBuffer); + glNormalPointer(GL_FLOAT, 0, nullptr); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); + glDrawElements(mPrimType, mPrimNum, GL_UNSIGNED_INT, nullptr); + + // disable client-side capabilities + if (usesColorBuffer) glDisableClientState(GL_COLOR_ARRAY); + if (usesNormalBuffer) glDisableClientState(GL_NORMAL_ARRAY); + + // release vbo's + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +void +BufferObject::genIndexBuffer(const std::vector& v, GLenum primType) +{ + // clear old buffer + if (glIsBuffer(mIndexBuffer) == GL_TRUE) glDeleteBuffers(1, &mIndexBuffer); + + // gen new buffer + glGenBuffers(1, &mIndexBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); + if (glIsBuffer(mIndexBuffer) == GL_FALSE) throw "Error: Unable to create index buffer"; + + // upload data + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + sizeof(GLuint) * v.size(), &v[0], GL_STATIC_DRAW); // upload data + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to upload index buffer data"; + + // release buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + mPrimNum = GLsizei(v.size()); + mPrimType = primType; +} + +void +BufferObject::genVertexBuffer(const std::vector& v) +{ + if (glIsBuffer(mVertexBuffer) == GL_TRUE) glDeleteBuffers(1, &mVertexBuffer); + + glGenBuffers(1, &mVertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + if (glIsBuffer(mVertexBuffer) == GL_FALSE) throw "Error: Unable to create vertex buffer"; + + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * v.size(), &v[0], GL_STATIC_DRAW); + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to upload vertex buffer data"; + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void +BufferObject::genNormalBuffer(const std::vector& v) +{ + if (glIsBuffer(mNormalBuffer) == GL_TRUE) glDeleteBuffers(1, &mNormalBuffer); + + glGenBuffers(1, &mNormalBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mNormalBuffer); + if (glIsBuffer(mNormalBuffer) == GL_FALSE) throw "Error: Unable to create normal buffer"; + + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * v.size(), &v[0], GL_STATIC_DRAW); + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to upload normal buffer data"; + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void +BufferObject::genColorBuffer(const std::vector& v) +{ + if (glIsBuffer(mColorBuffer) == GL_TRUE) glDeleteBuffers(1, &mColorBuffer); + + glGenBuffers(1, &mColorBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mColorBuffer); + if (glIsBuffer(mColorBuffer) == GL_FALSE) throw "Error: Unable to create color buffer"; + + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * v.size(), &v[0], GL_STATIC_DRAW); + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to upload color buffer data"; + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void +BufferObject::clear() +{ + if (glIsBuffer(mIndexBuffer) == GL_TRUE) glDeleteBuffers(1, &mIndexBuffer); + if (glIsBuffer(mVertexBuffer) == GL_TRUE) glDeleteBuffers(1, &mVertexBuffer); + if (glIsBuffer(mColorBuffer) == GL_TRUE) glDeleteBuffers(1, &mColorBuffer); + if (glIsBuffer(mNormalBuffer) == GL_TRUE) glDeleteBuffers(1, &mNormalBuffer); + + mPrimType = GL_POINTS; + mPrimNum = 0; +} + + +//////////////////////////////////////// + + +ShaderProgram::ShaderProgram(): + mProgram(0), + mVertShader(0), + mFragShader(0) +{ +} + +ShaderProgram::~ShaderProgram() { clear(); } + +void +ShaderProgram::setVertShader(const std::string& s) +{ + mVertShader = glCreateShader(GL_VERTEX_SHADER); + if (glIsShader(mVertShader) == GL_FALSE) throw "Error: Unable to create shader program."; + + GLint length = GLint(s.length()); + const char *str = s.c_str(); + glShaderSource(mVertShader, 1, &str, &length); + + glCompileShader(mVertShader); + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to compile vertex shader."; +} + +void +ShaderProgram::setFragShader(const std::string& s) +{ + mFragShader = glCreateShader(GL_FRAGMENT_SHADER); + if (glIsShader(mFragShader) == GL_FALSE) throw "Error: Unable to create shader program."; + + GLint length = GLint(s.length()); + const char *str = s.c_str(); + glShaderSource(mFragShader, 1, &str, &length); + + glCompileShader(mFragShader); + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to compile fragment shader."; +} + +void +ShaderProgram::build() +{ + mProgram = glCreateProgram(); + if (glIsProgram(mProgram) == GL_FALSE) throw "Error: Unable to create shader program."; + + if (glIsShader(mVertShader) == GL_TRUE) glAttachShader(mProgram, mVertShader); + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to attach vertex shader."; + + if (glIsShader(mFragShader) == GL_TRUE) glAttachShader(mProgram, mFragShader); + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to attach fragment shader."; + + + glLinkProgram(mProgram); + + GLint linked = 0; + glGetProgramiv(mProgram, GL_LINK_STATUS, &linked); + + if (!linked) throw "Error: Unable to link shader program."; +} + +void +ShaderProgram::build(const std::vector& attributes) +{ + mProgram = glCreateProgram(); + if (glIsProgram(mProgram) == GL_FALSE) throw "Error: Unable to create shader program."; + + for (GLuint n = 0, N = GLuint(attributes.size()); n < N; ++n) { + glBindAttribLocation(mProgram, n, attributes[n]); + } + + if (glIsShader(mVertShader) == GL_TRUE) glAttachShader(mProgram, mVertShader); + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to attach vertex shader."; + + if (glIsShader(mFragShader) == GL_TRUE) glAttachShader(mProgram, mFragShader); + if (GL_NO_ERROR != glGetError()) throw "Error: Unable to attach fragment shader."; + + glLinkProgram(mProgram); + + GLint linked; + glGetProgramiv(mProgram, GL_LINK_STATUS, &linked); + + if (!linked) throw "Error: Unable to link shader program."; +} + +void +ShaderProgram::startShading() const +{ + if (glIsProgram(mProgram) == GL_FALSE) { + throw "Error: called startShading() on uncompiled shader program."; + } + glUseProgram(mProgram); +} + +void +ShaderProgram::stopShading() const +{ + glUseProgram(0); +} + +void +ShaderProgram::clear() +{ + GLsizei numShaders = 0; + GLuint shaders[2] = { 0, 0 }; + + glGetAttachedShaders(mProgram, 2, &numShaders, shaders); + + // detach and remove shaders + for (GLsizei n = 0; n < numShaders; ++n) { + + glDetachShader(mProgram, shaders[n]); + + if (glIsShader(shaders[n]) == GL_TRUE) glDeleteShader(shaders[n]); + } + + // remove program + if (glIsProgram(mProgram)) glDeleteProgram(mProgram); +} + + +//////////////////////////////////////// + +// ViewportModule + +ViewportModule::ViewportModule(): + mAxisGnomonScale(1.5), + mGroundPlaneScale(8.0) +{ +} + + +void +ViewportModule::render() +{ + if (!mIsVisible) return; + + /// @todo use VBO's + + // Ground plane + glPushMatrix(); + glScalef(mGroundPlaneScale, mGroundPlaneScale, mGroundPlaneScale); + glColor3d(0.6, 0.6, 0.6); + + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + + float step = 0.125; + for (float x = -1; x < 1.125; x+=step) { + + if (std::fabs(x) == 0.5 || std::fabs(x) == 0.0) { + glLineWidth(1.5); + } else { + glLineWidth(1.0); + } + + glBegin(GL_LINES); + glVertex3f(x, 0, 1); + glVertex3f(x, 0, -1); + glVertex3f(1, 0, x); + glVertex3f(-1, 0, x); + glEnd(); + } + + OPENVDB_NO_FP_EQUALITY_WARNING_END + + + glPopMatrix(); + + // Axis gnomon + GLfloat modelview[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, &modelview[0]); + + // Stash current viewport settigs. + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, &viewport[0]); + + GLint width = viewport[2] / 20; + GLint height = viewport[3] / 20; + glViewport(0, 0, width, height); + + + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + + GLfloat campos[3] = { modelview[2], modelview[6], modelview[10] }; + GLfloat up[3] = { modelview[1], modelview[5], modelview[9] }; + + gluLookAt(campos[0], campos[1], campos[2], 0.0, 0.0, 0.0, up[0], up[1], up[2]); + + glScalef(mAxisGnomonScale, mAxisGnomonScale, mAxisGnomonScale); + + glLineWidth(1.0); + + glBegin(GL_LINES); + glColor3f(1.0f, 0.0f, 0.0f); + glVertex3f(0, 0, 0); + glVertex3f(1, 0, 0); + + glColor3f(0.0f, 1.0f, 0.0f ); + glVertex3f(0, 0, 0); + glVertex3f(0, 1, 0); + + glColor3f(0.0f, 0.0f, 1.0f); + glVertex3f(0, 0, 0); + glVertex3f(0, 0, 1); + glEnd(); + + glLineWidth(1.0); + + // reset viewport + glPopMatrix(); + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + +} + + +//////////////////////////////////////// + + +class TreeTopologyOp +{ +public: + TreeTopologyOp(BufferObject& buffer) : mBuffer(&buffer) {} + + template + void operator()(typename GridType::ConstPtr grid) + { + using openvdb::Index64; + + Index64 nodeCount = grid->tree().leafCount() + grid->tree().nonLeafCount(); + const Index64 N = nodeCount * 8 * 3; + + std::vector points(N); + std::vector colors(N); + std::vector indices(N); + + + openvdb::Vec3d ptn; + openvdb::Vec3s color; + openvdb::CoordBBox bbox; + Index64 pOffset = 0, iOffset = 0, cOffset = 0, idx = 0; + + for (typename GridType::TreeType::NodeCIter iter = grid->tree().cbeginNode(); iter; ++iter) + { + iter.getBoundingBox(bbox); + + // Nodes are rendered as cell-centered + const openvdb::Vec3d min(bbox.min().x()-0.5, bbox.min().y()-0.5, bbox.min().z()-0.5); + const openvdb::Vec3d max(bbox.max().x()+0.5, bbox.max().y()+0.5, bbox.max().z()+0.5); + + // corner 1 + ptn = grid->indexToWorld(min); + points[pOffset++] = static_cast(ptn[0]); + points[pOffset++] = static_cast(ptn[1]); + points[pOffset++] = static_cast(ptn[2]); + + // corner 2 + ptn = openvdb::Vec3d(min.x(), min.y(), max.z()); + ptn = grid->indexToWorld(ptn); + points[pOffset++] = static_cast(ptn[0]); + points[pOffset++] = static_cast(ptn[1]); + points[pOffset++] = static_cast(ptn[2]); + + // corner 3 + ptn = openvdb::Vec3d(max.x(), min.y(), max.z()); + ptn = grid->indexToWorld(ptn); + points[pOffset++] = static_cast(ptn[0]); + points[pOffset++] = static_cast(ptn[1]); + points[pOffset++] = static_cast(ptn[2]); + + // corner 4 + ptn = openvdb::Vec3d(max.x(), min.y(), min.z()); + ptn = grid->indexToWorld(ptn); + points[pOffset++] = static_cast(ptn[0]); + points[pOffset++] = static_cast(ptn[1]); + points[pOffset++] = static_cast(ptn[2]); + + // corner 5 + ptn = openvdb::Vec3d(min.x(), max.y(), min.z()); + ptn = grid->indexToWorld(ptn); + points[pOffset++] = static_cast(ptn[0]); + points[pOffset++] = static_cast(ptn[1]); + points[pOffset++] = static_cast(ptn[2]); + + // corner 6 + ptn = openvdb::Vec3d(min.x(), max.y(), max.z()); + ptn = grid->indexToWorld(ptn); + points[pOffset++] = static_cast(ptn[0]); + points[pOffset++] = static_cast(ptn[1]); + points[pOffset++] = static_cast(ptn[2]); + + // corner 7 + ptn = grid->indexToWorld(max); + points[pOffset++] = static_cast(ptn[0]); + points[pOffset++] = static_cast(ptn[1]); + points[pOffset++] = static_cast(ptn[2]); + + // corner 8 + ptn = openvdb::Vec3d(max.x(), max.y(), min.z()); + ptn = grid->indexToWorld(ptn); + points[pOffset++] = static_cast(ptn[0]); + points[pOffset++] = static_cast(ptn[1]); + points[pOffset++] = static_cast(ptn[2]); + + + // edge 1 + indices[iOffset++] = GLuint(idx); + indices[iOffset++] = GLuint(idx + 1); + // edge 2 + indices[iOffset++] = GLuint(idx + 1); + indices[iOffset++] = GLuint(idx + 2); + // edge 3 + indices[iOffset++] = GLuint(idx + 2); + indices[iOffset++] = GLuint(idx + 3); + // edge 4 + indices[iOffset++] = GLuint(idx + 3); + indices[iOffset++] = GLuint(idx); + // edge 5 + indices[iOffset++] = GLuint(idx + 4); + indices[iOffset++] = GLuint(idx + 5); + // edge 6 + indices[iOffset++] = GLuint(idx + 5); + indices[iOffset++] = GLuint(idx + 6); + // edge 7 + indices[iOffset++] = GLuint(idx + 6); + indices[iOffset++] = GLuint(idx + 7); + // edge 8 + indices[iOffset++] = GLuint(idx + 7); + indices[iOffset++] = GLuint(idx + 4); + // edge 9 + indices[iOffset++] = GLuint(idx); + indices[iOffset++] = GLuint(idx + 4); + // edge 10 + indices[iOffset++] = GLuint(idx + 1); + indices[iOffset++] = GLuint(idx + 5); + // edge 11 + indices[iOffset++] = GLuint(idx + 2); + indices[iOffset++] = GLuint(idx + 6); + // edge 12 + indices[iOffset++] = GLuint(idx + 3); + indices[iOffset++] = GLuint(idx + 7); + + // node vertex color + const int level = iter.getLevel(); + color = sNodeColors[(level == 0) ? 3 : (level == 1) ? 2 : 1]; + + for (Index64 n = 0; n < 8; ++n) { + colors[cOffset++] = color[0]; + colors[cOffset++] = color[1]; + colors[cOffset++] = color[2]; + } + + idx += 8; + } // end node iteration + + // gen buffers and upload data to GPU + mBuffer->genVertexBuffer(points); + mBuffer->genColorBuffer(colors); + mBuffer->genIndexBuffer(indices, GL_LINES); + } + +private: + BufferObject *mBuffer; + + static openvdb::Vec3s sNodeColors[]; +}; // TreeTopologyOp + + +openvdb::Vec3s TreeTopologyOp::sNodeColors[] = { + openvdb::Vec3s(0.045f, 0.045f, 0.045f), // root + openvdb::Vec3s(0.0432f, 0.33f, 0.0411023f), // first internal node level + openvdb::Vec3s(0.871f, 0.394f, 0.01916f), // intermediate internal node levels + openvdb::Vec3s(0.00608299f, 0.279541f, 0.625f) // leaf nodes +}; + + +//////////////////////////////////////// + +// Tree topology render module + +TreeTopologyModule::TreeTopologyModule(const openvdb::GridBase::ConstPtr& grid): + mGrid(grid), + mIsInitialized(false) +{ + mShader.setVertShader( + "#version 120\n" + "void main() {\n" + "gl_FrontColor = gl_Color;\n" + "gl_Position = ftransform();\n" + "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" + "}\n"); + + mShader.setFragShader( + "#version 120\n" + "void main() {\n" + "gl_FragColor = gl_Color;}\n"); + + mShader.build(); +} + + +void +TreeTopologyModule::init() +{ + mIsInitialized = true; + + // extract grid topology + TreeTopologyOp drawTopology(mBufferObject); + + if (!util::processTypedGrid(mGrid, drawTopology)) { + OPENVDB_LOG_INFO("Ignoring unrecognized grid type" + " during tree topology module initialization."); + } +} + + +void +TreeTopologyModule::render() +{ + if (!mIsVisible) return; + if (!mIsInitialized) init(); + + mShader.startShading(); + + mBufferObject.render(); + + mShader.stopShading(); +} + + +//////////////////////////////////////// + + +template +class PointGenerator +{ +public: + using LeafManagerType = openvdb::tree::LeafManager; + + PointGenerator( + std::vector& points, + std::vector& indices, + LeafManagerType& leafs, + std::vector& indexMap, + const openvdb::math::Transform& transform, + openvdb::Index64 voxelsPerLeaf = TreeType::LeafNodeType::NUM_VOXELS) + : mPoints(points) + , mIndices(indices) + , mLeafs(leafs) + , mIndexMap(indexMap) + , mTransform(transform) + , mVoxelsPerLeaf(voxelsPerLeaf) + { + } + + void runParallel() + { + tbb::parallel_for(mLeafs.getRange(), *this); + } + + + inline void operator()(const typename LeafManagerType::RangeType& range) const + { + using openvdb::Index64; + + using ValueOnCIter = typename TreeType::LeafNodeType::ValueOnCIter; + + openvdb::Vec3d pos; + size_t index = 0; + Index64 activeVoxels = 0; + + for (size_t n = range.begin(); n < range.end(); ++n) { + + index = mIndexMap[n]; + ValueOnCIter it = mLeafs.leaf(n).cbeginValueOn(); + + activeVoxels = mLeafs.leaf(n).onVoxelCount(); + + if (activeVoxels <= mVoxelsPerLeaf) { + + for ( ; it; ++it) { + pos = mTransform.indexToWorld(it.getCoord()); + insertPoint(pos, index); + ++index; + } + + } else if (1 == mVoxelsPerLeaf) { + + pos = mTransform.indexToWorld(it.getCoord()); + insertPoint(pos, index); + + } else { + + std::vector coords; + coords.reserve(static_cast(activeVoxels)); + for ( ; it; ++it) { coords.push_back(it.getCoord()); } + + pos = mTransform.indexToWorld(coords[0]); + insertPoint(pos, index); + ++index; + + pos = mTransform.indexToWorld(coords[static_cast(activeVoxels-1)]); + insertPoint(pos, index); + ++index; + + Index64 r = Index64(std::floor(double(mVoxelsPerLeaf) / double(activeVoxels))); + for (Index64 i = 1, I = mVoxelsPerLeaf - 2; i < I; ++i) { + pos = mTransform.indexToWorld(coords[static_cast(i * r)]); + insertPoint(pos, index); + ++index; + } + } + } + } + +private: + void insertPoint(const openvdb::Vec3d& pos, size_t index) const + { + mIndices[index] = GLuint(index); + const size_t element = index * 3; + mPoints[element ] = static_cast(pos[0]); + mPoints[element + 1] = static_cast(pos[1]); + mPoints[element + 2] = static_cast(pos[2]); + } + + std::vector& mPoints; + std::vector& mIndices; + LeafManagerType& mLeafs; + std::vector& mIndexMap; + const openvdb::math::Transform& mTransform; + const openvdb::Index64 mVoxelsPerLeaf; +}; // PointGenerator + + +template +class NormalGenerator +{ +public: + using AccessorType = typename GridType::ConstAccessor; + using Grad = openvdb::math::ISGradient; + + NormalGenerator(const AccessorType& acc): mAccessor(acc) {} + + NormalGenerator(const NormalGenerator&) = delete; + NormalGenerator& operator=(const NormalGenerator&) = delete; + + void operator()(const openvdb::Coord& ijk, openvdb::Vec3d& normal) + { + openvdb::Vec3d v{Grad::result(mAccessor, ijk)}; + const double length = v.length(); + if (length > 1.0e-7) { + v *= 1.0 / length; + normal = v; + } + } + +private: + const AccessorType& mAccessor; +}; // class NormalGenerator + +// Specialization for PointDataGrids, for which normals are not generated +template<> +class NormalGenerator +{ +public: + NormalGenerator(const openvdb::points::PointDataGrid::ConstAccessor&) {} + NormalGenerator(const NormalGenerator&) = delete; + NormalGenerator& operator=(const NormalGenerator&) = delete; + void operator()(const openvdb::Coord&, openvdb::Vec3d&) {} +}; + + +template +class PointAttributeGenerator +{ +public: + using ValueType = typename GridType::ValueType; + + PointAttributeGenerator( + std::vector& points, + std::vector& colors, + const GridType& grid, + ValueType minValue, + ValueType maxValue, + openvdb::Vec3s (&colorMap)[4], + bool isLevelSet = false) + : mPoints(points) + , mColors(colors) + , mNormals(nullptr) + , mGrid(grid) + , mAccessor(grid.tree()) + , mMinValue(minValue) + , mMaxValue(maxValue) + , mColorMap(colorMap) + , mIsLevelSet(isLevelSet) + , mZeroValue(openvdb::zeroVal()) + { + init(); + } + + PointAttributeGenerator( + std::vector& points, + std::vector& colors, + std::vector& normals, + const GridType& grid, + ValueType minValue, + ValueType maxValue, + openvdb::Vec3s (&colorMap)[4], + bool isLevelSet = false) + : mPoints(points) + , mColors(colors) + , mNormals(&normals) + , mGrid(grid) + , mAccessor(grid.tree()) + , mMinValue(minValue) + , mMaxValue(maxValue) + , mColorMap(colorMap) + , mIsLevelSet(isLevelSet) + , mZeroValue(openvdb::zeroVal()) + { + init(); + } + + void runParallel() + { + tbb::parallel_for(tbb::blocked_range(0, (mPoints.size() / 3)), *this); + } + + inline void operator()(const tbb::blocked_range& range) const + { + openvdb::Coord ijk; + openvdb::Vec3d pos, normal(0.0, -1.0, 0.0); + openvdb::Vec3s color(0.9f, 0.3f, 0.3f); + float w = 0.0; + NormalGenerator computeNormal{mAccessor}; + + size_t e1, e2, e3, voxelNum = 0; + for (size_t n = range.begin(); n < range.end(); ++n) { + e1 = 3 * n; + e2 = e1 + 1; + e3 = e2 + 1; + + pos[0] = mPoints[e1]; + pos[1] = mPoints[e2]; + pos[2] = mPoints[e3]; + + pos = mGrid.worldToIndex(pos); + ijk[0] = int(pos[0]); + ijk[1] = int(pos[1]); + ijk[2] = int(pos[2]); + + const ValueType& value = mAccessor.getValue(ijk); + + if (value < mZeroValue) { // is negative + if (mIsLevelSet) { + color = mColorMap[1]; + } else { + w = (float(value) - mOffset[1]) * mScale[1]; + color = openvdb::Vec3s(w * mColorMap[0] + (1.0 - w) * mColorMap[1]); + } + } else { + if (mIsLevelSet) { + color = mColorMap[2]; + } else { + w = (float(value) - mOffset[0]) * mScale[0]; + color = openvdb::Vec3s(w * mColorMap[2] + (1.0 - w) * mColorMap[3]); + } + } + + mColors[e1] = color[0]; + mColors[e2] = color[1]; + mColors[e3] = color[2]; + + if (mNormals) { + if ((voxelNum % 2) == 0) { computeNormal(ijk, normal); } + ++voxelNum; + (*mNormals)[e1] = static_cast(normal[0]); + (*mNormals)[e2] = static_cast(normal[1]); + (*mNormals)[e3] = static_cast(normal[2]); + } + } + } + +private: + + void init() + { + mOffset[0] = static_cast(std::min(mZeroValue, mMinValue)); + mScale[0] = static_cast( + 1.0 / (std::abs(float(std::max(mZeroValue, mMaxValue)) - mOffset[0]))); + mOffset[1] = static_cast(std::min(mZeroValue, mMinValue)); + mScale[1] = static_cast( + 1.0 / (std::abs(float(std::max(mZeroValue, mMaxValue)) - mOffset[1]))); + } + + std::vector& mPoints; + std::vector& mColors; + std::vector* mNormals; + + const GridType& mGrid; + openvdb::tree::ValueAccessor mAccessor; + + ValueType mMinValue, mMaxValue; + openvdb::Vec3s (&mColorMap)[4]; + const bool mIsLevelSet; + + ValueType mZeroValue; + float mOffset[2], mScale[2]; +}; // PointAttributeGenerator + + +//////////////////////////////////////// + + +class ActiveScalarValuesOp +{ +public: + ActiveScalarValuesOp( + BufferObject& interiorBuffer, BufferObject& surfaceBuffer) + : mInteriorBuffer(&interiorBuffer) + , mSurfaceBuffer(&surfaceBuffer) + { + } + + template + void operator()(typename GridType::ConstPtr grid) + { + using openvdb::Index64; + + const Index64 maxVoxelPoints = 26000000; + + openvdb::Vec3s colorMap[4]; + colorMap[0] = openvdb::Vec3s(0.3f, 0.9f, 0.3f); // green + colorMap[1] = openvdb::Vec3s(0.9f, 0.3f, 0.3f); // red + colorMap[2] = openvdb::Vec3s(0.9f, 0.9f, 0.3f); // yellow + colorMap[3] = openvdb::Vec3s(0.3f, 0.3f, 0.9f); // blue + + ////////// + + using ValueType = typename GridType::ValueType; + using TreeType = typename GridType::TreeType; + using BoolTreeT = typename TreeType::template ValueConverter::Type; + + const TreeType& tree = grid->tree(); + const bool isLevelSetGrid = grid->getGridClass() == openvdb::GRID_LEVEL_SET; + + ValueType minValue, maxValue; + openvdb::tree::LeafManager leafs(tree); + + { + util::MinMaxVoxel minmax(leafs); + minmax.runParallel(); + minValue = minmax.minVoxel(); + maxValue = minmax.maxVoxel(); + } + + openvdb::Index64 voxelsPerLeaf = TreeType::LeafNodeType::NUM_VOXELS; + + if (!isLevelSetGrid) { + + typename BoolTreeT::Ptr interiorMask(new BoolTreeT(false)); + + { // Generate Interior Points + interiorMask->topologyUnion(tree); + interiorMask->voxelizeActiveTiles(); + + if (interiorMask->activeLeafVoxelCount() > maxVoxelPoints) { + voxelsPerLeaf = std::max(1, + (maxVoxelPoints / interiorMask->leafCount())); + } + + openvdb::tools::erodeVoxels(*interiorMask, 2); + + openvdb::tree::LeafManager maskleafs(*interiorMask); + std::vector indexMap(maskleafs.leafCount()); + size_t voxelCount = 0; + for (Index64 l = 0, L = maskleafs.leafCount(); l < L; ++l) { + indexMap[l] = voxelCount; + voxelCount += std::min(maskleafs.leaf(l).onVoxelCount(), voxelsPerLeaf); + } + + std::vector points(voxelCount * 3), colors(voxelCount * 3); + std::vector indices(voxelCount); + + PointGenerator pointGen( + points, indices, maskleafs, indexMap, grid->transform(), voxelsPerLeaf); + pointGen.runParallel(); + + + PointAttributeGenerator attributeGen( + points, colors, *grid, minValue, maxValue, colorMap); + attributeGen.runParallel(); + + + // gen buffers and upload data to GPU + mInteriorBuffer->genVertexBuffer(points); + mInteriorBuffer->genColorBuffer(colors); + mInteriorBuffer->genIndexBuffer(indices, GL_POINTS); + } + + { // Generate Surface Points + typename BoolTreeT::Ptr surfaceMask(new BoolTreeT(false)); + surfaceMask->topologyUnion(tree); + surfaceMask->voxelizeActiveTiles(); + + openvdb::tree::ValueAccessor interiorAcc(*interiorMask); + for (typename BoolTreeT::LeafIter leafIt = surfaceMask->beginLeaf(); + leafIt; ++leafIt) + { + const typename BoolTreeT::LeafNodeType* leaf = + interiorAcc.probeConstLeaf(leafIt->origin()); + if (leaf) leafIt->topologyDifference(*leaf, false); + } + openvdb::tools::pruneInactive(*surfaceMask); + + openvdb::tree::LeafManager maskleafs(*surfaceMask); + std::vector indexMap(maskleafs.leafCount()); + size_t voxelCount = 0; + for (Index64 l = 0, L = maskleafs.leafCount(); l < L; ++l) { + indexMap[l] = voxelCount; + voxelCount += std::min(maskleafs.leaf(l).onVoxelCount(), voxelsPerLeaf); + } + + std::vector + points(voxelCount * 3), + colors(voxelCount * 3), + normals(voxelCount * 3); + std::vector indices(voxelCount); + + PointGenerator pointGen( + points, indices, maskleafs, indexMap, grid->transform(), voxelsPerLeaf); + pointGen.runParallel(); + + PointAttributeGenerator attributeGen( + points, colors, normals, *grid, minValue, maxValue, colorMap); + attributeGen.runParallel(); + + mSurfaceBuffer->genVertexBuffer(points); + mSurfaceBuffer->genColorBuffer(colors); + mSurfaceBuffer->genNormalBuffer(normals); + mSurfaceBuffer->genIndexBuffer(indices, GL_POINTS); + } + + return; + } + + // Level set rendering + if (tree.activeLeafVoxelCount() > maxVoxelPoints) { + voxelsPerLeaf = std::max(1, (maxVoxelPoints / tree.leafCount())); + } + + std::vector indexMap(leafs.leafCount()); + size_t voxelCount = 0; + for (Index64 l = 0, L = leafs.leafCount(); l < L; ++l) { + indexMap[l] = voxelCount; + voxelCount += std::min(leafs.leaf(l).onVoxelCount(), voxelsPerLeaf); + } + + std::vector + points(voxelCount * 3), + colors(voxelCount * 3), + normals(voxelCount * 3); + std::vector indices(voxelCount); + + PointGenerator pointGen( + points, indices, leafs, indexMap, grid->transform(), voxelsPerLeaf); + pointGen.runParallel(); + + PointAttributeGenerator attributeGen( + points, colors, normals, *grid, minValue, maxValue, colorMap, isLevelSetGrid); + attributeGen.runParallel(); + + mSurfaceBuffer->genVertexBuffer(points); + mSurfaceBuffer->genColorBuffer(colors); + mSurfaceBuffer->genNormalBuffer(normals); + mSurfaceBuffer->genIndexBuffer(indices, GL_POINTS); + } + +private: + BufferObject *mInteriorBuffer; + BufferObject *mSurfaceBuffer; +}; // ActiveScalarValuesOp + + +class ActiveVectorValuesOp +{ +public: + ActiveVectorValuesOp(BufferObject& vectorBuffer) + : mVectorBuffer(&vectorBuffer) + { + } + + template + void operator()(typename GridType::ConstPtr grid) + { + using openvdb::Index64; + + using ValueType = typename GridType::ValueType; + using TreeType = typename GridType::TreeType; + using BoolTreeT = typename TreeType::template ValueConverter::Type; + + + const TreeType& tree = grid->tree(); + + double length = 0.0; + { + ValueType minVal, maxVal; + tree.evalMinMax(minVal, maxVal); + length = maxVal.length(); + } + + typename BoolTreeT::Ptr mask(new BoolTreeT(false)); + mask->topologyUnion(tree); + mask->voxelizeActiveTiles(); + + ///@todo thread and restructure. + + const Index64 voxelCount = mask->activeLeafVoxelCount(); + + const Index64 pointCount = voxelCount * 2; + std::vector points(pointCount*3), colors(pointCount*3); + std::vector indices(pointCount); + + openvdb::Coord ijk; + openvdb::Vec3d pos, color, normal; + openvdb::tree::LeafManager leafs(*mask); + + openvdb::tree::ValueAccessor acc(tree); + + Index64 idx = 0, pt = 0, cc = 0; + for (Index64 l = 0, L = leafs.leafCount(); l < L; ++l) { + typename BoolTreeT::LeafNodeType::ValueOnIter iter = leafs.leaf(l).beginValueOn(); + for (; iter; ++iter) { + ijk = iter.getCoord(); + ValueType vec = acc.getValue(ijk); + + pos = grid->indexToWorld(ijk); + + points[idx++] = static_cast(pos[0]); + points[idx++] = static_cast(pos[1]); + points[idx++] = static_cast(pos[2]); + + indices[pt] = GLuint(pt); + ++pt; + indices[pt] = GLuint(pt); + + ++pt; + double w = vec.length() / length; + vec.normalize(); + pos += grid->voxelSize()[0] * 0.9 * vec; + + points[idx++] = static_cast(pos[0]); + points[idx++] = static_cast(pos[1]); + points[idx++] = static_cast(pos[2]); + + + color = w * openvdb::Vec3d(0.9, 0.3, 0.3) + + (1.0 - w) * openvdb::Vec3d(0.3, 0.3, 0.9); + + colors[cc++] = static_cast(color[0] * 0.3); + colors[cc++] = static_cast(color[1] * 0.3); + colors[cc++] = static_cast(color[2] * 0.3); + + colors[cc++] = static_cast(color[0]); + colors[cc++] = static_cast(color[1]); + colors[cc++] = static_cast(color[2]); + } + } + + mVectorBuffer->genVertexBuffer(points); + mVectorBuffer->genColorBuffer(colors); + mVectorBuffer->genIndexBuffer(indices, GL_LINES); + } + +private: + BufferObject *mVectorBuffer; + +}; // ActiveVectorValuesOp + + +class PointDataOp +{ +public: + using GLfloatVec = std::vector; + using GLuintVec = std::vector; + +private: + struct VectorAttributeWrapper + { + using ValueType = openvdb::Vec3f; + + struct Handle + { + explicit Handle(VectorAttributeWrapper& attribute): + mValues(attribute.mValues), mIndices(attribute.mIndices) {} + + void set(openvdb::Index offset, openvdb::Index/*unused*/, const ValueType& value) + { + if (mIndices) (*mIndices)[offset] = static_cast(offset); + offset *= 3; + for (int i = 0; i < 3; ++i, ++offset) { mValues[offset] = value[i]; } + } + private: + GLfloatVec& mValues; + GLuintVec* mIndices; + }; // struct Handle + + explicit VectorAttributeWrapper(GLfloatVec& values, GLuintVec* indices = nullptr): + mValues(values), mIndices(indices) {} + + void expand() {} + void compact() {} + private: + GLfloatVec& mValues; + GLuintVec* mIndices; + }; // struct VectorAttributeWrapper + +public: + explicit PointDataOp(BufferObject& buffer) : mBuffer(&buffer) {} + + template + void operator()(typename GridType::ConstPtr grid) + { + const typename GridType::TreeType& tree = grid->tree(); + + // obtain cumulative point offsets and total points + std::vector pointOffsets; + const openvdb::Index64 total = openvdb::points::pointOffsets(pointOffsets, tree); + + // @todo use glDrawArrays with GL_POINTS to avoid generating indices + GLfloatVec values(total * 3); + GLuintVec indices(total); + + VectorAttributeWrapper positionWrapper{values, &indices}; + openvdb::points::convertPointDataGridPosition(positionWrapper, *grid, pointOffsets, 0); + + // gen buffers and upload data to GPU + mBuffer->genVertexBuffer(values); + mBuffer->genIndexBuffer(indices, GL_POINTS); + + const auto leafIter = tree.cbeginLeaf(); + if (!leafIter) return; + + const size_t colorIdx = leafIter->attributeSet().find("Cd"); + if (colorIdx == openvdb::points::AttributeSet::INVALID_POS) return; + + const auto& colorArray = leafIter->constAttributeArray(colorIdx); + if (colorArray.template hasValueType()) { + VectorAttributeWrapper colorWrapper{values}; + openvdb::points::convertPointDataGridAttribute(colorWrapper, tree, pointOffsets, + /*startOffset=*/0, static_cast(colorIdx)); + + // gen color buffer + mBuffer->genColorBuffer(values); + } + } + +private: + BufferObject* mBuffer; +}; // PointDataOp + + +//////////////////////////////////////// + +// Active value render module + +VoxelModule::VoxelModule(const openvdb::GridBase::ConstPtr& grid): + mGrid(grid), + mIsInitialized(false) +{ + mFlatShader.setVertShader( + "#version 120\n" + "void main() {\n" + "gl_FrontColor = gl_Color;\n" + "gl_Position = ftransform();\n" + "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" + "}\n"); + + mFlatShader.setFragShader( + "#version 120\n" + "void main() {\n" + "gl_FragColor = gl_Color;}\n"); + + mFlatShader.build(); + + mSurfaceShader.setVertShader( + "#version 120\n" + "varying vec3 normal;\n" + "void main() {\n" + "gl_FrontColor = gl_Color;\n" + "normal = normalize(gl_NormalMatrix * gl_Normal);\n" + "gl_Position = ftransform();\n" + "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" + "}\n"); + + + mSurfaceShader.setFragShader( + "#version 120\n" + "varying vec3 normal;\n" + "void main() {\n" + "vec3 normalized_normal = normalize(normal);\n" + "float w = 0.5 * (1.0 + dot(normalized_normal, vec3(0.0, 1.0, 0.0)));\n" + "vec4 diffuseColor = w * gl_Color + (1.0 - w) * (gl_Color * 0.3);\n" + "gl_FragColor = diffuseColor;\n" + "}\n"); + + mSurfaceShader.build(); +} + + +void +VoxelModule::init() +{ + mIsInitialized = true; + + if (mGrid->isType()) { + mSurfaceBuffer.clear(); + PointDataOp drawPoints(mInteriorBuffer); + util::doProcessTypedGrid(mGrid, drawPoints); + } else { + ActiveScalarValuesOp drawScalars(mInteriorBuffer, mSurfaceBuffer); + if (!util::processTypedScalarOrPointDataGrid(mGrid, drawScalars)) { + ActiveVectorValuesOp drawVectors(mVectorBuffer); + if (!util::processTypedVectorGrid(mGrid, drawVectors)) { + OPENVDB_LOG_INFO("Ignoring unrecognized grid type " << mGrid->type() + << " during active value module initialization."); + } + } + } +} + + +void +VoxelModule::render() +{ + if (!mIsVisible) return; + if (!mIsInitialized) init(); + + mFlatShader.startShading(); + mInteriorBuffer.render(); + mVectorBuffer.render(); + mFlatShader.stopShading(); + + mSurfaceShader.startShading(); + mSurfaceBuffer.render(); + mSurfaceShader.stopShading(); +} + + +//////////////////////////////////////// + + +class MeshOp +{ +public: + MeshOp(BufferObject& buffer) : mBuffer(&buffer) {} + + template + void operator()(typename GridType::ConstPtr grid) + { + using openvdb::Index64; + + openvdb::tools::VolumeToMesh mesher( + grid->getGridClass() == openvdb::GRID_LEVEL_SET ? 0.0 : 0.01); + mesher(*grid); + + // Copy points and generate point normals. + std::vector points(mesher.pointListSize() * 3); + std::vector normals(mesher.pointListSize() * 3); + + openvdb::tree::ValueAccessor acc(grid->tree()); + openvdb::math::GenericMap map(grid->transform()); + openvdb::Coord ijk; + + for (Index64 n = 0, i = 0, N = mesher.pointListSize(); n < N; ++n) { + const openvdb::Vec3s& p = mesher.pointList()[n]; + points[i++] = p[0]; + points[i++] = p[1]; + points[i++] = p[2]; + } + + // Copy primitives + openvdb::tools::PolygonPoolList& polygonPoolList = mesher.polygonPoolList(); + Index64 numQuads = 0; + for (Index64 n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) { + numQuads += polygonPoolList[n].numQuads(); + } + + std::vector indices; + indices.reserve(numQuads * 4); + openvdb::Vec3d normal, e1, e2; + + for (Index64 n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) { + const openvdb::tools::PolygonPool& polygons = polygonPoolList[n]; + for (Index64 i = 0, I = polygons.numQuads(); i < I; ++i) { + const openvdb::Vec4I& quad = polygons.quad(i); + indices.push_back(quad[0]); + indices.push_back(quad[1]); + indices.push_back(quad[2]); + indices.push_back(quad[3]); + + e1 = mesher.pointList()[quad[1]]; + e1 -= mesher.pointList()[quad[0]]; + e2 = mesher.pointList()[quad[2]]; + e2 -= mesher.pointList()[quad[1]]; + normal = e1.cross(e2); + + const double length = normal.length(); + if (length > 1.0e-7) normal *= (1.0 / length); + + for (int v = 0; v < 4; ++v) { + normals[quad[v]*3] = static_cast(-normal[0]); + normals[quad[v]*3+1] = static_cast(-normal[1]); + normals[quad[v]*3+2] = static_cast(-normal[2]); + } + } + } + + // Construct and transfer GPU buffers. + mBuffer->genVertexBuffer(points); + mBuffer->genNormalBuffer(normals); + mBuffer->genIndexBuffer(indices, GL_QUADS); + } + +private: + BufferObject *mBuffer; + + static openvdb::Vec3s sNodeColors[]; + +}; // MeshOp + + +//////////////////////////////////////// + +// Meshing module + +MeshModule::MeshModule(const openvdb::GridBase::ConstPtr& grid): + mGrid(grid), + mIsInitialized(false) +{ + mShader.setVertShader( + "#version 120\n" + "varying vec3 normal;\n" + "void main() {\n" + "normal = normalize(gl_NormalMatrix * gl_Normal);\n" + "gl_Position = ftransform();\n" + "gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" + "}\n"); + + mShader.setFragShader( + "#version 120\n" + "varying vec3 normal;\n" + "const vec4 skyColor = vec4(0.9, 0.9, 1.0, 1.0);\n" + "const vec4 groundColor = vec4(0.3, 0.3, 0.2, 1.0);\n" + "void main() {\n" + "vec3 normalized_normal = normalize(normal);\n" + "float w = 0.5 * (1.0 + dot(normalized_normal, vec3(0.0, 1.0, 0.0)));\n" + "vec4 diffuseColor = w * skyColor + (1.0 - w) * groundColor;\n" + "gl_FragColor = diffuseColor;\n" + "}\n"); + + mShader.build(); +} + + +void +MeshModule::init() +{ + mIsInitialized = true; + + MeshOp drawMesh(mBufferObject); + + if (!util::processTypedScalarGrid(mGrid, drawMesh)) { + OPENVDB_LOG_INFO( + "Ignoring non-scalar grid type during mesh module initialization."); + } +} + + +void +MeshModule::render() +{ + if (!mIsVisible) return; + if (!mIsInitialized) init(); + + mShader.startShading(); + + mBufferObject.render(); + + mShader.stopShading(); +} + +} // namespace openvdb_viewer diff --git a/openvdb/viewer/RenderModules.h b/openvdb/viewer/RenderModules.h new file mode 100644 index 00000000..0f2a3ce0 --- /dev/null +++ b/openvdb/viewer/RenderModules.h @@ -0,0 +1,182 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_VIEWER_RENDERMODULES_HAS_BEEN_INCLUDED +#define OPENVDB_VIEWER_RENDERMODULES_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__APPLE__) || defined(MACOSX) +#include +#include +#else +#include +#include +#endif + + +namespace openvdb_viewer { + +// OpenGL helper objects + +class BufferObject +{ +public: + BufferObject(); + ~BufferObject(); + + void render() const; + + /// @note accepted @c primType: GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, + /// GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, + /// GL_QUAD_STRIP, GL_QUADS and GL_POLYGON + void genIndexBuffer(const std::vector&, GLenum primType); + + void genVertexBuffer(const std::vector&); + void genNormalBuffer(const std::vector&); + void genColorBuffer(const std::vector&); + + void clear(); + +private: + GLuint mVertexBuffer, mNormalBuffer, mIndexBuffer, mColorBuffer; + GLenum mPrimType; + GLsizei mPrimNum; +}; + + +class ShaderProgram +{ +public: + ShaderProgram(); + ~ShaderProgram(); + + void setVertShader(const std::string&); + void setFragShader(const std::string&); + + void build(); + void build(const std::vector& attributes); + + void startShading() const; + void stopShading() const; + + void clear(); + +private: + GLuint mProgram, mVertShader, mFragShader; +}; + + +//////////////////////////////////////// + + +/// @brief interface class +class RenderModule +{ +public: + virtual ~RenderModule() {} + + virtual void render() = 0; + + bool visible() { return mIsVisible; } + void setVisible(bool b) { mIsVisible = b; } + +protected: + RenderModule(): mIsVisible(true) {} + + bool mIsVisible; +}; + + +//////////////////////////////////////// + + +/// @brief Basic render module, axis gnomon and ground plane. +class ViewportModule: public RenderModule +{ +public: + ViewportModule(); + ~ViewportModule() override = default; + + void render() override; + +private: + float mAxisGnomonScale, mGroundPlaneScale; +}; + + +//////////////////////////////////////// + + +/// @brief Tree topology render module +class TreeTopologyModule: public RenderModule +{ +public: + TreeTopologyModule(const openvdb::GridBase::ConstPtr&); + ~TreeTopologyModule() override = default; + + void render() override; + +private: + void init(); + + const openvdb::GridBase::ConstPtr& mGrid; + BufferObject mBufferObject; + bool mIsInitialized; + ShaderProgram mShader; +}; + + +//////////////////////////////////////// + + +/// @brief Module to render active voxels as points +class VoxelModule: public RenderModule +{ +public: + VoxelModule(const openvdb::GridBase::ConstPtr&); + ~VoxelModule() override = default; + + void render() override; + +private: + void init(); + + const openvdb::GridBase::ConstPtr& mGrid; + BufferObject mInteriorBuffer, mSurfaceBuffer, mVectorBuffer; + bool mIsInitialized; + ShaderProgram mFlatShader, mSurfaceShader; +}; + + +//////////////////////////////////////// + + +/// @brief Surfacing render module +class MeshModule: public RenderModule +{ +public: + MeshModule(const openvdb::GridBase::ConstPtr&); + ~MeshModule() override = default; + + void render() override; + +private: + void init(); + + const openvdb::GridBase::ConstPtr& mGrid; + BufferObject mBufferObject; + bool mIsInitialized; + ShaderProgram mShader; +}; + +} // namespace openvdb_viewer + +#endif // OPENVDB_VIEWER_RENDERMODULES_HAS_BEEN_INCLUDED diff --git a/openvdb/viewer/Viewer.cc b/openvdb/viewer/Viewer.cc new file mode 100644 index 00000000..21cd9fa8 --- /dev/null +++ b/openvdb/viewer/Viewer.cc @@ -0,0 +1,1219 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Viewer.h" + +#include "Camera.h" +#include "ClipBox.h" +#include "Font.h" +#include "RenderModules.h" +#include // for formattedInt() +#include +#include +#include +#include // for OPENVDB_LIBRARY_MAJOR_VERSION, etc. +#include +#include +#include // for fabs() +#include // for std::setprecision() +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENVDB_USE_GLFW_3 +//#define GLFW_INCLUDE_GLU +#include +#else // if !defined(OPENVDB_USE_GLFW_3) +#if defined(__APPLE__) || defined(MACOSX) +#include +#include +#else +#include +#include +#endif +#include +#endif // !defined(OPENVDB_USE_GLFW_3) + + +namespace openvdb_viewer { + +class ViewerImpl +{ +public: + using CameraPtr = std::shared_ptr; + using ClipBoxPtr = std::shared_ptr; + using RenderModulePtr = std::shared_ptr; + + ViewerImpl(); + + void init(const std::string& progName); + + std::string getVersionString() const; + + bool isOpen() const; + bool open(int width = 900, int height = 800); + void view(const openvdb::GridCPtrVec&); + void handleEvents(); + void close(); + + void resize(int width, int height); + + void showPrevGrid(); + void showNextGrid(); + + bool needsDisplay(); + void setNeedsDisplay(); + + void toggleRenderModule(size_t n); + void toggleInfoText(); + + // Internal + void render(); + void interrupt(); + void setWindowTitle(double fps = 0.0); + void showNthGrid(size_t n); + void updateCutPlanes(int wheelPos); + void swapBuffers(); + + void keyCallback(int key, int action); + void mouseButtonCallback(int button, int action); + void mousePosCallback(int x, int y); + void mouseWheelCallback(int pos); + void windowSizeCallback(int width, int height); + void windowRefreshCallback(); + + static openvdb::BBoxd worldSpaceBBox(const openvdb::math::Transform&, + const openvdb::CoordBBox&); + static void sleep(double seconds); + +private: + bool mDidInit; + CameraPtr mCamera; + ClipBoxPtr mClipBox; + RenderModulePtr mViewportModule; + std::vector mRenderModules; + openvdb::GridCPtrVec mGrids; + size_t mGridIdx, mUpdates; + std::string mGridName, mProgName, mGridInfo, mTransformInfo, mTreeInfo; + int mWheelPos; + bool mShiftIsDown, mCtrlIsDown, mShowInfo; + bool mInterrupt; +#if GLFW_VERSION_MAJOR >= 3 + GLFWwindow* mWindow; +#endif +}; // class ViewerImpl + + +class ThreadManager +{ +public: + ThreadManager(); + + void view(const openvdb::GridCPtrVec& gridList); + void close(); + void resize(int width, int height); + +private: + void doView(); + static void* doViewTask(void* arg); + + tbb::atomic mRedisplay; + bool mClose, mHasThread; + boost::thread mThread; + openvdb::GridCPtrVec mGrids; +}; + + +//////////////////////////////////////// + + +namespace { + +ViewerImpl* sViewer = nullptr; +ThreadManager* sThreadMgr = nullptr; +tbb::mutex sLock; + + +#if GLFW_VERSION_MAJOR >= 3 +void +keyCB(GLFWwindow*, int key, int /*scancode*/, int action, int /*modifiers*/) +#else +void +keyCB(int key, int action) +#endif +{ + if (sViewer) sViewer->keyCallback(key, action); +} + + +#if GLFW_VERSION_MAJOR >= 3 +void +mouseButtonCB(GLFWwindow*, int button, int action, int /*modifiers*/) +#else +void +mouseButtonCB(int button, int action) +#endif +{ + if (sViewer) sViewer->mouseButtonCallback(button, action); +} + + +#if GLFW_VERSION_MAJOR >= 3 +void +mousePosCB(GLFWwindow*, double x, double y) +{ + if (sViewer) sViewer->mousePosCallback(int(x), int(y)); +} +#else +void +mousePosCB(int x, int y) +{ + if (sViewer) sViewer->mousePosCallback(x, y); +} +#endif + + +#if GLFW_VERSION_MAJOR >= 3 +void +mouseWheelCB(GLFWwindow*, double /*xoffset*/, double yoffset) +{ + if (sViewer) sViewer->mouseWheelCallback(int(yoffset)); +} +#else +void +mouseWheelCB(int pos) +{ + if (sViewer) sViewer->mouseWheelCallback(pos); +} +#endif + + +#if GLFW_VERSION_MAJOR >= 3 +void +windowSizeCB(GLFWwindow*, int width, int height) +#else +void +windowSizeCB(int width, int height) +#endif +{ + if (sViewer) sViewer->windowSizeCallback(width, height); +} + + +#if GLFW_VERSION_MAJOR >= 3 +void +windowRefreshCB(GLFWwindow*) +#else +void +windowRefreshCB() +#endif +{ + if (sViewer) sViewer->windowRefreshCallback(); +} + +} // unnamed namespace + + +//////////////////////////////////////// + + +Viewer +init(const std::string& progName, bool background) +{ + if (sViewer == nullptr) { + tbb::mutex::scoped_lock lock(sLock); + if (sViewer == nullptr) { + OPENVDB_START_THREADSAFE_STATIC_WRITE + sViewer = new ViewerImpl; + OPENVDB_FINISH_THREADSAFE_STATIC_WRITE + } + } + sViewer->init(progName); + + if (background) { + if (sThreadMgr == nullptr) { + tbb::mutex::scoped_lock lock(sLock); + if (sThreadMgr == nullptr) { + OPENVDB_START_THREADSAFE_STATIC_WRITE + sThreadMgr = new ThreadManager; + OPENVDB_FINISH_THREADSAFE_STATIC_WRITE + } + } + } else { + if (sThreadMgr != nullptr) { + tbb::mutex::scoped_lock lock(sLock); + delete sThreadMgr; + OPENVDB_START_THREADSAFE_STATIC_WRITE + sThreadMgr = nullptr; + OPENVDB_FINISH_THREADSAFE_STATIC_WRITE + } + } + + return Viewer(); +} + + +void +exit() +{ +#if GLFW_VERSION_MAJOR >= 3 + // Prior to GLFW 3, glfwTerminate() was called automatically from + // an atexit() function installed by glfwInit(), so this was not needed. + // But GLFW 3 does not register an atexit() function. + glfwTerminate(); +#endif +} + + +//////////////////////////////////////// + + +Viewer::Viewer() +{ + OPENVDB_LOG_DEBUG_RUNTIME("constructed Viewer from thread " << boost::this_thread::get_id()); +} + + +void +Viewer::open(int width, int height) +{ + if (sViewer) sViewer->open(width, height); +} + + +void +Viewer::view(const openvdb::GridCPtrVec& grids) +{ + if (sThreadMgr) { + sThreadMgr->view(grids); + } else if (sViewer) { + sViewer->view(grids); + } +} + + +void +Viewer::handleEvents() +{ + if (sViewer) sViewer->handleEvents(); +} + + +void +Viewer::close() +{ + if (sThreadMgr) sThreadMgr->close(); + else if (sViewer) sViewer->close(); +} + + +void +Viewer::resize(int width, int height) +{ + if (sViewer) sViewer->resize(width, height); +} + + +std::string +Viewer::getVersionString() const +{ + std::string version; + if (sViewer) version = sViewer->getVersionString(); + return version; +} + + +//////////////////////////////////////// + + +ThreadManager::ThreadManager() + : mClose(false) + , mHasThread(false) +{ + mRedisplay = false; +} + + +void +ThreadManager::view(const openvdb::GridCPtrVec& gridList) +{ + if (!sViewer) return; + + mGrids = gridList; + mClose = false; + mRedisplay = true; + + if (!mHasThread) { + mThread = boost::thread(doViewTask, this); + mHasThread = true; + } +} + + +void +ThreadManager::close() +{ + if (!sViewer) return; + + // Tell the viewer thread to exit. + mRedisplay = false; + mClose = true; + // Tell the viewer to terminate its event loop. + sViewer->interrupt(); + + if (mHasThread) { + mThread.join(); + mHasThread = false; + } + + // Tell the viewer to close its window. + sViewer->close(); +} + + +void +ThreadManager::doView() +{ + // This function runs in its own thread. + // The mClose and mRedisplay flags are set from the main thread. + while (!mClose) { + if (mRedisplay.compare_and_swap(/*set to*/false, /*if*/true)) { + if (sViewer) sViewer->view(mGrids); + } + sViewer->sleep(0.5/*sec*/); + } +} + + +//static +void* +ThreadManager::doViewTask(void* arg) +{ + if (ThreadManager* self = static_cast(arg)) { + self->doView(); + } + return nullptr; +} + + +//////////////////////////////////////// + + +ViewerImpl::ViewerImpl() + : mDidInit(false) + , mCamera(new Camera) + , mClipBox(new ClipBox) + , mGridIdx(0) + , mUpdates(0) + , mWheelPos(0) + , mShiftIsDown(false) + , mCtrlIsDown(false) + , mShowInfo(true) + , mInterrupt(false) +#if GLFW_VERSION_MAJOR >= 3 + , mWindow(nullptr) +#endif +{ +} + + +void +ViewerImpl::init(const std::string& progName) +{ + mProgName = progName; + + if (!mDidInit) { +#if GLFW_VERSION_MAJOR >= 3 + struct Local { + static void errorCB(int error, const char* descr) { + OPENVDB_LOG_ERROR("GLFW Error " << error << ": " << descr); + } + }; + glfwSetErrorCallback(Local::errorCB); +#endif + if (glfwInit() == GL_TRUE) { + OPENVDB_LOG_DEBUG_RUNTIME("initialized GLFW from thread " + << boost::this_thread::get_id()); + mDidInit = true; + } else { + OPENVDB_LOG_ERROR("GLFW initialization failed"); + } + } + mViewportModule.reset(new ViewportModule); +} + + +std::string +ViewerImpl::getVersionString() const +{ + std::ostringstream ostr; + + ostr << "OpenVDB: " << + openvdb::OPENVDB_LIBRARY_MAJOR_VERSION << "." << + openvdb::OPENVDB_LIBRARY_MINOR_VERSION << "." << + openvdb::OPENVDB_LIBRARY_PATCH_VERSION; + + int major, minor, rev; + glfwGetVersion(&major, &minor, &rev); + ostr << ", " << "GLFW: " << major << "." << minor << "." << rev; + + if (mDidInit) { + ostr << ", " << "OpenGL: "; +#if GLFW_VERSION_MAJOR >= 3 + std::shared_ptr wPtr; + GLFWwindow* w = mWindow; + if (!w) { + wPtr.reset(glfwCreateWindow(100, 100, "", nullptr, nullptr), &glfwDestroyWindow); + w = wPtr.get(); + } + if (w) { + ostr << glfwGetWindowAttrib(w, GLFW_CONTEXT_VERSION_MAJOR) << "." + << glfwGetWindowAttrib(w, GLFW_CONTEXT_VERSION_MINOR) << "." + << glfwGetWindowAttrib(w, GLFW_CONTEXT_REVISION); + } +#else + if (!glfwGetWindowParam(GLFW_OPENED)) { + if (glfwOpenWindow(100, 100, 8, 8, 8, 8, 24, 0, GLFW_WINDOW)) { + ostr << glGetString(GL_VERSION); + glfwCloseWindow(); + } + } else { + ostr << glGetString(GL_VERSION); + } +#endif + } + return ostr.str(); +} + + +#if GLFW_VERSION_MAJOR >= 3 +bool +ViewerImpl::open(int width, int height) +{ + if (mWindow == nullptr) { + glfwWindowHint(GLFW_RED_BITS, 8); + glfwWindowHint(GLFW_GREEN_BITS, 8); + glfwWindowHint(GLFW_BLUE_BITS, 8); + glfwWindowHint(GLFW_ALPHA_BITS, 8); + glfwWindowHint(GLFW_DEPTH_BITS, 32); + glfwWindowHint(GLFW_STENCIL_BITS, 0); + + mWindow = glfwCreateWindow( + width, height, mProgName.c_str(), /*monitor=*/nullptr, /*share=*/nullptr); + + OPENVDB_LOG_DEBUG_RUNTIME("created window " << std::hex << mWindow << std::dec + << " from thread " << boost::this_thread::get_id()); + + if (mWindow != nullptr) { + // Temporarily make the new window the current context, then create a font. + std::shared_ptr curWindow( + glfwGetCurrentContext(), glfwMakeContextCurrent); + glfwMakeContextCurrent(mWindow); + BitmapFont13::initialize(); + } + } + mCamera->setWindow(mWindow); + + if (mWindow != nullptr) { + glfwSetKeyCallback(mWindow, keyCB); + glfwSetMouseButtonCallback(mWindow, mouseButtonCB); + glfwSetCursorPosCallback(mWindow, mousePosCB); + glfwSetScrollCallback(mWindow, mouseWheelCB); + glfwSetWindowSizeCallback(mWindow, windowSizeCB); + glfwSetWindowRefreshCallback(mWindow, windowRefreshCB); + } + return (mWindow != nullptr); +} +#else // if GLFW_VERSION_MAJOR <= 2 +bool +ViewerImpl::open(int width, int height) +{ + if (!glfwGetWindowParam(GLFW_OPENED)) { + if (!glfwOpenWindow(width, height, + 8, 8, 8, 8, // # of R,G,B, & A bits + 32, 0, // # of depth & stencil buffer bits + GLFW_WINDOW)) // either GLFW_WINDOW or GLFW_FULLSCREEN + { + return false; + } + } + glfwSetWindowTitle(mProgName.c_str()); + + BitmapFont13::initialize(); + + glfwSetKeyCallback(keyCB); + glfwSetMouseButtonCallback(mouseButtonCB); + glfwSetMousePosCallback(mousePosCB); + glfwSetMouseWheelCallback(mouseWheelCB); + glfwSetWindowSizeCallback(windowSizeCB); + glfwSetWindowRefreshCallback(windowRefreshCB); + + return true; +} +#endif + + +bool +ViewerImpl::isOpen() const +{ +#if GLFW_VERSION_MAJOR >= 3 + return (mWindow != nullptr); +#else + return glfwGetWindowParam(GLFW_OPENED); +#endif +} + + +// Set a flag so as to break out of the event loop on the next iteration. +// (Useful only if the event loop is running in a separate thread.) +void +ViewerImpl::interrupt() +{ + mInterrupt = true; +#if GLFW_VERSION_MAJOR >= 3 + if (mWindow) glfwSetWindowShouldClose(mWindow, true); +#endif +} + + +void +ViewerImpl::handleEvents() +{ +#if GLFW_VERSION_MAJOR >= 3 + glfwPollEvents(); +#endif +} + + +void +ViewerImpl::close() +{ +#if GLFW_VERSION_MAJOR >= 3 + OPENVDB_LOG_DEBUG_RUNTIME("about to close window " << std::hex << mWindow << std::dec + << " from thread " << boost::this_thread::get_id()); +#else + OPENVDB_LOG_DEBUG_RUNTIME("about to close window from thread " + << boost::this_thread::get_id()); +#endif + + mViewportModule.reset(); + mRenderModules.clear(); +#if GLFW_VERSION_MAJOR >= 3 + mCamera->setWindow(nullptr); + GLFWwindow* win = mWindow; + mWindow = nullptr; + glfwDestroyWindow(win); + OPENVDB_LOG_DEBUG_RUNTIME("destroyed window " << std::hex << win << std::dec + << " from thread " << boost::this_thread::get_id()); +#else + glfwCloseWindow(); +#endif +} + + +//////////////////////////////////////// + + +void +ViewerImpl::view(const openvdb::GridCPtrVec& gridList) +{ + if (!isOpen()) return; + + mGrids = gridList; + mGridIdx = size_t(-1); + mGridName.clear(); + + // Compute the combined bounding box of all the grids. + openvdb::BBoxd bbox(openvdb::Vec3d(0.0), openvdb::Vec3d(0.0)); + if (!gridList.empty()) { + bbox = worldSpaceBBox( + gridList[0]->transform(), gridList[0]->evalActiveVoxelBoundingBox()); + openvdb::Vec3d voxelSize = gridList[0]->voxelSize(); + + for (size_t n = 1; n < gridList.size(); ++n) { + bbox.expand(worldSpaceBBox(gridList[n]->transform(), + gridList[n]->evalActiveVoxelBoundingBox())); + + voxelSize = minComponent(voxelSize, gridList[n]->voxelSize()); + } + mClipBox->setStepSize(voxelSize); + } + mClipBox->setBBox(bbox); + +#if GLFW_VERSION_MAJOR >= 3 + // Prepare window for rendering. + glfwMakeContextCurrent(mWindow); +#endif + + { + // set up camera + openvdb::Vec3d extents = bbox.extents(); + double maxExtent = std::max(extents[0], std::max(extents[1], extents[2])); + mCamera->setTarget(bbox.getCenter(), maxExtent); + mCamera->lookAtTarget(); + mCamera->setSpeed(); + } + + swapBuffers(); + setNeedsDisplay(); + + + ////////// + + // Screen color + glClearColor(0.85f, 0.85f, 0.85f, 0.0f); + + glDepthFunc(GL_LESS); + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + + glPointSize(4); + glLineWidth(2); + ////////// + + // construct render modules + showNthGrid(/*n=*/0); + + + // main loop + + size_t frame = 0; + double time = glfwGetTime(); + + glfwSwapInterval(1); + +#if GLFW_VERSION_MAJOR >= 3 + OPENVDB_LOG_DEBUG_RUNTIME("starting to render in window " << std::hex << mWindow << std::dec + << " from thread " << boost::this_thread::get_id()); +#else + OPENVDB_LOG_DEBUG_RUNTIME("starting to render from thread " << boost::this_thread::get_id()); +#endif + + mInterrupt = false; + for (bool stop = false; !stop; ) { + if (needsDisplay()) render(); + + // eval fps + ++frame; + double elapsed = glfwGetTime() - time; + if (elapsed > 1.0) { + time = glfwGetTime(); + setWindowTitle(/*fps=*/double(frame) / elapsed); + frame = 0; + } + + // Swap front and back buffers + swapBuffers(); + + sleep(0.01/*sec*/); + + // Exit if the Esc key is pressed or the window is closed. +#if GLFW_VERSION_MAJOR >= 3 + handleEvents(); + stop = (mInterrupt || glfwWindowShouldClose(mWindow)); +#else + stop = (mInterrupt || glfwGetKey(GLFW_KEY_ESC) || !glfwGetWindowParam(GLFW_OPENED)); +#endif + } + +#if GLFW_VERSION_MAJOR >= 3 + if (glfwGetCurrentContext() == mWindow) { ///< @todo not thread-safe + // Detach this viewer's GL context. + glfwMakeContextCurrent(nullptr); + OPENVDB_LOG_DEBUG_RUNTIME("detached window " << std::hex << mWindow << std::dec + << " from thread " << boost::this_thread::get_id()); + } +#endif + +#if GLFW_VERSION_MAJOR >= 3 + OPENVDB_LOG_DEBUG_RUNTIME("finished rendering in window " << std::hex << mWindow << std::dec + << " from thread " << boost::this_thread::get_id()); +#else + OPENVDB_LOG_DEBUG_RUNTIME("finished rendering from thread " << boost::this_thread::get_id()); +#endif +} + + +//////////////////////////////////////// + + +void +ViewerImpl::resize(int width, int height) +{ +#if GLFW_VERSION_MAJOR >= 3 + if (mWindow) glfwSetWindowSize(mWindow, width, height); +#else + glfwSetWindowSize(width, height); +#endif +} + + +//////////////////////////////////////// + + +void +ViewerImpl::render() +{ +#if GLFW_VERSION_MAJOR >= 3 + if (mWindow == nullptr) return; + + // Prepare window for rendering. + glfwMakeContextCurrent(mWindow); +#endif + + mCamera->aim(); + + // draw scene + mViewportModule->render(); // ground plane. + + mClipBox->render(); + mClipBox->enableClipping(); + + for (size_t n = 0, N = mRenderModules.size(); n < N; ++n) { + mRenderModules[n]->render(); + } + + mClipBox->disableClipping(); + + // Render text + + if (mShowInfo) { + BitmapFont13::enableFontRendering(); + + glColor3d(0.2, 0.2, 0.2); + + int width, height; +#if GLFW_VERSION_MAJOR >= 3 + glfwGetFramebufferSize(mWindow, &width, &height); +#else + glfwGetWindowSize(&width, &height); +#endif + + BitmapFont13::print(10, height - 13 - 10, mGridInfo); + BitmapFont13::print(10, height - 13 - 30, mTransformInfo); + BitmapFont13::print(10, height - 13 - 50, mTreeInfo); + + // Indicate via their hotkeys which render modules are enabled. + std::string keys = "123"; + for (auto n: {0, 1, 2}) { if (!mRenderModules[n]->visible()) keys[n] = ' '; } + BitmapFont13::print(width - 10 - 30, 10, keys); + glColor3d(0.75, 0.75, 0.75); + BitmapFont13::print(width - 10 - 30, 10, "123"); + + BitmapFont13::disableFontRendering(); + } +} + + +//////////////////////////////////////// + + +//static +void +ViewerImpl::sleep(double secs) +{ + secs = fabs(secs); + int isecs = int(secs); + boost::this_thread::sleep(boost::posix_time::seconds(isecs)); +} + + +//////////////////////////////////////// + + +//static +openvdb::BBoxd +ViewerImpl::worldSpaceBBox(const openvdb::math::Transform& xform, const openvdb::CoordBBox& bbox) +{ + openvdb::Vec3d pMin = openvdb::Vec3d(std::numeric_limits::max()); + openvdb::Vec3d pMax = -pMin; + + const openvdb::Coord& min = bbox.min(); + const openvdb::Coord& max = bbox.max(); + openvdb::Coord ijk; + + // corner 1 + openvdb::Vec3d ptn = xform.indexToWorld(min); + for (int i = 0; i < 3; ++i) { + if (ptn[i] < pMin[i]) pMin[i] = ptn[i]; + if (ptn[i] > pMax[i]) pMax[i] = ptn[i]; + } + + // corner 2 + ijk[0] = min.x(); + ijk[1] = min.y(); + ijk[2] = max.z(); + ptn = xform.indexToWorld(ijk); + for (int i = 0; i < 3; ++i) { + if (ptn[i] < pMin[i]) pMin[i] = ptn[i]; + if (ptn[i] > pMax[i]) pMax[i] = ptn[i]; + } + + // corner 3 + ijk[0] = max.x(); + ijk[1] = min.y(); + ijk[2] = max.z(); + ptn = xform.indexToWorld(ijk); + for (int i = 0; i < 3; ++i) { + if (ptn[i] < pMin[i]) pMin[i] = ptn[i]; + if (ptn[i] > pMax[i]) pMax[i] = ptn[i]; + } + + // corner 4 + ijk[0] = max.x(); + ijk[1] = min.y(); + ijk[2] = min.z(); + ptn = xform.indexToWorld(ijk); + for (int i = 0; i < 3; ++i) { + if (ptn[i] < pMin[i]) pMin[i] = ptn[i]; + if (ptn[i] > pMax[i]) pMax[i] = ptn[i]; + } + + // corner 5 + ijk[0] = min.x(); + ijk[1] = max.y(); + ijk[2] = min.z(); + ptn = xform.indexToWorld(ijk); + for (int i = 0; i < 3; ++i) { + if (ptn[i] < pMin[i]) pMin[i] = ptn[i]; + if (ptn[i] > pMax[i]) pMax[i] = ptn[i]; + } + + // corner 6 + ijk[0] = min.x(); + ijk[1] = max.y(); + ijk[2] = max.z(); + ptn = xform.indexToWorld(ijk); + for (int i = 0; i < 3; ++i) { + if (ptn[i] < pMin[i]) pMin[i] = ptn[i]; + if (ptn[i] > pMax[i]) pMax[i] = ptn[i]; + } + + + // corner 7 + ptn = xform.indexToWorld(max); + for (int i = 0; i < 3; ++i) { + if (ptn[i] < pMin[i]) pMin[i] = ptn[i]; + if (ptn[i] > pMax[i]) pMax[i] = ptn[i]; + } + + // corner 8 + ijk[0] = max.x(); + ijk[1] = max.y(); + ijk[2] = min.z(); + ptn = xform.indexToWorld(ijk); + for (int i = 0; i < 3; ++i) { + if (ptn[i] < pMin[i]) pMin[i] = ptn[i]; + if (ptn[i] > pMax[i]) pMax[i] = ptn[i]; + } + + return openvdb::BBoxd(pMin, pMax); +} + + +//////////////////////////////////////// + + +void +ViewerImpl::updateCutPlanes(int wheelPos) +{ + double speed = std::abs(mWheelPos - wheelPos); + if (mWheelPos < wheelPos) mClipBox->update(speed); + else mClipBox->update(-speed); + setNeedsDisplay(); +} + + +//////////////////////////////////////// + + +void +ViewerImpl::swapBuffers() +{ +#if GLFW_VERSION_MAJOR >= 3 + glfwSwapBuffers(mWindow); +#else + glfwSwapBuffers(); +#endif +} + + +//////////////////////////////////////// + + +void +ViewerImpl::setWindowTitle(double fps) +{ + std::ostringstream ss; + ss << mProgName << ": " + << (mGridName.empty() ? std::string("OpenVDB") : mGridName) + << " (" << (mGridIdx + 1) << " of " << mGrids.size() << ") @ " + << std::setprecision(1) << std::fixed << fps << " fps"; +#if GLFW_VERSION_MAJOR >= 3 + if (mWindow) glfwSetWindowTitle(mWindow, ss.str().c_str()); +#else + glfwSetWindowTitle(ss.str().c_str()); +#endif +} + + +//////////////////////////////////////// + + +void +ViewerImpl::showPrevGrid() +{ + if (const size_t numGrids = mGrids.size()) { + size_t idx = ((numGrids + mGridIdx) - 1) % numGrids; + showNthGrid(idx); + } +} + + +void +ViewerImpl::showNextGrid() +{ + if (const size_t numGrids = mGrids.size()) { + size_t idx = (mGridIdx + 1) % numGrids; + showNthGrid(idx); + } +} + + +void +ViewerImpl::showNthGrid(size_t n) +{ + if (mGrids.empty()) return; + n = n % mGrids.size(); + if (n == mGridIdx) return; + + mGridName = mGrids[n]->getName(); + mGridIdx = n; + + // save render settings + std::vector active(mRenderModules.size()); + for (size_t i = 0, I = active.size(); i < I; ++i) { + active[i] = mRenderModules[i]->visible(); + } + + mRenderModules.clear(); + mRenderModules.push_back(RenderModulePtr(new TreeTopologyModule(mGrids[n]))); + mRenderModules.push_back(RenderModulePtr(new MeshModule(mGrids[n]))); + mRenderModules.push_back(RenderModulePtr(new VoxelModule(mGrids[n]))); + + if (active.empty()) { + for (size_t i = 1, I = mRenderModules.size(); i < I; ++i) { + mRenderModules[i]->setVisible(false); + } + } else { + for (size_t i = 0, I = active.size(); i < I; ++i) { + mRenderModules[i]->setVisible(active[i]); + } + } + + // Collect info + { + std::ostringstream ostrm; + std::string s = mGrids[n]->getName(); + const openvdb::GridClass cls = mGrids[n]->getGridClass(); + if (!s.empty()) ostrm << s << " / "; + ostrm << mGrids[n]->valueType() << " / "; + if (cls == openvdb::GRID_UNKNOWN) ostrm << " class unknown"; + else ostrm << " " << openvdb::GridBase::gridClassToString(cls); + mGridInfo = ostrm.str(); + } + { + openvdb::Coord dim = mGrids[n]->evalActiveVoxelDim(); + std::ostringstream ostrm; + ostrm << dim[0] << " x " << dim[1] << " x " << dim[2] + << " / voxel size " << std::setprecision(4) << mGrids[n]->voxelSize()[0] + << " (" << mGrids[n]->transform().mapType() << ")"; + mTransformInfo = ostrm.str(); + } + { + std::ostringstream ostrm; + const openvdb::Index64 count = mGrids[n]->activeVoxelCount(); + ostrm << openvdb::util::formattedInt(count) + << " active voxel" << (count == 1 ? "" : "s"); + mTreeInfo = ostrm.str(); + } + { + if (mGrids[n]->isType()) { + const openvdb::points::PointDataGrid::ConstPtr points = + openvdb::gridConstPtrCast(mGrids[n]); + const openvdb::Index64 count = openvdb::points::pointCount(points->tree()); + std::ostringstream ostrm; + ostrm << " / " << openvdb::util::formattedInt(count) + << " point" << (count == 1 ? "" : "s"); + mTreeInfo.append(ostrm.str()); + } + } + + setWindowTitle(); +} + + +//////////////////////////////////////// + + +void +ViewerImpl::keyCallback(int key, int action) +{ + mCamera->keyCallback(key, action); + +#if GLFW_VERSION_MAJOR >= 3 + if (mWindow == nullptr) return; + const bool keyPress = (glfwGetKey(mWindow, key) == GLFW_PRESS); + /// @todo Should use "modifiers" argument to keyCB(). + mShiftIsDown = glfwGetKey(mWindow, GLFW_KEY_LEFT_SHIFT); + mCtrlIsDown = glfwGetKey(mWindow, GLFW_KEY_LEFT_CONTROL); +#else + const bool keyPress = glfwGetKey(key) == GLFW_PRESS; + mShiftIsDown = glfwGetKey(GLFW_KEY_LSHIFT); + mCtrlIsDown = glfwGetKey(GLFW_KEY_LCTRL); +#endif + + if (keyPress) { + switch (key) { + case '1': case GLFW_KEY_KP_1: + toggleRenderModule(0); + break; + case '2': case GLFW_KEY_KP_2: + toggleRenderModule(1); + break; + case '3': case GLFW_KEY_KP_3: + toggleRenderModule(2); + break; + case 'c': case 'C': + mClipBox->reset(); + break; + case 'h': case 'H': // center home + mCamera->lookAt(openvdb::Vec3d(0.0), 10.0); + break; + case 'g': case 'G': // center geometry + mCamera->lookAtTarget(); + break; + case 'i': case 'I': + toggleInfoText(); + break; + case GLFW_KEY_LEFT: + showPrevGrid(); + break; + case GLFW_KEY_RIGHT: + showNextGrid(); + break; +#if GLFW_VERSION_MAJOR >= 3 + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(mWindow, true); + break; +#endif + } + } + + switch (key) { + case 'x': case 'X': + mClipBox->activateXPlanes() = keyPress; + break; + case 'y': case 'Y': + mClipBox->activateYPlanes() = keyPress; + break; + case 'z': case 'Z': + mClipBox->activateZPlanes() = keyPress; + break; + } + + mClipBox->shiftIsDown() = mShiftIsDown; + mClipBox->ctrlIsDown() = mCtrlIsDown; + + setNeedsDisplay(); +} + + +void +ViewerImpl::mouseButtonCallback(int button, int action) +{ + mCamera->mouseButtonCallback(button, action); + mClipBox->mouseButtonCallback(button, action); + if (mCamera->needsDisplay()) setNeedsDisplay(); +} + + +void +ViewerImpl::mousePosCallback(int x, int y) +{ + bool handled = mClipBox->mousePosCallback(x, y); + if (!handled) mCamera->mousePosCallback(x, y); + if (mCamera->needsDisplay()) setNeedsDisplay(); +} + + +void +ViewerImpl::mouseWheelCallback(int pos) +{ +#if GLFW_VERSION_MAJOR >= 3 + pos += mWheelPos; +#endif + if (mClipBox->isActive()) { + updateCutPlanes(pos); + } else { + mCamera->mouseWheelCallback(pos, mWheelPos); + if (mCamera->needsDisplay()) setNeedsDisplay(); + } + + mWheelPos = pos; +} + + +void +ViewerImpl::windowSizeCallback(int, int) +{ + setNeedsDisplay(); +} + + +void +ViewerImpl::windowRefreshCallback() +{ + setNeedsDisplay(); +} + + +//////////////////////////////////////// + + +bool +ViewerImpl::needsDisplay() +{ + if (mUpdates < 2) { + mUpdates += 1; + return true; + } + return false; +} + + +void +ViewerImpl::setNeedsDisplay() +{ + mUpdates = 0; +} + + +void +ViewerImpl::toggleRenderModule(size_t n) +{ + mRenderModules[n]->setVisible(!mRenderModules[n]->visible()); +} + + +void +ViewerImpl::toggleInfoText() +{ + mShowInfo = !mShowInfo; +} + +} // namespace openvdb_viewer diff --git a/openvdb/viewer/Viewer.h b/openvdb/viewer/Viewer.h new file mode 100644 index 00000000..4b45be4e --- /dev/null +++ b/openvdb/viewer/Viewer.h @@ -0,0 +1,62 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_VIEWER_VIEWER_HAS_BEEN_INCLUDED +#define OPENVDB_VIEWER_VIEWER_HAS_BEEN_INCLUDED + +#include +#include + + +namespace openvdb_viewer { + +class Viewer; + +enum { DEFAULT_WIDTH = 900, DEFAULT_HEIGHT = 800 }; + + +/// @brief Initialize and return a viewer. +/// @param progName the name of the calling program (for use in info displays) +/// @param background if true, run the viewer in a separate thread +/// @note Currently, the viewer window is a singleton (but that might change +/// in the future), so although this function returns a new Viewer instance +/// on each call, all instances are associated with the same window. +Viewer init(const std::string& progName, bool background); + +/// @brief Destroy all viewer windows and release resources. +/// @details This should be called from the main thread before your program exits. +void exit(); + + +/// Manager for a window that displays OpenVDB grids +class Viewer +{ +public: + /// Set the size of and open the window associated with this viewer. + void open(int width = DEFAULT_WIDTH, int height = DEFAULT_HEIGHT); + + /// Display the given grids. + void view(const openvdb::GridCPtrVec&); + + /// @brief Process any pending user input (keyboard, mouse, etc.) + /// in the window associated with this viewer. + void handleEvents(); + + /// @brief Close the window associated with this viewer. + /// @warning The window associated with this viewer might be shared with other viewers. + void close(); + + /// Resize the window associated with this viewer. + void resize(int width, int height); + + /// Return a string with version number information. + std::string getVersionString() const; + +private: + friend Viewer init(const std::string&, bool); + Viewer(); +}; + +} // namespace openvdb_viewer + +#endif // OPENVDB_VIEWER_VIEWER_HAS_BEEN_INCLUDED diff --git a/scenes/bacilli_on_agar.py b/scenes/bacilli_on_agar.py new file mode 100644 index 00000000..35d80ea4 --- /dev/null +++ b/scenes/bacilli_on_agar.py @@ -0,0 +1,143 @@ +# +# Simple flip with level set and basic resampling +# +from manta import * + +# solver params +dim = 3 +res = 64 +#res = 128 +gs = vec3(res,res,res) + +if (dim==2): + gs.z=1 + +s = Solver(name='main', gridSize = gs, dim=dim) +s.timestep = 0.8 +minParticles = pow(2,dim) + +# save particles for separate surface generation pass? +saveParts = False + +# size of particles +radiusFactor = 1.0 + +# prepare grids and particles +flags = s.create(FlagGrid) +phi = s.create(LevelsetGrid) + +vel = s.create(MACGrid) +velOld = s.create(MACGrid) +pressure = s.create(RealGrid) +tmpVec3 = s.create(VecGrid) +tstGrid = s.create(RealGrid) + +pp = s.create(BasicParticleSystem) +pVel = pp.create(PdataVec3) +# test real value, not necessary for simulation +pTest = pp.create(PdataReal) +mesh = s.create(Mesh) + +# acceleration data for particle nbs +pindex = s.create(ParticleIndexSystem) +gpi = s.create(IntGrid) + +# scene setup, 0=breaking dam, 1=drop into pool +setup = 1 +bWidth=1 +flags.initDomain(boundaryWidth=bWidth) +fluidVel = 0 +fluidSetVel = 0 + +if setup==0: + # breaking dam + fluidbox = Box( parent=s, p0=gs*vec3(0,0,0), p1=gs*vec3(0.4,0.6,1)) # breaking dam + #fluidbox = Box( parent=s, p0=gs*vec3(0.4,0.72,0.4), p1=gs*vec3(0.6,0.92,0.6)) # centered falling block + phi = fluidbox.computeLevelset() +elif setup==1: + # falling drop + fluidBasin = Box( parent=s, p0=gs*vec3(0,0,0), p1=gs*vec3(1.0,0.1,1.0)) # basin + dropCenter = vec3(0.5,0.3,0.5) + dropRadius = 0.1 + fluidDrop = Sphere( parent=s , center=gs*dropCenter, radius=res*dropRadius) + fluidVel = Sphere( parent=s , center=gs*dropCenter, radius=res*(dropRadius+0.05) ) + fluidSetVel= vec3(0,-1,0) + phi = fluidBasin.computeLevelset() + phi.join( fluidDrop.computeLevelset() ) + +flags.updateFromLevelset(phi) +#setOpenBound(flags,bWidth,'xX',FlagOutflow|FlagEmpty) +sampleLevelsetWithParticles( phi=phi, flags=flags, parts=pp, discretization=2, randomness=0.05 ) + +if fluidVel!=0: + # set initial velocity + fluidVel.applyToGrid( grid=vel , value=fluidSetVel ) + mapGridToPartsVec3(source=vel, parts=pp, target=pVel ) + +# testing the real channel while resampling - original particles +# will have a value of 0.1, new particle will get a value from the tstGrid +testInitGridWithPos(tstGrid) +pTest.setConst( 0.1 ) + +# save reference any grid, to automatically determine grid size +if saveParts: + pressure.save( 'ref_flipParts_0000.uni' ); + +if 1 and (GUI): + gui = Gui() + gui.show() + #gui.pause() + + +#main loop +for t in range(2500): + mantaMsg('\nFrame %i, simulation time %f' % (s.frame, s.timeTotal)) + + # FLIP + pp.advectInGrid(flags=flags, vel=vel, integrationMode=IntRK4, deleteInObstacle=False ) + + # make sure we have velocities throught liquid region + mapPartsToMAC(vel=vel, flags=flags, velOld=velOld, parts=pp, partVel=pVel, weight=tmpVec3 ) + extrapolateMACFromWeight( vel=vel , distance=2, weight=tmpVec3 ) # note, tmpVec3 could be free'd now... + markFluidCells( parts=pp, flags=flags ) + + # create approximate surface level set, resample particles + gridParticleIndex( parts=pp , flags=flags, indexSys=pindex, index=gpi ) + unionParticleLevelset( pp, pindex, flags, gpi, phi , radiusFactor ) + resetOutflow(flags=flags,parts=pp,index=gpi,indexSys=pindex) + # extend levelset somewhat, needed by particle resampling in adjustNumber + extrapolateLsSimple(phi=phi, distance=4, inside=True); + + # forces & pressure solve + addGravity(flags=flags, vel=vel, gravity=(0,-0.001,0)) + setWallBcs(flags=flags, vel=vel) + solvePressure(flags=flags, vel=vel, pressure=pressure, phi=phi) + setWallBcs(flags=flags, vel=vel) + + # set source grids for resampling, used in adjustNumber! + pVel.setSource( vel, isMAC=True ) + pTest.setSource( tstGrid ); + adjustNumber( parts=pp, vel=vel, flags=flags, minParticles=1*minParticles, maxParticles=2*minParticles, phi=phi, radiusFactor=radiusFactor ) + + # make sure we have proper velocities + extrapolateMACSimple( flags=flags, vel=vel ) + + flipVelocityUpdate(vel=vel, velOld=velOld, flags=flags, parts=pp, partVel=pVel, flipRatio=0.97 ) + + if (dim==3): + phi.createMesh(mesh) + + #s.printMemInfo() + s.step() + + # file_name = './frames/particles-frame-%d.nptxt' % s.frame + # pp.save(file_name) + + # generate data for flip03_gen.py surface generation scene + # if saveParts: + # pp.save( 'flipParts_%04d.uni' % t ); + + # if 0 and (GUI): + # gui.screenshot( 'flip02_%04d.png' % t ); + + diff --git a/scenes/flip01_simple.py b/scenes/flip01_simple.py index 3a5091cd..67311677 100644 --- a/scenes/flip01_simple.py +++ b/scenes/flip01_simple.py @@ -12,6 +12,7 @@ if (dim==2): gs.z=1 particleNumber = 3 # use more particles in 2d + s = Solver(name='main', gridSize = gs, dim=dim) s.timestep = 0.5 diff --git a/scenes/flip02_surface.py b/scenes/flip02_surface.py index 1e3ab11d..cc0ef7c5 100644 --- a/scenes/flip02_surface.py +++ b/scenes/flip02_surface.py @@ -8,6 +8,7 @@ res = 64 #res = 128 gs = vec3(res,res,res) + if (dim==2): gs.z=1 s = Solver(name='main', gridSize = gs, dim=dim) diff --git a/scenes/freesurface.py b/scenes/freesurface.py index 473938fb..40179144 100644 --- a/scenes/freesurface.py +++ b/scenes/freesurface.py @@ -16,7 +16,7 @@ # scene file params ghostFluid = True -doOpen = False +doOpen = True accuracy = 5e-4 # using fast marching is more accurate, but currently causes asymmetries useMarching = False diff --git a/scenes/view_particles_in_notebook.ipynb b/scenes/view_particles_in_notebook.ipynb index 45413390..1d43789c 100644 --- a/scenes/view_particles_in_notebook.ipynb +++ b/scenes/view_particles_in_notebook.ipynb @@ -48,7 +48,7 @@ "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, - "model_id": "7abf44e35b4c430aba7bf0c6d17fba96" + "model_id": "6df76752d32a4d45bcf5b02a7c842838" } }, "metadata": {} @@ -57,8 +57,8 @@ "source": [ "# View 50 frames at a time\n", "\n", - "start = 0\n", - "stop = 49\n", + "start = 49\n", + "stop = 99\n", "\n", "if start > 0:\n", " offset = start\n", From 0cb6c17f59f436bd652a995e66a5030f6bd9792a Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Fri, 31 Jul 2020 15:43:30 -0400 Subject: [PATCH 08/15] =?UTF-8?q?The=20Bacilli=20on=20Agar=20scene=20is=20?= =?UTF-8?q?specified.=20=20Started=20work=20on=20implementing=20the=20FLIP?= =?UTF-8?q?=20and=20shape=20matching=20coupled=20method=20described=20in?= =?UTF-8?q?=20Gao,=20Y.,=20Li,=20S.,=20Qin,=20H.=20et=20al.=20An=20efficie?= =?UTF-8?q?nt=20FLIP=20and=20shape=20matching=20coupled=20method=20for=20f?= =?UTF-8?q?luid=E2=80=93solid=20and=20two-phase=20fluid=20simulations.=20V?= =?UTF-8?q?is=20Comput=2035,=201741=E2=80=931753=20(2019).=20https://doi.o?= =?UTF-8?q?rg/10.1007/s00371-018-1569-8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scenes/bacilli_on_agar.2.py | 147 ++++++++++++++++++++++++++++++++++++ scenes/bacilli_on_agar.py | 143 ----------------------------------- scenes/flip01_simple.py | 4 +- scenes/flip03_gen.py | 2 +- scenes/flip06_obstacle.py | 18 ++--- scenes/meshload.py | 3 +- scenes/movingObstacle.py | 4 +- source/grid.h | 1 + source/plugin/pbd.cpp | 93 +++++++++++++++++++++++ 9 files changed, 257 insertions(+), 158 deletions(-) create mode 100644 scenes/bacilli_on_agar.2.py delete mode 100644 scenes/bacilli_on_agar.py create mode 100644 source/plugin/pbd.cpp diff --git a/scenes/bacilli_on_agar.2.py b/scenes/bacilli_on_agar.2.py new file mode 100644 index 00000000..4b89e5be --- /dev/null +++ b/scenes/bacilli_on_agar.2.py @@ -0,0 +1,147 @@ +# +# Bacilli on Agar +# + +from manta import * + +# solver params +dim = 3 +res = 64 +# res = 128 +gs = vec3(res,res,res) + +if (dim==2): + gs.z=1 + +solver = Solver(name='main', gridSize = gs, dim=dim) +solver.timestep = 0.8 +minParticles = pow(2,dim) + +# save particles for separate surface generation pass? +saveParts = False + +# size of particles +radiusFactor = 1.0 + +# prepare grids and particles +flags = solver.create(FlagGrid) +phi = solver.create(LevelsetGrid) +vel = solver.create(MACGrid) +velOld = solver.create(MACGrid) +pressure = solver.create(RealGrid) +tmpVec3 = solver.create(VecGrid) +force = solver.create(VecGrid) +pp = solver.create(BasicParticleSystem) +pVel = pp.create(PdataVec3) +mesh = solver.create(Mesh) + +# acceleration data for particle nbs +pindex = solver.create(ParticleIndexSystem) +gpi = solver.create(IntGrid) +bWidth=1 +flags.initDomain(boundaryWidth=bWidth) +fluidVel = 0 +fluidSetVel = 0 + +# The scale is 1 = 10 micrometer (1 x 10E-7 meter) +# Agar +agar = Box(parent=solver, p0=gs*vec3(0,0,0), p1=gs*vec3(1.0,0.1,1.0)) +phi = agar.computeLevelset() + +# Bacillus +center = vec3(0.5,0.1125,0.5) +radius = 0.0125 +zpoint = vec3(0, 0, 0.1) +# The mantaflow cylinder has a capsule-like geometry +bacillus = Cylinder(parent=solver, center=gs*center, radius=res*radius, z=gs*zpoint) +phiBacillus = bacillus.computeLevelset() +setObstacleFlags(flags=flags, phiObs=phiBacillus) + +# Join the agar and bacillus levelsets +phi.join(phiBacillus) + +flags.updateFromLevelset(phi) +sampleLevelsetWithParticles(phi=phi, flags=flags, parts=pp, discretization=2, randomness=0.05) + +# save reference any grid, to automatically determine grid size +if saveParts: + pressure.save('ref_flipParts_0000.uni'); + +if 1 and (GUI): + gui = Gui() + gui.show() + gui.pause() + + +#main loop +for t in range(2500): + # mantaMsg('\nFrame %i, simulation time %f' % (solver.frame, solver.timeTotal)) + + # Algorithm 1 in Gao, 2018 + # 1: Advect velocities of particles + pp.advectInGrid(flags=flags, vel=vel, integrationMode=IntRK4, deleteInObstacle=False ) + + # 2: Enforce external forces (gravity) + addGravity(flags=flags, vel=vel, gravity=(0,-0.001,0)) + + if t == 3: + force.setConst(vec3(0.0, 0.0, 0.5)) + addForceField(flags=flags, vel=vel, force=force) + + # 3: Verify fluid and solid particle flags via Fsolid, Ffluid + # 4: Map all the particles to grid ug ← up, xg ← xp + # 5: Compute level set Φ and velocity on grid ug + # 6: Project up ← ug,xp ← xg0 + ugΔt + # 7: if particle ∈ Fsolid then + # 8: Project shape matching constraint + # 9: Compute target position g + # 10: Update x∗+=α 􏰉g − x 􏰊 + # 11: Update u p∗ ← ( u p , ( x p∗ − x p0 ) / Δ t ) + # 12: else continue + # 13: Correct boundary conditions + # 14: Conduct the mechanism of penetration prevention + # 15: Update the velocities and positions of all particles + # 16: Update particles’ flags + + # make sure we have velocities throughout liquid region + mapPartsToMAC(vel=vel, flags=flags, velOld=velOld, parts=pp, partVel=pVel, weight=tmpVec3 ) + extrapolateMACFromWeight(vel=vel , distance=2, weight=tmpVec3) # note, tmpVec3 could be free'd now... + markFluidCells(parts=pp, flags=flags) + + # create approximate surface level set, resample particles + gridParticleIndex(parts=pp , flags=flags, indexSys=pindex, index=gpi) + unionParticleLevelset(pp, pindex, flags, gpi, phi , radiusFactor) + resetOutflow(flags=flags,parts=pp,index=gpi,indexSys=pindex) + # extend levelset somewhat, needed by particle resampling in adjustNumber + extrapolateLsSimple(phi=phi, distance=4, inside=True); + + # forces & pressure solve + + + + + setWallBcs(flags=flags, vel=vel) + solvePressure(flags=flags, vel=vel, pressure=pressure, phi=phi) + + # set source grids for resampling, used in adjustNumber! + pVel.setSource( vel, isMAC=True ) + adjustNumber( parts=pp, vel=vel, flags=flags, minParticles=1*minParticles, maxParticles=2*minParticles, phi=phi, radiusFactor=radiusFactor ) + + # make sure we have proper velocities + extrapolateMACSimple( flags=flags, vel=vel ) + + flipVelocityUpdate(vel=vel, velOld=velOld, flags=flags, parts=pp, partVel=pVel, flipRatio=0.97 ) + + if (dim==3): + phi.createMesh(mesh) + mesh.fromShape(bacillus, append=True) + + #solver.printMemInfo() + solver.step() + + # generate data for flip03_gen.py surface generation scene + # if saveParts: + # pp.save( 'flipParts_%04d.uni' % t ); + + + diff --git a/scenes/bacilli_on_agar.py b/scenes/bacilli_on_agar.py deleted file mode 100644 index 35d80ea4..00000000 --- a/scenes/bacilli_on_agar.py +++ /dev/null @@ -1,143 +0,0 @@ -# -# Simple flip with level set and basic resampling -# -from manta import * - -# solver params -dim = 3 -res = 64 -#res = 128 -gs = vec3(res,res,res) - -if (dim==2): - gs.z=1 - -s = Solver(name='main', gridSize = gs, dim=dim) -s.timestep = 0.8 -minParticles = pow(2,dim) - -# save particles for separate surface generation pass? -saveParts = False - -# size of particles -radiusFactor = 1.0 - -# prepare grids and particles -flags = s.create(FlagGrid) -phi = s.create(LevelsetGrid) - -vel = s.create(MACGrid) -velOld = s.create(MACGrid) -pressure = s.create(RealGrid) -tmpVec3 = s.create(VecGrid) -tstGrid = s.create(RealGrid) - -pp = s.create(BasicParticleSystem) -pVel = pp.create(PdataVec3) -# test real value, not necessary for simulation -pTest = pp.create(PdataReal) -mesh = s.create(Mesh) - -# acceleration data for particle nbs -pindex = s.create(ParticleIndexSystem) -gpi = s.create(IntGrid) - -# scene setup, 0=breaking dam, 1=drop into pool -setup = 1 -bWidth=1 -flags.initDomain(boundaryWidth=bWidth) -fluidVel = 0 -fluidSetVel = 0 - -if setup==0: - # breaking dam - fluidbox = Box( parent=s, p0=gs*vec3(0,0,0), p1=gs*vec3(0.4,0.6,1)) # breaking dam - #fluidbox = Box( parent=s, p0=gs*vec3(0.4,0.72,0.4), p1=gs*vec3(0.6,0.92,0.6)) # centered falling block - phi = fluidbox.computeLevelset() -elif setup==1: - # falling drop - fluidBasin = Box( parent=s, p0=gs*vec3(0,0,0), p1=gs*vec3(1.0,0.1,1.0)) # basin - dropCenter = vec3(0.5,0.3,0.5) - dropRadius = 0.1 - fluidDrop = Sphere( parent=s , center=gs*dropCenter, radius=res*dropRadius) - fluidVel = Sphere( parent=s , center=gs*dropCenter, radius=res*(dropRadius+0.05) ) - fluidSetVel= vec3(0,-1,0) - phi = fluidBasin.computeLevelset() - phi.join( fluidDrop.computeLevelset() ) - -flags.updateFromLevelset(phi) -#setOpenBound(flags,bWidth,'xX',FlagOutflow|FlagEmpty) -sampleLevelsetWithParticles( phi=phi, flags=flags, parts=pp, discretization=2, randomness=0.05 ) - -if fluidVel!=0: - # set initial velocity - fluidVel.applyToGrid( grid=vel , value=fluidSetVel ) - mapGridToPartsVec3(source=vel, parts=pp, target=pVel ) - -# testing the real channel while resampling - original particles -# will have a value of 0.1, new particle will get a value from the tstGrid -testInitGridWithPos(tstGrid) -pTest.setConst( 0.1 ) - -# save reference any grid, to automatically determine grid size -if saveParts: - pressure.save( 'ref_flipParts_0000.uni' ); - -if 1 and (GUI): - gui = Gui() - gui.show() - #gui.pause() - - -#main loop -for t in range(2500): - mantaMsg('\nFrame %i, simulation time %f' % (s.frame, s.timeTotal)) - - # FLIP - pp.advectInGrid(flags=flags, vel=vel, integrationMode=IntRK4, deleteInObstacle=False ) - - # make sure we have velocities throught liquid region - mapPartsToMAC(vel=vel, flags=flags, velOld=velOld, parts=pp, partVel=pVel, weight=tmpVec3 ) - extrapolateMACFromWeight( vel=vel , distance=2, weight=tmpVec3 ) # note, tmpVec3 could be free'd now... - markFluidCells( parts=pp, flags=flags ) - - # create approximate surface level set, resample particles - gridParticleIndex( parts=pp , flags=flags, indexSys=pindex, index=gpi ) - unionParticleLevelset( pp, pindex, flags, gpi, phi , radiusFactor ) - resetOutflow(flags=flags,parts=pp,index=gpi,indexSys=pindex) - # extend levelset somewhat, needed by particle resampling in adjustNumber - extrapolateLsSimple(phi=phi, distance=4, inside=True); - - # forces & pressure solve - addGravity(flags=flags, vel=vel, gravity=(0,-0.001,0)) - setWallBcs(flags=flags, vel=vel) - solvePressure(flags=flags, vel=vel, pressure=pressure, phi=phi) - setWallBcs(flags=flags, vel=vel) - - # set source grids for resampling, used in adjustNumber! - pVel.setSource( vel, isMAC=True ) - pTest.setSource( tstGrid ); - adjustNumber( parts=pp, vel=vel, flags=flags, minParticles=1*minParticles, maxParticles=2*minParticles, phi=phi, radiusFactor=radiusFactor ) - - # make sure we have proper velocities - extrapolateMACSimple( flags=flags, vel=vel ) - - flipVelocityUpdate(vel=vel, velOld=velOld, flags=flags, parts=pp, partVel=pVel, flipRatio=0.97 ) - - if (dim==3): - phi.createMesh(mesh) - - #s.printMemInfo() - s.step() - - # file_name = './frames/particles-frame-%d.nptxt' % s.frame - # pp.save(file_name) - - # generate data for flip03_gen.py surface generation scene - # if saveParts: - # pp.save( 'flipParts_%04d.uni' % t ); - - # if 0 and (GUI): - # gui.screenshot( 'flip02_%04d.png' % t ); - - diff --git a/scenes/flip01_simple.py b/scenes/flip01_simple.py index 67311677..e427b0ef 100644 --- a/scenes/flip01_simple.py +++ b/scenes/flip01_simple.py @@ -29,8 +29,8 @@ # scene setup flags.initDomain(boundaryWidth=0) # enable one of the following -fluidbox = Box( parent=s, p0=gs*vec3(0,0,0), p1=gs*vec3(0.4,0.6,1)) # breaking dam -#fluidbox = Box( parent=s, p0=gs*vec3(0.4,0.72,0.4), p1=gs*vec3(0.6,0.92,0.6)) # centered falling block +# fluidbox = Box( parent=s, p0=gs*vec3(0,0,0), p1=gs*vec3(0.4,0.6,1)) # breaking dam +fluidbox = Box( parent=s, p0=gs*vec3(0.4,0.72,0.4), p1=gs*vec3(0.6,0.92,0.6)) # centered falling block phiInit = fluidbox.computeLevelset() flags.updateFromLevelset(phiInit) # phiInit is not needed from now on! diff --git a/scenes/flip03_gen.py b/scenes/flip03_gen.py index 732d5fd4..551f1f38 100644 --- a/scenes/flip03_gen.py +++ b/scenes/flip03_gen.py @@ -21,7 +21,7 @@ upres = 2.0 # output file name so that blender can directly read it... -meshfile = 'fluidsurface_final_%04d.bobj.gz' +meshfile = 'frames/fluidsurface_final_%04d.bobj.gz' # resolution for level set / output mesh refName = ("ref_" + (partfile % 0) ) diff --git a/scenes/flip06_obstacle.py b/scenes/flip06_obstacle.py index ee642071..b2ad4ed7 100644 --- a/scenes/flip06_obstacle.py +++ b/scenes/flip06_obstacle.py @@ -146,14 +146,14 @@ s.step() - if (lastFrame!=s.frame): - # generate data for flip03_gen.py surface generation scene - if saveParts: - pp.save( 'flipParts_%04d.uni' % s.frame ); - if 0 and (GUI): - gui.screenshot( 'flip06_%04d.png' % s.frame ); - - #s.printMemInfo() - lastFrame = s.frame; + # if (lastFrame!=s.frame): + # # generate data for flip03_gen.py surface generation scene + # if saveParts: + # pp.save( 'flipParts_%04d.uni' % s.frame ); + # if 0 and (GUI): + # gui.screenshot( 'flip06_%04d.png' % s.frame ); + + # #s.printMemInfo() + # lastFrame = s.frame; diff --git a/scenes/meshload.py b/scenes/meshload.py index c07a0883..89e2b3fa 100644 --- a/scenes/meshload.py +++ b/scenes/meshload.py @@ -5,7 +5,8 @@ import os # mesh to load -meshfile = '../resources/simpletorus.obj' +# meshfile = '../resources/simpletorus.obj' +meshfile = './resources/simpletorus.obj' mantaMsg("Loading %s. Note: relative path by default, assumes this scene is called from the 'scenes' directory.") # resolution for level set / output mesh diff --git a/scenes/movingObstacle.py b/scenes/movingObstacle.py index 70699e6c..c40eec38 100644 --- a/scenes/movingObstacle.py +++ b/scenes/movingObstacle.py @@ -3,7 +3,7 @@ # from manta import * -dim = 2 +dim = 3 res = 50 gs = vec3(res,res, 1 if dim==2 else res ) s = Solver(name='main', gridSize = gs, dim=dim) @@ -37,7 +37,7 @@ #gui.pause() #main loop -for t in range(400): +for t in range(1000): mantaMsg('\nFrame %i' % (s.frame)) advectSemiLagrange(flags=flags, vel=vel, grid=density, order=2) diff --git a/source/grid.h b/source/grid.h index 16104922..1fc99364 100644 --- a/source/grid.h +++ b/source/grid.h @@ -298,6 +298,7 @@ PYTHON() class FlagGrid : public Grid { TypeOutflow = 16, TypeOpen = 32, TypeStick = 64, + TypeSolid = 128, // internal use only, for fast marching TypeReserved = 256, // 2^10 - 2^14 reserved for moving obstacles diff --git a/source/plugin/pbd.cpp b/source/plugin/pbd.cpp new file mode 100644 index 00000000..85f57e95 --- /dev/null +++ b/source/plugin/pbd.cpp @@ -0,0 +1,93 @@ +/****************************************************************************** + * + * Plugins for Position-Based Dynamics (PDB) + * Copyright 2020 Cesar Rodriguez, James Mossell + * + * The implementation is based on the publication An efficient FLIP and shape + * matching coupled method for fluid-solid and two-phase fluid simulations by + * Yang Gao, Shuai Li, Hong Qin, Yinghao Xu, and Aimin Hao + * https://doi.org/10.1007/s00371-018-1569-8 + * + * This source code is free and distributed under the terms of the + * Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "grid.h" +#include "kernel.h" +#include + +using namespace std; + +namespace Manta { + + //! add constant force between fl/fl and fl/em cells + KERNEL(bnd=1) void KnApplyForce(const FlagGrid& flags, MACGrid& vel, Vec3 force, const Grid* exclude, bool additive) { + bool curFluid = flags.isFluid(i,j,k); + bool curEmpty = flags.isEmpty(i,j,k); + if (!curFluid && !curEmpty) return; + if (exclude && ((*exclude)(i,j,k) < 0.)) return; + + if (flags.isFluid(i-1,j,k) || (curFluid && flags.isEmpty(i-1,j,k))) + vel(i,j,k).x = (additive) ? vel(i,j,k).x+force.x : force.x; + if (flags.isFluid(i,j-1,k) || (curFluid && flags.isEmpty(i,j-1,k))) + vel(i,j,k).y = (additive) ? vel(i,j,k).y+force.y : force.y; + if (vel.is3D() && (flags.isFluid(i,j,k-1) || (curFluid && flags.isEmpty(i,j,k-1)))) + vel(i,j,k).z = (additive) ? vel(i,j,k).z+force.z : force.z; + } + + //! add gravity forces to all fluid cells, optionally adapts to different grid sizes automatically + PYTHON() void addGravity(const FlagGrid& flags, MACGrid& vel, Vec3 gravity, const Grid* exclude=NULL, bool scale=true) { + float gridScale = (scale) ? flags.getDx() : 1; + Vec3 f = gravity * flags.getParent()->getDt() / gridScale; + KnApplyForce(flags, vel, f, exclude, true); + } + + + //! Semi-Lagrange interpolation kernel + KERNEL(bnd=1) template + void SemiLagrange (const FlagGrid& flags, const MACGrid& vel, Grid& dst, const Grid& src, Real dt, bool isLevelset, int orderSpace, int orderTrace) + { + if (orderTrace == 1) { + // traceback position + Vec3 pos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getCentered(i,j,k) * dt; + dst(i,j,k) = src.getInterpolatedHi(pos, orderSpace); + } else if (orderTrace == 2) { + // backtracing using explicit midpoint + Vec3 p0 = Vec3(i+0.5f,j+0.5f,k+0.5f); + Vec3 p1 = p0 - vel.getCentered(i,j,k)*dt*0.5; + Vec3 p2 = p0 - vel.getInterpolated(p1)*dt; + dst(i,j,k) = src.getInterpolatedHi(p2, orderSpace); + } else { + assertMsg(false, "Unknown backtracing order "<getType() & GridBase::TypeReal) { + fnAdvectSemiLagrange< Grid >(flags->getParent(), *flags, *vel, *((Grid*) grid), order, strength, orderSpace, clampMode, orderTrace); + } + else if (grid->getType() & GridBase::TypeMAC) { + fnAdvectSemiLagrange< MACGrid >(flags->getParent(), *flags, *vel, *((MACGrid*) grid), order, strength, orderSpace, clampMode, orderTrace); + } + else if (grid->getType() & GridBase::TypeVec3) { + fnAdvectSemiLagrange< Grid >(flags->getParent(), *flags, *vel, *((Grid*) grid), order, strength, orderSpace, clampMode, orderTrace); + } + else + errMsg("AdvectSemiLagrange: Grid Type is not supported (only Real, Vec3, MAC, Levelset)"); + } + +} // end namespace DDF + From a3f9526c8d13007a15adb74520841cbb90709cdd Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Fri, 31 Jul 2020 21:00:21 -0400 Subject: [PATCH 09/15] Implementing the FLIP and shape matching coupled method for fluid-solid simulations. --- scenes/bacilli_on_agar.2.py | 52 +++++++++++++++++-------------------- source/plugin/pbd.cpp | 47 +++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 33 deletions(-) diff --git a/scenes/bacilli_on_agar.2.py b/scenes/bacilli_on_agar.2.py index 4b89e5be..b5fb5a67 100644 --- a/scenes/bacilli_on_agar.2.py +++ b/scenes/bacilli_on_agar.2.py @@ -80,29 +80,7 @@ # Algorithm 1 in Gao, 2018 # 1: Advect velocities of particles pp.advectInGrid(flags=flags, vel=vel, integrationMode=IntRK4, deleteInObstacle=False ) - - # 2: Enforce external forces (gravity) - addGravity(flags=flags, vel=vel, gravity=(0,-0.001,0)) - - if t == 3: - force.setConst(vec3(0.0, 0.0, 0.5)) - addForceField(flags=flags, vel=vel, force=force) - - # 3: Verify fluid and solid particle flags via Fsolid, Ffluid - # 4: Map all the particles to grid ug ← up, xg ← xp - # 5: Compute level set Φ and velocity on grid ug - # 6: Project up ← ug,xp ← xg0 + ugΔt - # 7: if particle ∈ Fsolid then - # 8: Project shape matching constraint - # 9: Compute target position g - # 10: Update x∗+=α 􏰉g − x 􏰊 - # 11: Update u p∗ ← ( u p , ( x p∗ − x p0 ) / Δ t ) - # 12: else continue - # 13: Correct boundary conditions - # 14: Conduct the mechanism of penetration prevention - # 15: Update the velocities and positions of all particles - # 16: Update particles’ flags - + # make sure we have velocities throughout liquid region mapPartsToMAC(vel=vel, flags=flags, velOld=velOld, parts=pp, partVel=pVel, weight=tmpVec3 ) extrapolateMACFromWeight(vel=vel , distance=2, weight=tmpVec3) # note, tmpVec3 could be free'd now... @@ -115,11 +93,6 @@ # extend levelset somewhat, needed by particle resampling in adjustNumber extrapolateLsSimple(phi=phi, distance=4, inside=True); - # forces & pressure solve - - - - setWallBcs(flags=flags, vel=vel) solvePressure(flags=flags, vel=vel, pressure=pressure, phi=phi) @@ -129,9 +102,32 @@ # make sure we have proper velocities extrapolateMACSimple( flags=flags, vel=vel ) + + # 2: Enforce external forces (gravity) + addGravity(flags=flags, vel=vel, gravity=(0,-0.001,0)) + if t == 3: + force.setConst(vec3(0.0, 0.0, 0.5)) + addForceField(flags=flags, vel=vel, force=force) + + # 3: Verify fluid and solid particle flags via Fsolid, Ffluid + # 4: Map all the particles to grid ug ← up, xg ← xp + # 5: Compute level set Φ and velocity on grid ug + # 6: Project up ← ug,xp ← xg0 + ugΔt + # 7: if particle ∈ Fsolid then + # 8: Project shape matching constraint + # 9: Compute target position g + # 10: Update x∗+=α 􏰉g − x 􏰊 + # 11: Update u p∗ ← ( u p , ( x p∗ − x p0 ) / Δ t ) + # 12: else continue + # 13: Correct boundary conditions + # 14: Conduct the mechanism of penetration prevention + + # 15: Update the velocities and positions of all particles flipVelocityUpdate(vel=vel, velOld=velOld, flags=flags, parts=pp, partVel=pVel, flipRatio=0.97 ) + # 16: Update particles’ flags + if (dim==3): phi.createMesh(mesh) mesh.fromShape(bacillus, append=True) diff --git a/source/plugin/pbd.cpp b/source/plugin/pbd.cpp index 85f57e95..745f3bf4 100644 --- a/source/plugin/pbd.cpp +++ b/source/plugin/pbd.cpp @@ -1,11 +1,13 @@ /****************************************************************************** * - * Plugins for Position-Based Dynamics (PDB) + * Plugins for Position-Based Dynamics (PBD) * Copyright 2020 Cesar Rodriguez, James Mossell * - * The implementation is based on the publication An efficient FLIP and shape - * matching coupled method for fluid-solid and two-phase fluid simulations by - * Yang Gao, Shuai Li, Hong Qin, Yinghao Xu, and Aimin Hao + * The implementation is based on the publication: + * + * Gao, Y., Li, S., Qin, H. et al. An efficient FLIP and shape matching + * coupled method for fluid–solid and two-phase fluid simulations. + * Vis Comput 35, 1741–1753 (2019). * https://doi.org/10.1007/s00371-018-1569-8 * * This source code is free and distributed under the terms of the @@ -22,7 +24,42 @@ using namespace std; -namespace Manta { +namespace Manta { + +KERNEL (bnd=boundaryWidth) +void KnUpdateFlagsSolid(FlagGrid& flags, const MACGrid* fractions, const Grid& phiObs, const Grid* phiOut, const Grid* phiIn, int boundaryWidth) { + + bool isObs = false; + if(fractions) { + Real f = 0.; + f += fractions->get(i ,j,k).x; + f += fractions->get(i+1,j,k).x; + f += fractions->get(i,j ,k).y; + f += fractions->get(i,j+1,k).y; + if (flags.is3D()) { + f += fractions->get(i,j,k ).z; + f += fractions->get(i,j,k+1).z; } + if(f==0.) isObs = true; + } else { + if(phiObs(i,j,k) < 0.) isObs = true; + } + + bool isOutflow = false; + bool isInflow = false; + if (phiOut && (*phiOut)(i,j,k) < 0.) isOutflow = true; + if (phiIn && (*phiIn)(i,j,k) < 0.) isInflow = true; + + if (isObs) flags(i,j,k) = FlagGrid::TypeObstacle; + else if (isInflow) flags(i,j,k) = (FlagGrid::TypeFluid | FlagGrid::TypeInflow); + else if (isOutflow) flags(i,j,k) = (FlagGrid::TypeEmpty | FlagGrid::TypeOutflow); + else flags(i,j,k) = FlagGrid::TypeEmpty; +} + +//! update obstacle and outflow flags from levelsets +//! optionally uses fill fractions for obstacle +PYTHON() void setSolidFlags(FlagGrid& flags, const Grid& phiObs, const MACGrid* fractions=NULL, const Grid* phiOut=NULL, const Grid* phiIn=NULL, int boundaryWidth=1) { + KnUpdateFlagsSolid(flags, fractions, phiObs, phiOut, phiIn, boundaryWidth); +} //! add constant force between fl/fl and fl/em cells KERNEL(bnd=1) void KnApplyForce(const FlagGrid& flags, MACGrid& vel, Vec3 force, const Grid* exclude, bool additive) { From 05d0c7c132767d5b3cac0f16dc5a84fb31684333 Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Mon, 28 Sep 2020 16:23:40 -0400 Subject: [PATCH 10/15] Ignoring the Results folder in the Notebook folder. --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 727f6f1d..9da37fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,7 @@ waveletNoiseTileDouble.bin build doc tensorflow/mantaGen/datasets -scenes/results -scenes/frames +notebooks/results # macos .DS_Store From f01845a02c675659ee9cb645c3453269496c084f Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Mon, 28 Sep 2020 16:28:55 -0400 Subject: [PATCH 11/15] Added a solid flag. Added an algorithm for setting solid flags from a levelset based on the geometry of a cylinder. Working on managing the solid flags with a position-based dynamics algorithm. --- CMakeLists.txt | 1 + notebooks/flags.ipynb | 69 +++++ .../view_particles_in_notebook.ipynb | 0 scenes/bacilli_on_agar.2.py | 37 +-- source/grid.cpp | 2 +- source/grid.h | 16 +- source/particle.h | 1 + source/plugin/pbd.cpp | 235 +++++++++++------- 8 files changed, 248 insertions(+), 113 deletions(-) create mode 100644 notebooks/flags.ipynb rename {scenes => notebooks}/view_particles_in_notebook.ipynb (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8324169b..0a11aecd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,6 +214,7 @@ set(PP_SOURCES source/plugin/vortexplugins.cpp source/plugin/waveletturbulence.cpp source/plugin/waves.cpp + source/plugin/pbd.cpp source/python/defines.py source/test.cpp ) diff --git a/notebooks/flags.ipynb b/notebooks/flags.ipynb new file mode 100644 index 00000000..2dfb9fff --- /dev/null +++ b/notebooks/flags.ipynb @@ -0,0 +1,69 @@ +{ + "metadata": { + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6-final" + }, + "orig_nbformat": 2, + "kernelspec": { + "name": "python_defaultSpec_1600384803676", + "display_name": "Python 3.7.6 64-bit ('anaconda3': conda)" + } + }, + "nbformat": 4, + "nbformat_minor": 2, + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "file = np.load('./results/flags.npz')" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "arr_0\niteration\n" + } + ], + "source": [ + "for data in file:\n", + " print(data)\n", + " print(\"iteration\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ] +} \ No newline at end of file diff --git a/scenes/view_particles_in_notebook.ipynb b/notebooks/view_particles_in_notebook.ipynb similarity index 100% rename from scenes/view_particles_in_notebook.ipynb rename to notebooks/view_particles_in_notebook.ipynb diff --git a/scenes/bacilli_on_agar.2.py b/scenes/bacilli_on_agar.2.py index b5fb5a67..7431b929 100644 --- a/scenes/bacilli_on_agar.2.py +++ b/scenes/bacilli_on_agar.2.py @@ -49,13 +49,12 @@ phi = agar.computeLevelset() # Bacillus -center = vec3(0.5,0.1125,0.5) -radius = 0.0125 +center = vec3(0.5,0.2,0.5) +radius = 0.05 zpoint = vec3(0, 0, 0.1) -# The mantaflow cylinder has a capsule-like geometry -bacillus = Cylinder(parent=solver, center=gs*center, radius=res*radius, z=gs*zpoint) +bacillus = Cylinder(parent=solver, center=gs*center, radius=res*radius, z=gs*zpoint) # The mantaflow cylinder has a capsule-like geometry phiBacillus = bacillus.computeLevelset() -setObstacleFlags(flags=flags, phiObs=phiBacillus) +setSolidFlags(flags=flags, phiSolid=phiBacillus) # Join the agar and bacillus levelsets phi.join(phiBacillus) @@ -63,10 +62,6 @@ flags.updateFromLevelset(phi) sampleLevelsetWithParticles(phi=phi, flags=flags, parts=pp, discretization=2, randomness=0.05) -# save reference any grid, to automatically determine grid size -if saveParts: - pressure.save('ref_flipParts_0000.uni'); - if 1 and (GUI): gui = Gui() gui.show() @@ -74,8 +69,11 @@ #main loop -for t in range(2500): +for t in range(2500): # mantaMsg('\nFrame %i, simulation time %f' % (solver.frame, solver.timeTotal)) + + # if t==0: + # flags.printGrid() # Algorithm 1 in Gao, 2018 # 1: Advect velocities of particles @@ -106,15 +104,18 @@ # 2: Enforce external forces (gravity) addGravity(flags=flags, vel=vel, gravity=(0,-0.001,0)) - if t == 3: - force.setConst(vec3(0.0, 0.0, 0.5)) - addForceField(flags=flags, vel=vel, force=force) + # if t == 3: + # force.setConst(vec3(0.0, 0.0, 0.5)) + # addForceField(flags=flags, vel=vel, force=force) + # This is likely FLIP # 3: Verify fluid and solid particle flags via Fsolid, Ffluid # 4: Map all the particles to grid ug ← up, xg ← xp # 5: Compute level set Φ and velocity on grid ug # 6: Project up ← ug,xp ← xg0 + ugΔt # 7: if particle ∈ Fsolid then + + # This is PBD # 8: Project shape matching constraint # 9: Compute target position g # 10: Update x∗+=α 􏰉g − x 􏰊 @@ -130,14 +131,14 @@ if (dim==3): phi.createMesh(mesh) - mesh.fromShape(bacillus, append=True) + # mesh.fromShape(bacillus, append=True) #solver.printMemInfo() - solver.step() - # generate data for flip03_gen.py surface generation scene - # if saveParts: - # pp.save( 'flipParts_%04d.uni' % t ); + if t == 20: + flags.printGrid() + + solver.step() diff --git a/source/grid.cpp b/source/grid.cpp index 84844a8a..2f56f732 100644 --- a/source/grid.cpp +++ b/source/grid.cpp @@ -843,7 +843,7 @@ void FlagGrid::initBoundaries(const int &boundaryWidth, const int *types) { void FlagGrid::updateFromLevelset(LevelsetGrid& levelset) { FOR_IDX(*this) { - if (!isObstacle(idx) && !isOutflow(idx)) { + if (!isObstacle(idx) && !isOutflow(idx) && !isSolid(idx)) { const Real phi = levelset[idx]; if (phi <= levelset.invalidTimeValue()) continue; diff --git a/source/grid.h b/source/grid.h index 1fc99364..d223c7e5 100644 --- a/source/grid.h +++ b/source/grid.h @@ -312,10 +312,14 @@ PYTHON() class FlagGrid : public Grid { inline bool isObstacle(int i, int j, int k) const { return get(i,j,k) & TypeObstacle; } inline bool isObstacle(const Vec3i& pos) const { return get(pos) & TypeObstacle; } inline bool isObstacle(const Vec3& pos) const { return getAt(pos) & TypeObstacle; } - inline bool isFluid(IndexInt idx) const { return get(idx) & TypeFluid; } - inline bool isFluid(int i, int j, int k) const { return get(i,j,k) & TypeFluid; } - inline bool isFluid(const Vec3i& pos) const { return get(pos) & TypeFluid; } - inline bool isFluid(const Vec3& pos) const { return getAt(pos) & TypeFluid; } + + // Major Hack!!! + + inline bool isFluid(IndexInt idx) const { return get(idx) & (TypeFluid || TypeSolid); } + inline bool isFluid(int i, int j, int k) const { return get(i,j,k) & (TypeFluid || TypeSolid); } + inline bool isFluid(const Vec3i& pos) const { return get(pos) & (TypeFluid || TypeSolid); } + inline bool isFluid(const Vec3& pos) const { return getAt(pos) & (TypeFluid || TypeSolid); } + inline bool isInflow(IndexInt idx) const { return get(idx) & TypeInflow; } inline bool isInflow(int i, int j, int k) const { return get(i,j,k) & TypeInflow; } inline bool isInflow(const Vec3i& pos) const { return get(pos) & TypeInflow; } @@ -336,6 +340,10 @@ PYTHON() class FlagGrid : public Grid { inline bool isStick(int i, int j, int k) const { return get(i,j,k) & TypeStick; } inline bool isStick(const Vec3i& pos) const { return get(pos) & TypeStick; } inline bool isStick(const Vec3& pos) const { return getAt(pos) & TypeStick; } + inline bool isSolid(IndexInt idx) const { return get(idx) & TypeSolid; } + inline bool isSolid(int i, int j, int k) const { return get(i,j,k) & TypeSolid; } + inline bool isSolid(const Vec3i& pos) const { return get(pos) & TypeSolid; } + inline bool isSolid(const Vec3& pos) const { return getAt(pos) & TypeSolid; } void InitMinXWall(const int &boundaryWidth, Grid& phiWalls); diff --git a/source/particle.h b/source/particle.h index fb88fcda..ba484f5a 100644 --- a/source/particle.h +++ b/source/particle.h @@ -521,6 +521,7 @@ void ParticleSystem::advectInGrid( { // position clamp requires old positions, backup ParticleDataImpl *posOld = NULL; + if(!deleteInObstacle) { posOld = new ParticleDataImpl(this->getParent()); posOld->resize(mData.size()); diff --git a/source/plugin/pbd.cpp b/source/plugin/pbd.cpp index 745f3bf4..06854f49 100644 --- a/source/plugin/pbd.cpp +++ b/source/plugin/pbd.cpp @@ -25,106 +25,161 @@ using namespace std; namespace Manta { + //--------------------- Kernel Plugins --------------------------- -KERNEL (bnd=boundaryWidth) -void KnUpdateFlagsSolid(FlagGrid& flags, const MACGrid* fractions, const Grid& phiObs, const Grid* phiOut, const Grid* phiIn, int boundaryWidth) { - - bool isObs = false; - if(fractions) { - Real f = 0.; - f += fractions->get(i ,j,k).x; - f += fractions->get(i+1,j,k).x; - f += fractions->get(i,j ,k).y; - f += fractions->get(i,j+1,k).y; - if (flags.is3D()) { - f += fractions->get(i,j,k ).z; - f += fractions->get(i,j,k+1).z; } - if(f==0.) isObs = true; - } else { - if(phiObs(i,j,k) < 0.) isObs = true; - } + // KERNEL (bnd=boundaryWidth) + // void knUpdateFlagsSolid(FlagGrid& flags, const Grid& phiSolid, int boundaryWidth) { + // bool isSolid; + + // if(phiSolid(i,j,k) < 0.) isSolid = true; - bool isOutflow = false; - bool isInflow = false; - if (phiOut && (*phiOut)(i,j,k) < 0.) isOutflow = true; - if (phiIn && (*phiIn)(i,j,k) < 0.) isInflow = true; - - if (isObs) flags(i,j,k) = FlagGrid::TypeObstacle; - else if (isInflow) flags(i,j,k) = (FlagGrid::TypeFluid | FlagGrid::TypeInflow); - else if (isOutflow) flags(i,j,k) = (FlagGrid::TypeEmpty | FlagGrid::TypeOutflow); - else flags(i,j,k) = FlagGrid::TypeEmpty; -} - -//! update obstacle and outflow flags from levelsets -//! optionally uses fill fractions for obstacle -PYTHON() void setSolidFlags(FlagGrid& flags, const Grid& phiObs, const MACGrid* fractions=NULL, const Grid* phiOut=NULL, const Grid* phiIn=NULL, int boundaryWidth=1) { - KnUpdateFlagsSolid(flags, fractions, phiObs, phiOut, phiIn, boundaryWidth); -} - - //! add constant force between fl/fl and fl/em cells - KERNEL(bnd=1) void KnApplyForce(const FlagGrid& flags, MACGrid& vel, Vec3 force, const Grid* exclude, bool additive) { - bool curFluid = flags.isFluid(i,j,k); - bool curEmpty = flags.isEmpty(i,j,k); - if (!curFluid && !curEmpty) return; - if (exclude && ((*exclude)(i,j,k) < 0.)) return; - - if (flags.isFluid(i-1,j,k) || (curFluid && flags.isEmpty(i-1,j,k))) - vel(i,j,k).x = (additive) ? vel(i,j,k).x+force.x : force.x; - if (flags.isFluid(i,j-1,k) || (curFluid && flags.isEmpty(i,j-1,k))) - vel(i,j,k).y = (additive) ? vel(i,j,k).y+force.y : force.y; - if (vel.is3D() && (flags.isFluid(i,j,k-1) || (curFluid && flags.isEmpty(i,j,k-1)))) - vel(i,j,k).z = (additive) ? vel(i,j,k).z+force.z : force.z; - } + // if (isSolid) flags(i,j,k) = FlagGrid::TypeSolid; - //! add gravity forces to all fluid cells, optionally adapts to different grid sizes automatically - PYTHON() void addGravity(const FlagGrid& flags, MACGrid& vel, Vec3 gravity, const Grid* exclude=NULL, bool scale=true) { - float gridScale = (scale) ? flags.getDx() : 1; - Vec3 f = gravity * flags.getParent()->getDt() / gridScale; - KnApplyForce(flags, vel, f, exclude, true); - } + // } + KERNEL (bnd=boundaryWidth) + void KnUpdateFlagsSolid(FlagGrid& flags, const MACGrid* fractions, const Grid& phiSolid, const Grid* phiOut, const Grid* phiIn, int boundaryWidth) { - //! Semi-Lagrange interpolation kernel - KERNEL(bnd=1) template - void SemiLagrange (const FlagGrid& flags, const MACGrid& vel, Grid& dst, const Grid& src, Real dt, bool isLevelset, int orderSpace, int orderTrace) - { - if (orderTrace == 1) { - // traceback position - Vec3 pos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getCentered(i,j,k) * dt; - dst(i,j,k) = src.getInterpolatedHi(pos, orderSpace); - } else if (orderTrace == 2) { - // backtracing using explicit midpoint - Vec3 p0 = Vec3(i+0.5f,j+0.5f,k+0.5f); - Vec3 p1 = p0 - vel.getCentered(i,j,k)*dt*0.5; - Vec3 p2 = p0 - vel.getInterpolated(p1)*dt; - dst(i,j,k) = src.getInterpolatedHi(p2, orderSpace); + bool isSolid = false; + + if(fractions) { + Real f = 0.; + f += fractions->get(i ,j,k).x; + f += fractions->get(i+1,j,k).x; + f += fractions->get(i,j ,k).y; + f += fractions->get(i,j+1,k).y; + if (flags.is3D()) { + f += fractions->get(i,j,k ).z; + f += fractions->get(i,j,k+1).z; } + if(f==0.) isSolid = true; } else { - assertMsg(false, "Unknown backtracing order "<getType() & GridBase::TypeReal) { - fnAdvectSemiLagrange< Grid >(flags->getParent(), *flags, *vel, *((Grid*) grid), order, strength, orderSpace, clampMode, orderTrace); - } - else if (grid->getType() & GridBase::TypeMAC) { - fnAdvectSemiLagrange< MACGrid >(flags->getParent(), *flags, *vel, *((MACGrid*) grid), order, strength, orderSpace, clampMode, orderTrace); - } - else if (grid->getType() & GridBase::TypeVec3) { - fnAdvectSemiLagrange< Grid >(flags->getParent(), *flags, *vel, *((Grid*) grid), order, strength, orderSpace, clampMode, orderTrace); - } - else - errMsg("AdvectSemiLagrange: Grid Type is not supported (only Real, Vec3, MAC, Levelset)"); + //! update obstacle and outflow flags from levelsets + //! optionally uses fill fractions for obstacle + PYTHON() void setSolidFlags(FlagGrid& flags, const Grid& phiSolid, const MACGrid* fractions=NULL, const Grid* phiOut=NULL, const Grid* phiIn=NULL, int boundaryWidth=1){ + KnUpdateFlagsSolid(flags, fractions, phiSolid, phiOut, phiIn, boundaryWidth); } + // Update solid flags from levelsets + // PYTHON() void setSolidFlags(FlagGrid& flags, const Grid& phiSolid, int boundaryWidth=1) { + // knUpdateFlagsSolid(flags, phiSolid, boundaryWidth); + // } + + // PYTHON() bool initShapeMatchingConstraint( + // const Vector3r x0[], const Real invMasses[], int numPoints, + // Vector3r &restCm, Matrix3r &invRestMat) + // { + // invRestMat.setIdentity(); + + // // center of mass + // restCm.setZero(); + // Real wsum = 0.0; + // for (int i = 0; i < numPoints; i++) { + // Real wi = static_cast(1.0) / (invMasses[i] + eps); + // restCm += x0[i] * wi; + // wsum += wi; + // } + // if (wsum == 0.0) + // return false; + // restCm /= wsum; + + // // A + // Matrix3r A; + // A.setZero(); + // for (int i = 0; i < numPoints; i++) { + // const Vector3r qi = x0[i] - restCm; + // Real wi = static_cast(1.0) / (invMasses[i] + eps); + // Real x2 = wi * qi[0] * qi[0]; + // Real y2 = wi * qi[1] * qi[1]; + // Real z2 = wi * qi[2] * qi[2]; + // Real xy = wi * qi[0] * qi[1]; + // Real xz = wi * qi[0] * qi[2]; + // Real yz = wi * qi[1] * qi[2]; + // A(0, 0) += x2; A(0, 1) += xy; A(0, 2) += xz; + // A(1, 0) += xy; A(1, 1) += y2; A(1, 2) += yz; + // A(2, 0) += xz; A(2, 1) += yz; A(2, 2) += z2; + // } + // Real det = A.determinant(); + // if (fabs(det) > eps) + // { + // invRestMat = A.inverse(); + // return true; + // } + // return false; + // } + + // PYTHON() bool solveShapeMatchingConstraint( + // const Vector3r x0[], const Vector3r x[], const Real invMasses[], int numPoints, + // const Vector3r &restCm, + // const Matrix3r &invRestMat, + // const Real stiffness, + // const bool allowStretch, + // Vector3r corr[], Matrix3r *rot) + // { + // for (int i = 0; i < numPoints; i++) + // corr[i].setZero(); + + // // center of mass + // Vector3r cm(0.0, 0.0, 0.0); + // Real wsum = 0.0; + // for (int i = 0; i < numPoints; i++) + // { + // Real wi = static_cast(1.0) / (invMasses[i] + eps); + // cm += x[i] * wi; + // wsum += wi; + // } + // if (wsum == 0.0) + // return false; + // cm /= wsum; + + // // A + // Matrix3r mat; + // mat.setZero(); + // for (int i = 0; i < numPoints; i++) { + // Vector3r q = x0[i] - restCm; + // Vector3r p = x[i] - cm; + + // Real w = static_cast(1.0) / (invMasses[i] + eps); + // p *= w; + + // mat(0, 0) += p[0] * q[0]; mat(0, 1) += p[0] * q[1]; mat(0, 2) += p[0] * q[2]; + // mat(1, 0) += p[1] * q[0]; mat(1, 1) += p[1] * q[1]; mat(1, 2) += p[1] * q[2]; + // mat(2, 0) += p[2] * q[0]; mat(2, 1) += p[2] * q[1]; mat(2, 2) += p[2] * q[2]; + // } + + // mat = mat * invRestMat; + + // Matrix3r R, U, D; + // R = mat; + // if (allowStretch) + // R = mat; + // else + // //MathFunctions::polarDecomposition(mat, R, U, D); + // MathFunctions::polarDecompositionStable(mat, eps, R); + + // for (int i = 0; i < numPoints; i++) { + // Vector3r goal = cm + R * (x0[i] - restCm); + // corr[i] = (goal - x[i]) * stiffness; + // } + + // if (rot) + // *rot = R; + + // return true; + // } + } // end namespace DDF From 153aeabde101d3e8e7e9cf98063960003f700adb Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Thu, 15 Oct 2020 17:05:30 -0400 Subject: [PATCH 12/15] The solid flag is used in the sampleFlagsWithParticles and addGravity functions. Added a new markSolidCells function. --- scenes/bacilli_on_agar.3.py | 111 ++++++++++++++++++++++++++++++++++++ source/grid.h | 15 +++-- source/plugin/extforces.cpp | 34 ++++++++++- source/plugin/flip.cpp | 91 ++++++++++++++++++++++++++++- 4 files changed, 243 insertions(+), 8 deletions(-) create mode 100644 scenes/bacilli_on_agar.3.py diff --git a/scenes/bacilli_on_agar.3.py b/scenes/bacilli_on_agar.3.py new file mode 100644 index 00000000..5a474ab3 --- /dev/null +++ b/scenes/bacilli_on_agar.3.py @@ -0,0 +1,111 @@ +# +# Very simple flip without level set +# and without any particle resampling +# +from manta import * + +# solver params +dim = 3 +particleNumber = 2 +res = 64 +gs = vec3(res,res,res) + +if (dim==2): + gs.z=1 + particleNumber = 3 # use more particles in 2d + +s = Solver(name='main', gridSize = gs, dim=dim) +s.timestep = 0.5 + +# prepare grids and particles +flags = s.create(FlagGrid) +vel = s.create(MACGrid) +velOld = s.create(MACGrid) +pressure = s.create(RealGrid) +tmpVec3 = s.create(VecGrid) +pp = s.create(BasicParticleSystem) +# add velocity data to particles +pVel = pp.create(PdataVec3) +mesh = s.create(Mesh) + +# scene setup +flags.initDomain(boundaryWidth=0) + +# The scale is 1 = 10 micrometer (1 x 10E-7 meter) +# Agar +agar = Box(parent=s, p0=gs*vec3(0,0,0), p1=gs*vec3(1.0,0.1,1.0)) +phi = agar.computeLevelset() + +# Bacillus +center = vec3(0.5,0.2,0.5) +radius = 0.05 +zpoint = vec3(0, 0, 0.1) +# The mantaflow cylinder has a capsule-like geometry +bacillus = Cylinder(parent=s, center=gs*center, radius=res*radius, z=gs*zpoint) +phiBacillus = bacillus.computeLevelset() + +# Fluid Cylinder +centerTwo = vec3(0.3,0.2,0.5) +cylinder = Cylinder(parent=s, center=gs*centerTwo, radius=res*radius, z=gs*zpoint) +phiCylinder = cylinder.computeLevelset() + +# Set the flags to solid for the bacillus +setSolidFlags(flags=flags, phiSolid=phiBacillus) + +# Join the agar and fluid cylinder levelsets +phi.join(phiCylinder) + +# Set the flags to fluid for the agar and cylinder +flags.updateFromLevelset(phi) + +# There's no resamplig here, so we need _LOTS_ of particles... +sampleFlagsWithParticles(flags=flags, parts=pp, discretization=particleNumber, randomness=0.2) + + +if (GUI): + gui = Gui() + gui.show() + gui.pause() + +if (dim==3): + phi.createMesh(mesh) + +#main loop +for t in range(2500): + # mantaMsg('\nFrame %i, simulation time %f' % (s.frame, s.timeTotal)) + + # Algorithm 1 in Gao, 2018 + + # 1: Advect velocities of particles + # 2: Enforce external forces (gravity) + # 3: Verify fluid and solid particle flags via Fsolid, Ffluid + # 4: Map all the particles to grid ug ← up, xg ← xp + # 6: Project up ← ug,xp ← xg0 + ugΔt + # 7: if particle ∈ Fsolid then + # 8: Project shape matching constraint + # 9: Compute target position g + # 10: Update x∗+=α 􏰉g − x 􏰊 + # 11: Update u p∗ ← ( u p , ( x p∗ − x p0 ) / Δ t ) + # 12: else continue + # 13: Correct boundary conditions + # 14: Conduct the mechanism of penetration prevention + # 15: Update the velocities and positions of all particles + # 16: Update particles’ flags + + pp.advectInGrid(flags=flags, vel=vel, integrationMode=IntRK4, deleteInObstacle=False ) + mapPartsToMAC(vel=vel, flags=flags, velOld=velOld, parts=pp, partVel=pVel, weight=tmpVec3 ) + extrapolateMACFromWeight( vel=vel , distance=2, weight=tmpVec3 ) + + markFluidCells(parts=pp, flags=flags) + markSolidCells(parts=pp, flags=flags) + + addGravity(flags=flags, vel=vel, gravity=(0,-0.002,0)) + setWallBcs(flags=flags, vel=vel) + solvePressure(flags=flags, vel=vel, pressure=pressure) + # setWallBcs(flags=flags, vel=vel) + + extrapolateMACSimple( flags=flags, vel=vel ) + flipVelocityUpdate(vel=vel, velOld=velOld, flags=flags, parts=pp, partVel=pVel, flipRatio=0.97) + + s.step() + diff --git a/source/grid.h b/source/grid.h index d223c7e5..c8bd6ec1 100644 --- a/source/grid.h +++ b/source/grid.h @@ -313,33 +313,36 @@ PYTHON() class FlagGrid : public Grid { inline bool isObstacle(const Vec3i& pos) const { return get(pos) & TypeObstacle; } inline bool isObstacle(const Vec3& pos) const { return getAt(pos) & TypeObstacle; } - // Major Hack!!! - - inline bool isFluid(IndexInt idx) const { return get(idx) & (TypeFluid || TypeSolid); } - inline bool isFluid(int i, int j, int k) const { return get(i,j,k) & (TypeFluid || TypeSolid); } - inline bool isFluid(const Vec3i& pos) const { return get(pos) & (TypeFluid || TypeSolid); } - inline bool isFluid(const Vec3& pos) const { return getAt(pos) & (TypeFluid || TypeSolid); } + inline bool isFluid(IndexInt idx) const { return get(idx) & TypeFluid; } + inline bool isFluid(int i, int j, int k) const { return get(i,j,k) & TypeFluid; } + inline bool isFluid(const Vec3i& pos) const { return get(pos) & TypeFluid; } + inline bool isFluid(const Vec3& pos) const { return getAt(pos) & TypeFluid; } inline bool isInflow(IndexInt idx) const { return get(idx) & TypeInflow; } inline bool isInflow(int i, int j, int k) const { return get(i,j,k) & TypeInflow; } inline bool isInflow(const Vec3i& pos) const { return get(pos) & TypeInflow; } inline bool isInflow(const Vec3& pos) const { return getAt(pos) & TypeInflow; } + inline bool isEmpty(IndexInt idx) const { return get(idx) & TypeEmpty; } inline bool isEmpty(int i, int j, int k) const { return get(i,j,k) & TypeEmpty; } inline bool isEmpty(const Vec3i& pos) const { return get(pos) & TypeEmpty; } inline bool isEmpty(const Vec3& pos) const { return getAt(pos) & TypeEmpty; } + inline bool isOutflow(IndexInt idx) const { return get(idx) & TypeOutflow; } inline bool isOutflow(int i, int j, int k) const { return get(i, j, k) & TypeOutflow; } inline bool isOutflow(const Vec3i& pos) const { return get(pos) & TypeOutflow; } inline bool isOutflow(const Vec3& pos) const { return getAt(pos) & TypeOutflow; } + inline bool isOpen(IndexInt idx) const { return get(idx) & TypeOpen; } inline bool isOpen(int i, int j, int k) const { return get(i, j, k) & TypeOpen; } inline bool isOpen(const Vec3i& pos) const { return get(pos) & TypeOpen; } inline bool isOpen(const Vec3& pos) const { return getAt(pos) & TypeOpen; } + inline bool isStick(IndexInt idx) const { return get(idx) & TypeStick; } inline bool isStick(int i, int j, int k) const { return get(i,j,k) & TypeStick; } inline bool isStick(const Vec3i& pos) const { return get(pos) & TypeStick; } inline bool isStick(const Vec3& pos) const { return getAt(pos) & TypeStick; } + inline bool isSolid(IndexInt idx) const { return get(idx) & TypeSolid; } inline bool isSolid(int i, int j, int k) const { return get(i,j,k) & TypeSolid; } inline bool isSolid(const Vec3i& pos) const { return get(pos) & TypeSolid; } diff --git a/source/plugin/extforces.cpp b/source/plugin/extforces.cpp index 731a8ceb..6849a540 100644 --- a/source/plugin/extforces.cpp +++ b/source/plugin/extforces.cpp @@ -43,18 +43,50 @@ KERNEL(bnd=1) void KnApplyForceField(const FlagGrid& flags, MACGrid& vel, const } //! add constant force between fl/fl and fl/em cells +// KERNEL(bnd=1) void KnApplyForce(const FlagGrid& flags, MACGrid& vel, Vec3 force, const Grid* exclude, bool additive) { +// bool curFluid = flags.isFluid(i,j,k); +// bool curEmpty = flags.isEmpty(i,j,k); +// if (!curFluid && !curEmpty) return; +// if (exclude && ((*exclude)(i,j,k) < 0.)) return; + +// if (flags.isFluid(i-1,j,k) || (curFluid && flags.isEmpty(i-1,j,k))) +// vel(i,j,k).x = (additive) ? vel(i,j,k).x+force.x : force.x; +// if (flags.isFluid(i,j-1,k) || (curFluid && flags.isEmpty(i,j-1,k))) +// vel(i,j,k).y = (additive) ? vel(i,j,k).y+force.y : force.y; +// if (vel.is3D() && (flags.isFluid(i,j,k-1) || (curFluid && flags.isEmpty(i,j,k-1)))) +// vel(i,j,k).z = (additive) ? vel(i,j,k).z+force.z : force.z; +// } + KERNEL(bnd=1) void KnApplyForce(const FlagGrid& flags, MACGrid& vel, Vec3 force, const Grid* exclude, bool additive) { bool curFluid = flags.isFluid(i,j,k); + bool curSolid = flags.isSolid(i,j,k); bool curEmpty = flags.isEmpty(i,j,k); - if (!curFluid && !curEmpty) return; + + if (!curFluid && !curEmpty && !curSolid) return; if (exclude && ((*exclude)(i,j,k) < 0.)) return; + // fl/fl and fl/em if (flags.isFluid(i-1,j,k) || (curFluid && flags.isEmpty(i-1,j,k))) vel(i,j,k).x = (additive) ? vel(i,j,k).x+force.x : force.x; + if (flags.isFluid(i,j-1,k) || (curFluid && flags.isEmpty(i,j-1,k))) vel(i,j,k).y = (additive) ? vel(i,j,k).y+force.y : force.y; + if (vel.is3D() && (flags.isFluid(i,j,k-1) || (curFluid && flags.isEmpty(i,j,k-1)))) vel(i,j,k).z = (additive) ? vel(i,j,k).z+force.z : force.z; + + // solid/solid and solid/em + + if (flags.isSolid(i-1,j,k) || (curSolid && flags.isEmpty(i-1,j,k))) + vel(i,j,k).x = (additive) ? vel(i,j,k).x+force.x : force.x; + + if (flags.isSolid(i,j-1,k) || (curSolid && flags.isEmpty(i,j-1,k))) + vel(i,j,k).y = (additive) ? vel(i,j,k).y+force.y : force.y; + + if (vel.is3D() && (flags.isSolid(i,j,k-1) || (curSolid && flags.isEmpty(i,j,k-1)))) + vel(i,j,k).z = (additive) ? vel(i,j,k).z+force.z : force.z; + + // fluid/solid } //! add gravity forces to all fluid cells, optionally adapts to different grid sizes automatically diff --git a/source/plugin/flip.cpp b/source/plugin/flip.cpp index 0c9c2415..08b71225 100644 --- a/source/plugin/flip.cpp +++ b/source/plugin/flip.cpp @@ -29,6 +29,31 @@ namespace Manta { // init //! note - this is a simplified version , sampleLevelsetWithParticles has more functionality +// PYTHON() void sampleFlagsWithParticles(const FlagGrid& flags, BasicParticleSystem& parts, +// const int discretization, const Real randomness) +// { +// const bool is3D = flags.is3D(); +// const Real jlen = randomness / discretization; +// const Vec3 disp (1.0 / discretization, 1.0 / discretization, 1.0/discretization); +// RandomStream mRand(9832); + +// FOR_IJK_BND(flags, 0) { +// if (flags.isObstacle(i,j,k)) continue; +// if (flags.isFluid(i,j,k)) { +// const Vec3 pos (i,j,k); +// for (int dk=0; dk<(is3D ? discretization : 1); dk++) +// for (int dj=0; dj* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { +// // remove all fluid and solid cells +// knClearFluidSolidFlags(flags, 0); + +// // mark all particles in flaggrid as fluid +// for(IndexInt idx=0; idx* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { + // remove all solid cells + knClearSolidFlags(flags, 0); + + // mark all particles in flaggrid as solid + for(IndexInt idx=0; idx& grid) { FOR_IJK(grid) { grid(i,j,k) = norm( Vec3(i,j,k) ); } @@ -564,6 +652,7 @@ void knMapLinearVec3ToMACGrid(const BasicParticleSystem& p, const FlagGrid& flag const ParticleDataImpl& pvel, const ParticleDataImpl* ptype, const int exclude) { unusedParameter(flags); + if (!p.isActive(idx) || (ptype && ((*ptype)[idx] & exclude))) return; vel.setInterpolated( p[idx].pos, pvel[idx], &tmp[0] ); } From 28ab35255b73f35f77a011a44b8a01407c6770d5 Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Fri, 16 Oct 2020 03:45:54 -0400 Subject: [PATCH 13/15] Particles that are part of a solid can be tracked over time. Need to modify the pressure solver to include TypeSolid particles. --- scenes/bacilli_on_agar.3.py | 44 +++++++++++++++------ source/grid.h | 2 +- source/plugin/extforces.cpp | 2 +- source/plugin/flip.cpp | 78 ++++++++++++++++++++++--------------- source/plugin/pbd.cpp | 16 +++++++- 5 files changed, 94 insertions(+), 48 deletions(-) diff --git a/scenes/bacilli_on_agar.3.py b/scenes/bacilli_on_agar.3.py index 5a474ab3..a3a048de 100644 --- a/scenes/bacilli_on_agar.3.py +++ b/scenes/bacilli_on_agar.3.py @@ -49,10 +49,14 @@ cylinder = Cylinder(parent=s, center=gs*centerTwo, radius=res*radius, z=gs*zpoint) phiCylinder = cylinder.computeLevelset() +# phi.join(phiBacillus) + # Set the flags to solid for the bacillus setSolidFlags(flags=flags, phiSolid=phiBacillus) +# setSolidFlags(flags=flags, phiSolid=phi) -# Join the agar and fluid cylinder levelsets +# Join the agar, bacillus, and fluid cylinder levelsets +phi.join(phiBacillus) phi.join(phiCylinder) # Set the flags to fluid for the agar and cylinder @@ -61,21 +65,30 @@ # There's no resamplig here, so we need _LOTS_ of particles... sampleFlagsWithParticles(flags=flags, parts=pp, discretization=particleNumber, randomness=0.2) - +indices = [] + +for index in range(pp.pySize()): + inSolid = isParticleInSolid(index=index, particles=pp, flags=flags) + + if inSolid: + indices.append(index) + +# print(indices) + if (GUI): gui = Gui() gui.show() gui.pause() -if (dim==3): - phi.createMesh(mesh) +# if (dim==3): +# phi.createMesh(mesh) #main loop for t in range(2500): # mantaMsg('\nFrame %i, simulation time %f' % (s.frame, s.timeTotal)) # Algorithm 1 in Gao, 2018 - + # 1: Advect velocities of particles # 2: Enforce external forces (gravity) # 3: Verify fluid and solid particle flags via Fsolid, Ffluid @@ -92,20 +105,27 @@ # 15: Update the velocities and positions of all particles # 16: Update particles’ flags + # FLIP pp.advectInGrid(flags=flags, vel=vel, integrationMode=IntRK4, deleteInObstacle=False ) mapPartsToMAC(vel=vel, flags=flags, velOld=velOld, parts=pp, partVel=pVel, weight=tmpVec3 ) extrapolateMACFromWeight( vel=vel , distance=2, weight=tmpVec3 ) - markFluidCells(parts=pp, flags=flags) - markSolidCells(parts=pp, flags=flags) + clearSolidFlags(flags = flags) + + for index in indices: + markCellSolid(index=index, particles=pp, flags=flags) addGravity(flags=flags, vel=vel, gravity=(0,-0.002,0)) + + # pressure solve setWallBcs(flags=flags, vel=vel) solvePressure(flags=flags, vel=vel, pressure=pressure) - # setWallBcs(flags=flags, vel=vel) - - extrapolateMACSimple( flags=flags, vel=vel ) - flipVelocityUpdate(vel=vel, velOld=velOld, flags=flags, parts=pp, partVel=pVel, flipRatio=0.97) + setWallBcs(flags=flags, vel=vel) - s.step() + # we dont have any levelset, ie no extrapolation, so make sure the velocities are valid + extrapolateMACSimple(flags=flags, vel=vel) + + # FLIP velocity update + flipVelocityUpdate(vel=vel, velOld=velOld, flags=flags, parts=pp, partVel=pVel, flipRatio=0.97 ) + s.step() \ No newline at end of file diff --git a/source/grid.h b/source/grid.h index c8bd6ec1..46e8af42 100644 --- a/source/grid.h +++ b/source/grid.h @@ -343,7 +343,7 @@ PYTHON() class FlagGrid : public Grid { inline bool isStick(const Vec3i& pos) const { return get(pos) & TypeStick; } inline bool isStick(const Vec3& pos) const { return getAt(pos) & TypeStick; } - inline bool isSolid(IndexInt idx) const { return get(idx) & TypeSolid; } + PYTHON() inline bool isSolid(IndexInt idx) const { return get(idx) & TypeSolid; } inline bool isSolid(int i, int j, int k) const { return get(i,j,k) & TypeSolid; } inline bool isSolid(const Vec3i& pos) const { return get(pos) & TypeSolid; } inline bool isSolid(const Vec3& pos) const { return getAt(pos) & TypeSolid; } diff --git a/source/plugin/extforces.cpp b/source/plugin/extforces.cpp index 6849a540..9fbe11d9 100644 --- a/source/plugin/extforces.cpp +++ b/source/plugin/extforces.cpp @@ -42,7 +42,7 @@ KERNEL(bnd=1) void KnApplyForceField(const FlagGrid& flags, MACGrid& vel, const vel(i,j,k).z = (additive) ? vel(i,j,k).z+forceZ : forceZ; } -//! add constant force between fl/fl and fl/em cells +// ! add constant force between fl/fl and fl/em cells // KERNEL(bnd=1) void KnApplyForce(const FlagGrid& flags, MACGrid& vel, Vec3 force, const Grid* exclude, bool additive) { // bool curFluid = flags.isFluid(i,j,k); // bool curEmpty = flags.isEmpty(i,j,k); diff --git a/source/plugin/flip.cpp b/source/plugin/flip.cpp index 08b71225..837037fe 100644 --- a/source/plugin/flip.cpp +++ b/source/plugin/flip.cpp @@ -161,11 +161,6 @@ PYTHON() void sampleShapeWithParticles(const Shape& shape, const FlagGrid& flags } //! mark fluid cells and helpers -KERNEL() void knClearFluidFlags(FlagGrid& flags, int dummy=0) { - if (flags.isFluid(i,j,k)) { - flags(i,j,k) = (flags(i,j,k) | FlagGrid::TypeEmpty) & ~FlagGrid::TypeFluid; - } -} KERNEL(bnd=1) void knSetNbObstacle(FlagGrid& nflags, const FlagGrid& flags, const Grid& phiObs) { if ( phiObs(i,j,k)>0. ) return; @@ -182,11 +177,18 @@ void knSetNbObstacle(FlagGrid& nflags, const FlagGrid& flags, const Grid& if(set) nflags(i,j,k) = (flags(i,j,k) | FlagGrid::TypeFluid) & ~FlagGrid::TypeEmpty; } } + +KERNEL() void knClearFluidFlags(FlagGrid& flags, int dummy=0) { + if (flags.isFluid(i,j,k)) { + flags(i,j,k) = (flags(i,j,k) | FlagGrid::TypeEmpty) & ~FlagGrid::TypeFluid; + } +} + PYTHON() void markFluidCells(const BasicParticleSystem& parts, FlagGrid& flags, const Grid* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { // remove all fluid cells knClearFluidFlags(flags, 0); - // mark all particles in flaggrid as fluid + // mark fluid particles in flaggrid as fluid for(IndexInt idx=0; idx* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { +// // remove all solid cells +// knClearSolidFlags(flags, 0); + +// // Mark particles in a solid as solid in the flaggrid +// for(IndexInt idx=0; idx* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { +PYTHON() void clearSolidFlags(FlagGrid& flags, int dummy = 0) { // remove all solid cells knClearSolidFlags(flags, 0); - - // mark all particles in flaggrid as solid - for(IndexInt idx=0; idx* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { + Vec3i p = toVec3i(particles.getPos(index)); + flags(p) = FlagGrid::TypeSolid; } // for testing purposes only... diff --git a/source/plugin/pbd.cpp b/source/plugin/pbd.cpp index 06854f49..f49515f8 100644 --- a/source/plugin/pbd.cpp +++ b/source/plugin/pbd.cpp @@ -20,6 +20,7 @@ #include "vectorbase.h" #include "grid.h" #include "kernel.h" +#include "particle.h" #include using namespace std; @@ -67,12 +68,23 @@ namespace Manta { else flags(i,j,k) = FlagGrid::TypeEmpty; } - //! update obstacle and outflow flags from levelsets - //! optionally uses fill fractions for obstacle PYTHON() void setSolidFlags(FlagGrid& flags, const Grid& phiSolid, const MACGrid* fractions=NULL, const Grid* phiOut=NULL, const Grid* phiIn=NULL, int boundaryWidth=1){ KnUpdateFlagsSolid(flags, fractions, phiSolid, phiOut, phiIn, boundaryWidth); } + PYTHON() bool isParticleInSolid(const int index, const BasicParticleSystem& particles, FlagGrid& flags) { + bool inSolid; + + Vec3i position = toVec3i(particles.getPos(index)); + + if (flags.isSolid(position)) + inSolid = true; + else + inSolid = false; + + return inSolid; + } + // Update solid flags from levelsets // PYTHON() void setSolidFlags(FlagGrid& flags, const Grid& phiSolid, int boundaryWidth=1) { // knUpdateFlagsSolid(flags, phiSolid, boundaryWidth); From e055f31b32812ae1b3cecb880b8a0e9c2ed67eda Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Sun, 18 Oct 2020 21:55:48 -0400 Subject: [PATCH 14/15] Particles in a ParticleSystem can store if a particle is in a solid object. The Position-based Dynamics plugin has functions that keep cells in a flag grid with the TypeSolid status when there are particles in the cell that have a PSOLID status (ie. part of a solid object). --- scenes/bacilli_on_agar.2.py | 144 ------------------------------------ scenes/bacilli_on_agar.3.py | 22 ++---- source/particle.h | 2 + source/plugin/flip.cpp | 72 ------------------ source/plugin/pbd.cpp | 59 ++++++++++++--- source/python/defines.py | 2 + 6 files changed, 60 insertions(+), 241 deletions(-) delete mode 100644 scenes/bacilli_on_agar.2.py diff --git a/scenes/bacilli_on_agar.2.py b/scenes/bacilli_on_agar.2.py deleted file mode 100644 index 7431b929..00000000 --- a/scenes/bacilli_on_agar.2.py +++ /dev/null @@ -1,144 +0,0 @@ -# -# Bacilli on Agar -# - -from manta import * - -# solver params -dim = 3 -res = 64 -# res = 128 -gs = vec3(res,res,res) - -if (dim==2): - gs.z=1 - -solver = Solver(name='main', gridSize = gs, dim=dim) -solver.timestep = 0.8 -minParticles = pow(2,dim) - -# save particles for separate surface generation pass? -saveParts = False - -# size of particles -radiusFactor = 1.0 - -# prepare grids and particles -flags = solver.create(FlagGrid) -phi = solver.create(LevelsetGrid) -vel = solver.create(MACGrid) -velOld = solver.create(MACGrid) -pressure = solver.create(RealGrid) -tmpVec3 = solver.create(VecGrid) -force = solver.create(VecGrid) -pp = solver.create(BasicParticleSystem) -pVel = pp.create(PdataVec3) -mesh = solver.create(Mesh) - -# acceleration data for particle nbs -pindex = solver.create(ParticleIndexSystem) -gpi = solver.create(IntGrid) -bWidth=1 -flags.initDomain(boundaryWidth=bWidth) -fluidVel = 0 -fluidSetVel = 0 - -# The scale is 1 = 10 micrometer (1 x 10E-7 meter) -# Agar -agar = Box(parent=solver, p0=gs*vec3(0,0,0), p1=gs*vec3(1.0,0.1,1.0)) -phi = agar.computeLevelset() - -# Bacillus -center = vec3(0.5,0.2,0.5) -radius = 0.05 -zpoint = vec3(0, 0, 0.1) -bacillus = Cylinder(parent=solver, center=gs*center, radius=res*radius, z=gs*zpoint) # The mantaflow cylinder has a capsule-like geometry -phiBacillus = bacillus.computeLevelset() -setSolidFlags(flags=flags, phiSolid=phiBacillus) - -# Join the agar and bacillus levelsets -phi.join(phiBacillus) - -flags.updateFromLevelset(phi) -sampleLevelsetWithParticles(phi=phi, flags=flags, parts=pp, discretization=2, randomness=0.05) - -if 1 and (GUI): - gui = Gui() - gui.show() - gui.pause() - - -#main loop -for t in range(2500): - # mantaMsg('\nFrame %i, simulation time %f' % (solver.frame, solver.timeTotal)) - - # if t==0: - # flags.printGrid() - - # Algorithm 1 in Gao, 2018 - # 1: Advect velocities of particles - pp.advectInGrid(flags=flags, vel=vel, integrationMode=IntRK4, deleteInObstacle=False ) - - # make sure we have velocities throughout liquid region - mapPartsToMAC(vel=vel, flags=flags, velOld=velOld, parts=pp, partVel=pVel, weight=tmpVec3 ) - extrapolateMACFromWeight(vel=vel , distance=2, weight=tmpVec3) # note, tmpVec3 could be free'd now... - markFluidCells(parts=pp, flags=flags) - - # create approximate surface level set, resample particles - gridParticleIndex(parts=pp , flags=flags, indexSys=pindex, index=gpi) - unionParticleLevelset(pp, pindex, flags, gpi, phi , radiusFactor) - resetOutflow(flags=flags,parts=pp,index=gpi,indexSys=pindex) - # extend levelset somewhat, needed by particle resampling in adjustNumber - extrapolateLsSimple(phi=phi, distance=4, inside=True); - - setWallBcs(flags=flags, vel=vel) - solvePressure(flags=flags, vel=vel, pressure=pressure, phi=phi) - - # set source grids for resampling, used in adjustNumber! - pVel.setSource( vel, isMAC=True ) - adjustNumber( parts=pp, vel=vel, flags=flags, minParticles=1*minParticles, maxParticles=2*minParticles, phi=phi, radiusFactor=radiusFactor ) - - # make sure we have proper velocities - extrapolateMACSimple( flags=flags, vel=vel ) - - # 2: Enforce external forces (gravity) - addGravity(flags=flags, vel=vel, gravity=(0,-0.001,0)) - - # if t == 3: - # force.setConst(vec3(0.0, 0.0, 0.5)) - # addForceField(flags=flags, vel=vel, force=force) - - # This is likely FLIP - # 3: Verify fluid and solid particle flags via Fsolid, Ffluid - # 4: Map all the particles to grid ug ← up, xg ← xp - # 5: Compute level set Φ and velocity on grid ug - # 6: Project up ← ug,xp ← xg0 + ugΔt - # 7: if particle ∈ Fsolid then - - # This is PBD - # 8: Project shape matching constraint - # 9: Compute target position g - # 10: Update x∗+=α 􏰉g − x 􏰊 - # 11: Update u p∗ ← ( u p , ( x p∗ − x p0 ) / Δ t ) - # 12: else continue - # 13: Correct boundary conditions - # 14: Conduct the mechanism of penetration prevention - - # 15: Update the velocities and positions of all particles - flipVelocityUpdate(vel=vel, velOld=velOld, flags=flags, parts=pp, partVel=pVel, flipRatio=0.97 ) - - # 16: Update particles’ flags - - if (dim==3): - phi.createMesh(mesh) - # mesh.fromShape(bacillus, append=True) - - #solver.printMemInfo() - - if t == 20: - flags.printGrid() - - solver.step() - - - diff --git a/scenes/bacilli_on_agar.3.py b/scenes/bacilli_on_agar.3.py index a3a048de..bae14f2a 100644 --- a/scenes/bacilli_on_agar.3.py +++ b/scenes/bacilli_on_agar.3.py @@ -1,7 +1,8 @@ # -# Very simple flip without level set -# and without any particle resampling +# Bacilli on Agar # +# + from manta import * # solver params @@ -26,7 +27,7 @@ pp = s.create(BasicParticleSystem) # add velocity data to particles pVel = pp.create(PdataVec3) -mesh = s.create(Mesh) +mesh = s.create(Mesh) # scene setup flags.initDomain(boundaryWidth=0) @@ -65,15 +66,7 @@ # There's no resamplig here, so we need _LOTS_ of particles... sampleFlagsWithParticles(flags=flags, parts=pp, discretization=particleNumber, randomness=0.2) -indices = [] - -for index in range(pp.pySize()): - inSolid = isParticleInSolid(index=index, particles=pp, flags=flags) - - if inSolid: - indices.append(index) - -# print(indices) +setStatusOfParticlesInSolid(particles=pp, flags=flags) if (GUI): gui = Gui() @@ -110,11 +103,8 @@ mapPartsToMAC(vel=vel, flags=flags, velOld=velOld, parts=pp, partVel=pVel, weight=tmpVec3 ) extrapolateMACFromWeight( vel=vel , distance=2, weight=tmpVec3 ) markFluidCells(parts=pp, flags=flags) - clearSolidFlags(flags = flags) + markSolidCells(particles=pp, flags=flags) - for index in indices: - markCellSolid(index=index, particles=pp, flags=flags) - addGravity(flags=flags, vel=vel, gravity=(0,-0.002,0)) # pressure solve diff --git a/source/particle.h b/source/particle.h index ba484f5a..5ff39b7b 100644 --- a/source/particle.h +++ b/source/particle.h @@ -38,6 +38,7 @@ PYTHON() class ParticleBase : public PbClass { PBUBBLE = (1<<2), PFOAM = (1<<3), PTRACER = (1<<4), + PSOLID = (1<<5), PDELETE = (1<<10), // mark as deleted, will be deleted in next compress() step PINVALID = (1<<30), // unused }; @@ -124,6 +125,7 @@ PYTHON() template class ParticleSystem : public ParticleBase { inline bool isBubble(IndexInt idx) const { DEBUG_ONLY(checkPartIndex(idx)); return (mData[idx].flag & PBUBBLE); } inline bool isFoam(IndexInt idx) const { DEBUG_ONLY(checkPartIndex(idx)); return (mData[idx].flag & PFOAM); } inline bool isTracer(IndexInt idx) const { DEBUG_ONLY(checkPartIndex(idx)); return (mData[idx].flag & PTRACER); } + inline bool isSolid(IndexInt idx) const { DEBUG_ONLY(checkPartIndex(idx)); return (mData[idx].flag & PSOLID); } //! update status inline void setStatus(IndexInt idx, const int status) { DEBUG_ONLY(checkPartIndex(idx)); mData[idx].flag = status; } diff --git a/source/plugin/flip.cpp b/source/plugin/flip.cpp index 837037fe..51bb24df 100644 --- a/source/plugin/flip.cpp +++ b/source/plugin/flip.cpp @@ -207,83 +207,11 @@ PYTHON() void markFluidCells(const BasicParticleSystem& parts, FlagGrid& flags, } } -// KERNEL() void knClearFluidSolidFlags(FlagGrid& flags, int dummy=0) { -// if (flags.isFluid(i,j,k)) { -// flags(i,j,k) = (flags(i,j,k) | FlagGrid::TypeEmpty) & ~FlagGrid::TypeFluid); -// } - -// if (flags.isSolid(i,j,k)) { -// flags(i,j,k) = (flags(i,j,k) | FlagGrid::TypeEmpty) & ~FlagGrid::TypeSolid; -// } -// } - -// PYTHON() void markFluidSolidCells(const BasicParticleSystem& parts, FlagGrid& flags, const Grid* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { -// // remove all fluid and solid cells -// knClearFluidSolidFlags(flags, 0); - -// // mark all particles in flaggrid as fluid -// // for(IndexInt idx=0; idx* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { -// // remove all solid cells -// knClearSolidFlags(flags, 0); - -// // Mark particles in a solid as solid in the flaggrid -// for(IndexInt idx=0; idx* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { - Vec3i p = toVec3i(particles.getPos(index)); - flags(p) = FlagGrid::TypeSolid; -} - // for testing purposes only... PYTHON() void testInitGridWithPos(Grid& grid) { FOR_IJK(grid) { grid(i,j,k) = norm( Vec3(i,j,k) ); } } - - //! helper to calculate particle radius factor to cover the diagonal of a cell in 2d/3d inline Real calculateRadiusFactor(const Grid& grid, Real factor) { return (grid.is3D() ? sqrt(3.) : sqrt(2.) ) * (factor+.01); // note, a 1% safety factor is added here diff --git a/source/plugin/pbd.cpp b/source/plugin/pbd.cpp index f49515f8..d04597d9 100644 --- a/source/plugin/pbd.cpp +++ b/source/plugin/pbd.cpp @@ -72,19 +72,60 @@ namespace Manta { KnUpdateFlagsSolid(flags, fractions, phiSolid, phiOut, phiIn, boundaryWidth); } - PYTHON() bool isParticleInSolid(const int index, const BasicParticleSystem& particles, FlagGrid& flags) { - bool inSolid; + PYTHON() void setStatusOfParticlesInSolid(BasicParticleSystem& particles, const FlagGrid& flags) { - Vec3i position = toVec3i(particles.getPos(index)); - - if (flags.isSolid(position)) - inSolid = true; - else - inSolid = false; + for(IndexInt index=0; index < particles.size(); index++) { + Vec3i position = toVec3i(particles.getPos(index)); + + if (flags.isSolid(position)) + particles.setStatus(index, particles.PSOLID); + } + } + + KERNEL() void knClearSolidFlags(FlagGrid& flags, int dummy=0) { + if (flags.isSolid(i,j,k)) { + flags(i,j,k) = (flags(i,j,k) | FlagGrid::TypeEmpty) & ~FlagGrid::TypeSolid; + } + } - return inSolid; + PYTHON() void markSolidCells(const BasicParticleSystem& particles, FlagGrid& flags, const Grid* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { + // remove all solid cells + knClearSolidFlags(flags, 0); + + // Mark particles in a solid as solid in the flaggrid + for(IndexInt index = 0; index < particles.size(); index++) { + if (!particles.isActive(index) || (ptype && ((*ptype)[index] & exclude))) continue; + + Vec3i position = toVec3i(particles.getPos(index)); + + if (particles.isSolid(index) && flags.isInBounds(position)) + flags(position) = FlagGrid::TypeSolid; + } } + // PYTHON() bool isParticleInSolid(const int index, const BasicParticleSystem& particles, FlagGrid& flags) { + // bool inSolid; + + // Vec3i position = toVec3i(particles.getPos(index)); + + // if (flags.isSolid(position)) + // inSolid = true; + // else + // inSolid = false; + + // return inSolid; + // } + + // PYTHON() void clearSolidFlags(FlagGrid& flags, int dummy = 0) { + // // remove all solid cells + // knClearSolidFlags(flags, 0); + // } + + // PYTHON() void markCellSolid(const int index, const BasicParticleSystem& particles, FlagGrid& flags, const Grid* phiObs=NULL, const ParticleDataImpl* ptype=NULL, const int exclude=0) { + // Vec3i p = toVec3i(particles.getPos(index)); + // flags(p) = FlagGrid::TypeSolid; + // } + // Update solid flags from levelsets // PYTHON() void setSolidFlags(FlagGrid& flags, const Grid& phiSolid, int boundaryWidth=1) { // knUpdateFlagsSolid(flags, phiSolid, boundaryWidth); diff --git a/source/python/defines.py b/source/python/defines.py index ec4299e9..d93eccda 100644 --- a/source/python/defines.py +++ b/source/python/defines.py @@ -28,6 +28,7 @@ FlagInflow = 8 FlagOutflow = 16 FlagStick = 64 +FlagSolid = 128 FlagReserved = 256 # and same for FlagGrid::CellType enum names: TypeFluid = 1 @@ -36,6 +37,7 @@ TypeInflow = 8 TypeOutflow = 16 TypeStick = 64 +TypeSolid = 128 TypeReserved = 256 # integration mode From e97f451499b2e55de435f0e5409ba05ae5137540 Mon Sep 17 00:00:00 2001 From: "Cesar A. Rodriguez" Date: Wed, 21 Oct 2020 18:31:27 -0400 Subject: [PATCH 15/15] Renamed pbd.cpp to solids.cpp. Added code to the setWallBcs function for handling solids. Working through the solvePressure function to have it work with solids. --- CMakeLists.txt | 2 +- scenes/bacilli_on_agar.3.py | 6 +-- source/plugin/extforces.cpp | 60 +++++++++++++++++++---- source/plugin/pressure.cpp | 68 ++++++++++++++++++++++++++- source/plugin/{pbd.cpp => solids.cpp} | 0 5 files changed, 120 insertions(+), 16 deletions(-) rename source/plugin/{pbd.cpp => solids.cpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a11aecd..c3c8794f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,7 +214,7 @@ set(PP_SOURCES source/plugin/vortexplugins.cpp source/plugin/waveletturbulence.cpp source/plugin/waves.cpp - source/plugin/pbd.cpp + source/plugin/solids.cpp source/python/defines.py source/test.cpp ) diff --git a/scenes/bacilli_on_agar.3.py b/scenes/bacilli_on_agar.3.py index bae14f2a..a8271814 100644 --- a/scenes/bacilli_on_agar.3.py +++ b/scenes/bacilli_on_agar.3.py @@ -63,7 +63,7 @@ # Set the flags to fluid for the agar and cylinder flags.updateFromLevelset(phi) -# There's no resamplig here, so we need _LOTS_ of particles... +# There's no resampling here, so we need _LOTS_ of particles... sampleFlagsWithParticles(flags=flags, parts=pp, discretization=particleNumber, randomness=0.2) setStatusOfParticlesInSolid(particles=pp, flags=flags) @@ -108,9 +108,9 @@ addGravity(flags=flags, vel=vel, gravity=(0,-0.002,0)) # pressure solve - setWallBcs(flags=flags, vel=vel) + # setWallBcs(flags=flags, vel=vel) solvePressure(flags=flags, vel=vel, pressure=pressure) - setWallBcs(flags=flags, vel=vel) + # setWallBcs(flags=flags, vel=vel) # we dont have any levelset, ie no extrapolation, so make sure the velocities are valid extrapolateMACSimple(flags=flags, vel=vel) diff --git a/source/plugin/extforces.cpp b/source/plugin/extforces.cpp index 9fbe11d9..92e9821e 100644 --- a/source/plugin/extforces.cpp +++ b/source/plugin/extforces.cpp @@ -218,9 +218,11 @@ PYTHON() void setInflowBcs(MACGrid& vel, string dir, Vec3 value) { KERNEL() void KnSetWallBcs(const FlagGrid& flags, MACGrid& vel, const MACGrid* obvel) { bool curFluid = flags.isFluid(i,j,k); + bool curSolid = flags.isSolid(i,j,k); bool curObs = flags.isObstacle(i,j,k); Vec3 bcsVel(0.,0.,0.); - if (!curFluid && !curObs) return; + + if (!curFluid && !curSolid && !curObs) return; if (obvel) { bcsVel.x = (*obvel)(i,j,k).x; @@ -229,16 +231,21 @@ KERNEL() void KnSetWallBcs(const FlagGrid& flags, MACGrid& vel, const MACGrid* o } // we use i>0 instead of bnd=1 to check outer wall - if (i>0 && flags.isObstacle(i-1,j,k)) vel(i,j,k).x = bcsVel.x; - if (i>0 && curObs && flags.isFluid(i-1,j,k)) vel(i,j,k).x = bcsVel.x; - if (j>0 && flags.isObstacle(i,j-1,k)) vel(i,j,k).y = bcsVel.y; - if (j>0 && curObs && flags.isFluid(i,j-1,k)) vel(i,j,k).y = bcsVel.y; - - if(!vel.is3D()) { vel(i,j,k).z = 0; } else { - if (k>0 && flags.isObstacle(i,j,k-1)) vel(i,j,k).z = bcsVel.z; - if (k>0 && curObs && flags.isFluid(i,j,k-1)) vel(i,j,k).z = bcsVel.z; } + if (i > 0 && flags.isObstacle(i-1,j,k)) vel(i,j,k).x = bcsVel.x; + if (i > 0 && curObs && flags.isFluid(i-1,j,k)) vel(i,j,k).x = bcsVel.x; + if (i > 0 && curObs && flags.isSolid(i-1,j,k)) vel(i,j,k).x = bcsVel.x; + if (j > 0 && flags.isObstacle(i,j-1,k)) vel(i,j,k).y = bcsVel.y; + if (j > 0 && curObs && flags.isFluid(i,j-1,k)) vel(i,j,k).y = bcsVel.y; + if (j > 0 && curObs && flags.isSolid(i,j-1,k)) vel(i,j,k).y = bcsVel.y; + + if(!vel.is3D()) {vel(i,j,k).z = 0;} + else { + if (k > 0 && flags.isObstacle(i,j,k-1)) vel(i,j,k).z = bcsVel.z; + if (k > 0 && curObs && flags.isFluid(i,j,k-1)) vel(i,j,k).z = bcsVel.z; + if (k > 0 && curObs && flags.isSolid(i,j,k-1)) vel(i,j,k).z = bcsVel.z; + } - if (curFluid) { + if (curFluid || curSolid) { if ((i>0 && flags.isStick(i-1,j,k)) || (i0 && flags.isStick(i,j-1,k)) || (j0 instead of bnd=1 to check outer wall +// if (i>0 && flags.isObstacle(i-1,j,k)) vel(i,j,k).x = bcsVel.x; +// if (i>0 && curObs && flags.isFluid(i-1,j,k)) vel(i,j,k).x = bcsVel.x; +// if (j>0 && flags.isObstacle(i,j-1,k)) vel(i,j,k).y = bcsVel.y; +// if (j>0 && curObs && flags.isFluid(i,j-1,k)) vel(i,j,k).y = bcsVel.y; + +// if(!vel.is3D()) { vel(i,j,k).z = 0; } else { +// if (k>0 && flags.isObstacle(i,j,k-1)) vel(i,j,k).z = bcsVel.z; +// if (k>0 && curObs && flags.isFluid(i,j,k-1)) vel(i,j,k).z = bcsVel.z; } + +// if (curFluid) { +// if ((i>0 && flags.isStick(i-1,j,k)) || (i0 && flags.isStick(i,j-1,k)) || (j0 && flags.isStick(i,j,k-1)) || (k* phiObs, const int &boundaryWidth=0) diff --git a/source/plugin/pressure.cpp b/source/plugin/pressure.cpp index 63a8201b..47243f53 100644 --- a/source/plugin/pressure.cpp +++ b/source/plugin/pressure.cpp @@ -106,8 +106,71 @@ void knCorrectVelocity(const FlagGrid& flags, MACGrid& vel, const Grid& pr else vel[idx].z = 0.f; } } + + if(flags.isSolid(idx)) { + if( flags.isSolid(i-1,j,k)) vel[idx].x -= (pressure[idx] - pressure(i-1,j,k)); + if( flags.isSolid(i,j-1,k)) vel[idx].y -= (pressure[idx] - pressure(i,j-1,k)); + if(flags.is3D() && flags.isSolid(i,j,k-1)) vel[idx].z -= (pressure[idx] - pressure(i,j,k-1)); + + if( flags.isEmpty(i-1,j,k)) vel[idx].x -= pressure[idx]; + if( flags.isEmpty(i,j-1,k)) vel[idx].y -= pressure[idx]; + if(flags.is3D() && flags.isEmpty(i,j,k-1)) vel[idx].z -= pressure[idx]; + } else if(flags.isEmpty(idx)&&!flags.isOutflow(idx)) { // don't change velocities in outflow cells + if(flags.isSolid(i-1,j,k)) vel[idx].x += pressure(i-1,j,k); + else vel[idx].x = 0.f; + if(flags.isSolid(i,j-1,k)) vel[idx].y += pressure(i,j-1,k); + else vel[idx].y = 0.f; + if(flags.is3D()) { + if(flags.isSolid(i,j,k-1)) vel[idx].z += pressure(i,j,k-1); + else vel[idx].z = 0.f; + } + } } +// KERNEL(bnd = 1) +// void knCorrectVelocity(const FlagGrid& flags, MACGrid& vel, const Grid& pressure) +// { +// const IndexInt idx = flags.index(i,j,k); +// if(flags.isFluid(idx)) { +// if( flags.isFluid(i-1,j,k)) vel[idx].x -= (pressure[idx] - pressure(i-1,j,k)); +// if( flags.isFluid(i,j-1,k)) vel[idx].y -= (pressure[idx] - pressure(i,j-1,k)); +// if(flags.is3D() && flags.isFluid(i,j,k-1)) vel[idx].z -= (pressure[idx] - pressure(i,j,k-1)); + +// if( flags.isEmpty(i-1,j,k)) vel[idx].x -= pressure[idx]; +// if( flags.isEmpty(i,j-1,k)) vel[idx].y -= pressure[idx]; +// if(flags.is3D() && flags.isEmpty(i,j,k-1)) vel[idx].z -= pressure[idx]; +// } else if(flags.isEmpty(idx)&&!flags.isOutflow(idx)) { // don't change velocities in outflow cells +// if(flags.isFluid(i-1,j,k)) vel[idx].x += pressure(i-1,j,k); +// else vel[idx].x = 0.f; +// if(flags.isFluid(i,j-1,k)) vel[idx].y += pressure(i,j-1,k); +// else vel[idx].y = 0.f; +// if(flags.is3D()) { +// if(flags.isFluid(i,j,k-1)) vel[idx].z += pressure(i,j,k-1); +// else vel[idx].z = 0.f; +// } +// } + +// if(flags.isSolid(idx)) { +// if( flags.isSolid(i-1,j,k)) vel[idx].x -= (pressure[idx] - pressure(i-1,j,k)); +// if( flags.isSolid(i,j-1,k)) vel[idx].y -= (pressure[idx] - pressure(i,j-1,k)); +// if(flags.is3D() && flags.isSolid(i,j,k-1)) vel[idx].z -= (pressure[idx] - pressure(i,j,k-1)); + +// if( flags.isEmpty(i-1,j,k)) vel[idx].x -= pressure[idx]; +// if( flags.isEmpty(i,j-1,k)) vel[idx].y -= pressure[idx]; +// if(flags.is3D() && flags.isEmpty(i,j,k-1)) vel[idx].z -= pressure[idx]; +// } else if(flags.isEmpty(idx)&&!flags.isOutflow(idx)) { // don't change velocities in outflow cells +// if(flags.isSolid(i-1,j,k)) vel[idx].x += pressure(i-1,j,k); +// else vel[idx].x = 0.f; +// if(flags.isSolid(i,j-1,k)) vel[idx].y += pressure(i,j-1,k); +// else vel[idx].y = 0.f; +// if(flags.is3D()) { +// if(flags.isSolid(i,j,k-1)) vel[idx].z += pressure(i,j,k-1); +// else vel[idx].z = 0.f; +// } +// } +// } + + // ***************************************************************************** // Ghost fluid helpers @@ -360,7 +423,7 @@ PYTHON() void solvePressureSystem( topCenter - Vec3i(0,2,0) }; for (Vec3i pos : preferredPos) { - if(flags.isFluid(pos)) { + if(flags.isFluid(pos) || flags.isSolid(pos)) { fixPidx = flags.index(pos); break; } @@ -369,7 +432,7 @@ PYTHON() void solvePressureSystem( // 2) Then search whole domain if(fixPidx == -1) { FOR_IJK_BND(flags,1) { - if(flags.isFluid(i,j,k)) { + if(flags.isFluid(i,j,k) || flags.isSolid(i,j,k)) { fixPidx = flags.index(i,j,k); // break FOR_IJK_BND loop i = flags.getSizeX()-1; @@ -468,6 +531,7 @@ PYTHON() void correctVelocity( const Real surfTens = 0.) { knCorrectVelocity(flags, vel, pressure); + if(phi) { knCorrectVelocityGhostFluid(vel, flags, pressure, *phi, gfClamp, curv, surfTens); // improve behavior of clamping for large time steps: diff --git a/source/plugin/pbd.cpp b/source/plugin/solids.cpp similarity index 100% rename from source/plugin/pbd.cpp rename to source/plugin/solids.cpp